this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include "module-builtins.h"
3
4#include "gtest/gtest.h"
5
6#include "attributedict.h"
7#include "builtins.h"
8#include "dict-builtins.h"
9#include "ic.h"
10#include "object-builtins.h"
11#include "objects.h"
12#include "runtime.h"
13#include "str-builtins.h"
14#include "test-utils.h"
15
16namespace py {
17namespace testing {
18
19using ModuleBuiltinsDeathTest = RuntimeFixture;
20using ModuleBuiltinsTest = RuntimeFixture;
21
22TEST_F(ModuleBuiltinsTest, DunderName) {
23 ASSERT_FALSE(runFromCStr(runtime_, R"(
24import sys
25a = sys.__name__
26)")
27 .isError());
28 HandleScope scope(thread_);
29 Str a(&scope, mainModuleAt(runtime_, "a"));
30 EXPECT_TRUE(a.equalsCStr("sys"));
31}
32
33TEST_F(ModuleBuiltinsTest, DunderNameOverwritesDoesNotAffectModuleNameField) {
34 ASSERT_FALSE(runFromCStr(runtime_, R"(
35import sys
36sys.__name__ = "ysy"
37)")
38 .isError());
39 HandleScope scope(thread_);
40 Module sys_module(&scope, mainModuleAt(runtime_, "sys"));
41 EXPECT_TRUE(isStrEqualsCStr(sys_module.name(), "sys"));
42}
43
44// TODO(T39575976): Add tests verifying internal names's hiding.
45TEST_F(ModuleBuiltinsTest, DunderGetattributeReturnsAttribute) {
46 HandleScope scope(thread_);
47 ASSERT_FALSE(runFromCStr(runtime_, "foo = -6").isError());
48 Module module(&scope, runtime_->findModuleById(ID(__main__)));
49 Object name(&scope, runtime_->newStrFromCStr("foo"));
50 EXPECT_TRUE(isIntEqualsWord(
51 runBuiltin(METH(module, __getattribute__), module, name), -6));
52}
53
54TEST_F(ModuleBuiltinsTest, DunderGetattributeWithNonStringNameRaisesTypeError) {
55 HandleScope scope(thread_);
56 ASSERT_FALSE(runFromCStr(runtime_, "").isError());
57 Module module(&scope, runtime_->findModuleById(ID(__main__)));
58 Object name(&scope, runtime_->newInt(0));
59 EXPECT_TRUE(raisedWithStr(
60 runBuiltin(METH(module, __getattribute__), module, name),
61 LayoutId::kTypeError, "attribute name must be string, not 'int'"));
62}
63
64TEST_F(ModuleBuiltinsTest,
65 DunderGetattributeWithMissingAttributeRaisesAttributeError) {
66 HandleScope scope(thread_);
67 ASSERT_FALSE(runFromCStr(runtime_, "").isError());
68 Module module(&scope, runtime_->findModuleById(ID(__main__)));
69 Object name(&scope, runtime_->newStrFromCStr("xxx"));
70 EXPECT_TRUE(raisedWithStr(
71 runBuiltin(METH(module, __getattribute__), module, name),
72 LayoutId::kAttributeError, "module '__main__' has no attribute 'xxx'"));
73}
74
75TEST_F(ModuleBuiltinsTest, DunderSetattrSetsAttribute) {
76 HandleScope scope(thread_);
77 Object module_name(&scope, runtime_->newStrFromCStr("foo"));
78 Module module(&scope, runtime_->newModule(module_name));
79 Object name(&scope, Runtime::internStrFromCStr(thread_, "foobarbaz"));
80 Object value(&scope, runtime_->newInt(0xf00d));
81 EXPECT_TRUE(
82 runBuiltin(METH(module, __setattr__), module, name, value).isNoneType());
83 EXPECT_TRUE(isIntEqualsWord(moduleAt(module, name), 0xf00d));
84}
85
86TEST_F(ModuleBuiltinsTest, DunderSetattrWithNonStrNameRaisesTypeError) {
87 HandleScope scope(thread_);
88 Object module_name(&scope, runtime_->newStrFromCStr("foo"));
89 Object module(&scope, runtime_->newModule(module_name));
90 Object name(&scope, runtime_->newFloat(4.4));
91 Object value(&scope, runtime_->newInt(0));
92 EXPECT_TRUE(raisedWithStr(
93 runBuiltin(METH(module, __setattr__), module, name, value),
94 LayoutId::kTypeError, "attribute name must be string, not 'float'"));
95}
96
97TEST_F(ModuleBuiltinsTest, DunderDict) {
98 ASSERT_FALSE(runFromCStr(runtime_, R"(
99import sys
100result = sys.__dict__
101)")
102 .isError());
103 HandleScope scope(thread_);
104 Object result(&scope, mainModuleAt(runtime_, "result"));
105 EXPECT_TRUE(result.isModuleProxy());
106}
107
108static RawModule createTestingModule(Thread* thread) {
109 HandleScope scope(thread);
110 Runtime* runtime = thread->runtime();
111
112 // Create a builtins module.
113 Object builtins_name(&scope, runtime->symbols()->at(ID(builtins)));
114 Module builtins_module(&scope, runtime->newModule(builtins_name));
115
116 // Create a module dict with builtins in it.
117 Object name(&scope, Runtime::internStrFromCStr(thread, "<test module>"));
118 Module module(&scope, runtime->newModule(name));
119 moduleAtPutById(thread, module, ID(__builtins__), builtins_module);
120 return *module;
121}
122
123TEST_F(ModuleBuiltinsTest, ModuleAtIgnoresBuiltinsEntry) {
124 HandleScope scope(thread_);
125 Module module(&scope, createTestingModule(thread_));
126 Module builtins(&scope, moduleAtById(thread_, module, ID(__builtins__)));
127
128 Object foo(&scope, Runtime::internStrFromCStr(thread_, "foo"));
129 Str foo_in_builtins(&scope, runtime_->newStrFromCStr("foo_in_builtins"));
130 moduleAtPut(thread_, builtins, foo, foo_in_builtins);
131 EXPECT_TRUE(moduleAt(module, foo).isErrorNotFound());
132}
133
134TEST_F(ModuleBuiltinsTest, ModuleAtReturnsValuePutByModuleAtPut) {
135 HandleScope scope(thread_);
136 Module module(&scope, createTestingModule(thread_));
137 Object name(&scope, Runtime::internStrFromCStr(thread_, "a"));
138 Object value(&scope, runtime_->newStrFromCStr("a's value"));
139 moduleAtPut(thread_, module, name, value);
140 EXPECT_EQ(moduleAt(module, name), *value);
141}
142
143TEST_F(ModuleBuiltinsTest, ModuleAtReturnsErrorNotFoundForPlaceholder) {
144 HandleScope scope(thread_);
145 Module module(&scope, createTestingModule(thread_));
146 Object name(&scope, Runtime::internStrFromCStr(thread_, "a"));
147 Object value(&scope, runtime_->newStrFromCStr("a's value"));
148 moduleAtPut(thread_, module, name, value);
149
150 RawObject value_cell = NoneType::object();
151 EXPECT_TRUE(attributeValueCellAt(*module, *name, &value_cell));
152 ValueCell::cast(value_cell).makePlaceholder();
153 EXPECT_TRUE(moduleAt(module, name).isErrorNotFound());
154}
155
156TEST_F(ModuleBuiltinsTest, ModuleAtByIdReturnsValuePutByModuleAtPutById) {
157 HandleScope scope(thread_);
158 Module module(&scope, createTestingModule(thread_));
159 Object value(&scope, runtime_->newStrFromCStr("a's value"));
160 moduleAtPutById(thread_, module, ID(NotImplemented), value);
161 EXPECT_EQ(moduleAtById(thread_, module, ID(NotImplemented)), *value);
162}
163
164TEST_F(ModuleBuiltinsTest, ModuleAtReturnsValuePutByModuleAtPutByCStr) {
165 HandleScope scope(thread_);
166 Module module(&scope, createTestingModule(thread_));
167 Object value(&scope, runtime_->newStrFromCStr("a's value"));
168 const char* name_cstr = "a";
169 moduleAtPutByCStr(thread_, module, name_cstr, value);
170 Object name(&scope, Runtime::internStrFromCStr(thread_, name_cstr));
171 EXPECT_EQ(moduleAt(module, name), *value);
172}
173
174TEST_F(ModuleBuiltinsTest,
175 ModuleAtPutDoesNotInvalidateCachedModuleDictValueCell) {
176 HandleScope scope(thread_);
177 EXPECT_FALSE(runFromCStr(runtime_, R"(
178a = 4
179
180def foo():
181 return a
182
183foo()
184)")
185 .isError());
186 Module module(&scope, findMainModule(runtime_));
187 Object a(&scope, Runtime::internStrFromCStr(thread_, "a"));
188 ValueCell value_cell_a(&scope, moduleValueCellAt(thread_, module, a));
189
190 // The looked up module entry got cached in function foo().
191 Function function_foo(&scope, mainModuleAt(runtime_, "foo"));
192 MutableTuple caches(&scope, function_foo.caches());
193 ASSERT_EQ(icLookupGlobalVar(*caches, 0), *value_cell_a);
194
195 // Updating global variable a does not invalidate the cache.
196 Str new_value(&scope, runtime_->newStrFromCStr("value"));
197 moduleAtPut(thread_, module, a, new_value);
198 EXPECT_EQ(icLookupGlobalVar(*caches, 0), *value_cell_a);
199 EXPECT_EQ(value_cell_a.value(), *new_value);
200}
201
202TEST_F(ModuleBuiltinsTest, ModuleAtPutInvalidatesCachedBuiltinsValueCell) {
203 HandleScope scope(thread_);
204 EXPECT_FALSE(runFromCStr(runtime_, R"(
205__builtins__.a = 4
206
207def foo():
208 return a
209
210foo()
211)")
212 .isError());
213 Module module(&scope, findMainModule(runtime_));
214 Module builtins(&scope, runtime_->findModuleById(ID(builtins)));
215 Object a(&scope, Runtime::internStrFromCStr(thread_, "a"));
216 Str new_value(&scope, runtime_->newStrFromCStr("value"));
217 ValueCell value_cell_a(&scope, moduleValueCellAt(thread_, builtins, a));
218
219 // The looked up module entry got cached in function foo().
220 Function function_foo(&scope, mainModuleAt(runtime_, "foo"));
221 MutableTuple caches(&scope, function_foo.caches());
222 ASSERT_EQ(icLookupGlobalVar(*caches, 0), *value_cell_a);
223
224 ASSERT_FALSE(mainModuleAt(runtime_, "__builtins__").isErrorNotFound());
225
226 // Updating global variable a does invalidate the cache since it shadows
227 // __builtins__.a.
228 moduleAtPut(thread_, module, a, new_value);
229 EXPECT_TRUE(icLookupGlobalVar(*caches, 0).isNoneType());
230}
231
232TEST_F(ModuleBuiltinsTest, ModuleKeysFiltersOutPlaceholders) {
233 HandleScope scope(thread_);
234 Str name(&scope, Str::empty());
235 Module module(&scope, runtime_->newModule(name));
236 attributeDictInit(thread_, module);
237
238 Object foo(&scope, Runtime::internStrFromCStr(thread_, "foo"));
239 Object bar(&scope, Runtime::internStrFromCStr(thread_, "bar"));
240 Object baz(&scope, Runtime::internStrFromCStr(thread_, "baz"));
241 Str value(&scope, runtime_->newStrFromCStr("value"));
242
243 moduleAtPut(thread_, module, foo, value);
244 moduleAtPut(thread_, module, bar, value);
245 moduleAtPut(thread_, module, baz, value);
246
247 ValueCell::cast(moduleValueCellAt(thread_, module, bar)).makePlaceholder();
248
249 List keys(&scope, moduleKeys(thread_, module));
250 EXPECT_EQ(keys.numItems(), 2);
251 EXPECT_EQ(keys.at(0), *foo);
252 EXPECT_EQ(keys.at(1), *baz);
253}
254
255TEST_F(ModuleBuiltinsTest, ModuleLenReturnsItemCountExcludingPlaceholders) {
256 HandleScope scope(thread_);
257 Object module_name(&scope, Runtime::internStrFromCStr(thread_, "mymodule"));
258 Module module(&scope, runtime_->newModule(module_name));
259
260 Object foo(&scope, Runtime::internStrFromCStr(thread_, "foo"));
261 Object bar(&scope, Runtime::internStrFromCStr(thread_, "bar"));
262 Object baz(&scope, Runtime::internStrFromCStr(thread_, "baz"));
263 Str value(&scope, runtime_->newStrFromCStr("value"));
264
265 moduleAtPut(thread_, module, foo, value);
266 moduleAtPut(thread_, module, bar, value);
267 moduleAtPut(thread_, module, baz, value);
268
269 word previous_len = moduleLen(thread_, module);
270
271 RawObject value_cell = NoneType::object();
272 EXPECT_TRUE(attributeValueCellAt(*module, *bar, &value_cell));
273 ValueCell::cast(value_cell).makePlaceholder();
274
275 word after_len = moduleLen(thread_, module);
276 EXPECT_EQ(previous_len, after_len + 1);
277}
278
279TEST_F(ModuleBuiltinsTest, ModuleRemoveInvalidatesCachedModuleDictValueCell) {
280 HandleScope scope(thread_);
281 EXPECT_FALSE(runFromCStr(runtime_, R"(
282a = 4
283
284def foo():
285 return a
286
287foo()
288)")
289 .isError());
290
291 // The looked up module entry got cached in function foo().
292 Function function_foo(&scope, mainModuleAt(runtime_, "foo"));
293 MutableTuple caches(&scope, function_foo.caches());
294
295 Module module(&scope, findMainModule(runtime_));
296 Str a(&scope, runtime_->newStrFromCStr("a"));
297 ASSERT_EQ(icLookupGlobalVar(*caches, 0),
298 moduleValueCellAt(thread_, module, a));
299
300 EXPECT_FALSE(moduleRemove(thread_, module, a).isError());
301 EXPECT_TRUE(icLookupGlobalVar(*caches, 0).isNoneType());
302}
303
304TEST_F(ModuleBuiltinsTest, ModuleValuesFiltersOutPlaceholders) {
305 HandleScope scope(thread_);
306 Str module_name(&scope, runtime_->newStrFromCStr("mymodule"));
307 Module module(&scope, runtime_->newModule(module_name));
308
309 Object foo(&scope, Runtime::internStrFromCStr(thread_, "foo"));
310 Str foo_value(&scope, runtime_->newStrFromCStr("foo_value"));
311 Object bar(&scope, Runtime::internStrFromCStr(thread_, "bar"));
312 Str bar_value(&scope, runtime_->newStrFromCStr("bar_value"));
313 Object baz(&scope, Runtime::internStrFromCStr(thread_, "baz"));
314 Str baz_value(&scope, runtime_->newStrFromCStr("baz_value"));
315
316 moduleAtPut(thread_, module, foo, foo_value);
317 moduleAtPut(thread_, module, bar, bar_value);
318 moduleAtPut(thread_, module, baz, baz_value);
319
320 RawObject value_cell = NoneType::object();
321 EXPECT_TRUE(attributeValueCellAt(*module, *bar, &value_cell));
322 ValueCell::cast(value_cell).makePlaceholder();
323
324 List values(&scope, moduleValues(thread_, module));
325 EXPECT_TRUE(listContains(values, foo_value));
326 EXPECT_FALSE(listContains(values, bar_value));
327 EXPECT_TRUE(listContains(values, baz_value));
328}
329
330TEST_F(ModuleBuiltinsTest, ModuleDelAttrEmptiesValueCell) {
331 HandleScope scope(thread_);
332 Str module_name(&scope, runtime_->newStrFromCStr("mymodule"));
333 Module module(&scope, runtime_->newModule(module_name));
334
335 Object foo(&scope, Runtime::internStrFromCStr(thread_, "foo"));
336 Str foo_value(&scope, runtime_->newStrFromCStr("foo_value"));
337
338 moduleAtPut(thread_, module, foo, foo_value);
339
340 Object value_cell(&scope, NoneType::object());
341 EXPECT_TRUE(attributeValueCellAt(*module, *foo, &value_cell));
342 EXPECT_EQ(ValueCell::cast(*value_cell).value(), *foo_value);
343 moduleDeleteAttribute(thread_, module, foo);
344 EXPECT_NE(ValueCell::cast(*value_cell).value(), *foo_value);
345 EXPECT_TRUE(ValueCell::cast(*value_cell).isPlaceholder());
346}
347
348TEST_F(ModuleBuiltinsTest, ModuleGetAttributeReturnsInstanceValue) {
349 HandleScope scope(thread_);
350 ASSERT_FALSE(runFromCStr(runtime_, "x = 42").isError());
351 Module module(&scope, runtime_->findModuleById(ID(__main__)));
352 Object name(&scope, Runtime::internStrFromCStr(thread_, "x"));
353 EXPECT_TRUE(isIntEqualsWord(moduleGetAttribute(thread_, module, name), 42));
354}
355
356TEST_F(ModuleBuiltinsTest, ModuleGetAttributeWithNonExistentNameReturnsError) {
357 HandleScope scope(thread_);
358 Object module_name(&scope, runtime_->newStrFromCStr(""));
359 Module module(&scope, runtime_->newModule(module_name));
360 Object name(&scope, Runtime::internStrFromCStr(thread_, "xxx"));
361 EXPECT_TRUE(moduleGetAttribute(thread_, module, name).isError());
362 EXPECT_FALSE(thread_->hasPendingException());
363}
364
365TEST_F(ModuleBuiltinsTest, ModuleSetAttrSetsAttribute) {
366 HandleScope scope(thread_);
367 Object module_name(&scope, runtime_->newStrFromCStr("foo"));
368 Module module(&scope, runtime_->newModule(module_name));
369 Object name(&scope, Runtime::internStrFromCStr(thread_, "bar"));
370 Object value(&scope, runtime_->newInt(-543));
371 EXPECT_TRUE(moduleSetAttr(thread_, module, name, value).isNoneType());
372 EXPECT_TRUE(isIntEqualsWord(moduleAt(module, name), -543));
373}
374
375TEST_F(ModuleBuiltinsTest, NewModuleDunderReprReturnsString) {
376 HandleScope scope(thread_);
377 Object name(&scope, runtime_->newStrFromCStr("hello"));
378 Object module(&scope, runtime_->newModule(name));
379 Object result(&scope, Thread::current()->invokeMethod1(module, ID(__repr__)));
380 EXPECT_TRUE(isStrEqualsCStr(*result, "<module 'hello'>"));
381}
382
383TEST_F(ModuleBuiltinsTest, BuiltinModuleDunderReprReturnsString) {
384 ASSERT_FALSE(runFromCStr(runtime_, R"(
385import sys
386result = sys.__repr__()
387)")
388 .isError());
389 HandleScope scope(thread_);
390 Object result(&scope, mainModuleAt(runtime_, "result"));
391 EXPECT_TRUE(isStrEqualsCStr(*result, "<module 'sys' (built-in)>"));
392}
393
394TEST_F(ModuleBuiltinsTest, ModuleIsMarkedAsCustomDict) {
395 HandleScope scope(thread_);
396 Type type(&scope, runtime_->typeAt(LayoutId::kModule));
397 EXPECT_TRUE(type.hasFlag(Type::Flag::kHasCustomDict));
398 EXPECT_TRUE(Layout::cast(type.instanceLayout()).isSealed());
399}
400
401TEST_F(ModuleBuiltinsTest, ModuleSubclassLayoutIsSealed) {
402 HandleScope scope(thread_);
403 EXPECT_FALSE(runFromCStr(runtime_, R"(
404class C(module): pass
405)")
406 .isError());
407 Type type(&scope, mainModuleAt(runtime_, "C"));
408 EXPECT_TRUE(type.hasFlag(Type::Flag::kHasCustomDict));
409 EXPECT_TRUE(Layout::cast(type.instanceLayout()).isSealed());
410}
411
412} // namespace testing
413} // namespace py