this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include <memory>
3
4#include "cpython-data.h"
5#include "gtest/gtest.h"
6
7#include "api-handle.h"
8#include "dict-builtins.h"
9#include "function-utils.h"
10#include "runtime.h"
11#include "test-utils.h"
12
13namespace py {
14namespace testing {
15
16using MethodTrampolinesTest = RuntimeFixture;
17
18static PyObject* capiFunctionNoArgs(PyObject* self, PyObject* args) {
19 Runtime* runtime = Thread::current()->runtime();
20 runtime->collectGarbage();
21 ApiHandle* handle = ApiHandle::fromPyObject(self);
22 EXPECT_GT(ApiHandle::refcnt(handle), 0);
23 EXPECT_TRUE(isStrEqualsCStr(ApiHandle::asObject(handle), "the self argument"));
24 EXPECT_EQ(args, nullptr);
25 return ApiHandle::newReference(runtime, SmallInt::fromWord(1230));
26}
27
28static RawObject newFunctionNoArgs(Thread* thread) {
29 HandleScope scope(thread);
30 Runtime* runtime = thread->runtime();
31 Object name(&scope, runtime->newStrFromCStr("foo"));
32 PyCFunction function_ptr = capiFunctionNoArgs;
33 return newExtensionFunction(
34 thread, name, reinterpret_cast<void*>(function_ptr), METH_NOARGS);
35}
36
37TEST_F(MethodTrampolinesTest, NoArgs) {
38 HandleScope scope(thread_);
39 Object function(&scope, newFunctionNoArgs(thread_));
40 Object arg0(&scope, runtime_->newStrFromCStr("the self argument"));
41 EXPECT_TRUE(
42 isIntEqualsWord(Interpreter::call1(thread_, function, arg0), 1230));
43}
44
45TEST_F(MethodTrampolinesTest, NoArgsWithoutSelfRaisesTypeError) {
46 HandleScope scope(thread_);
47 Function function(&scope, newFunctionNoArgs(thread_));
48 EXPECT_TRUE(raisedWithStr(Interpreter::call0(thread_, function),
49 LayoutId::kTypeError,
50 "'foo' must be bound to an object"));
51}
52
53TEST_F(MethodTrampolinesTest, NoArgsWithTooManyArgsRaisesTypeError) {
54 HandleScope scope(thread_);
55 Function function(&scope, newFunctionNoArgs(thread_));
56 Object arg0(&scope, runtime_->newStrFromCStr("the self argument"));
57 Object arg1(&scope, runtime_->newStrFromCStr("bar"));
58 EXPECT_TRUE(raisedWithStr(Interpreter::call2(thread_, function, arg0, arg1),
59 LayoutId::kTypeError,
60 "'foo' takes no arguments (1 given)"));
61}
62
63TEST_F(MethodTrampolinesTest, NoArgsKw) {
64 thread_->stackPush(newFunctionNoArgs(thread_));
65 thread_->stackPush(runtime_->newStrFromCStr("the self argument"));
66 thread_->stackPush(runtime_->emptyTuple());
67 EXPECT_TRUE(isIntEqualsWord(Interpreter::callKw(thread_, 1), 1230));
68}
69
70TEST_F(MethodTrampolinesTest, NoArgsKwWithoutSelfRaisesTypeError) {
71 thread_->stackPush(newFunctionNoArgs(thread_));
72 thread_->stackPush(runtime_->emptyTuple());
73 EXPECT_TRUE(raisedWithStr(Interpreter::callKw(thread_, 0),
74 LayoutId::kTypeError,
75 "'foo' must be bound to an object"));
76}
77
78TEST_F(MethodTrampolinesTest, NoArgsKwWithTooManyArgsRaisesTypeError) {
79 thread_->stackPush(newFunctionNoArgs(thread_));
80 thread_->stackPush(runtime_->newStrFromCStr("the self argument"));
81 thread_->stackPush(runtime_->newStrFromCStr("bar"));
82 thread_->stackPush(runtime_->emptyTuple());
83 EXPECT_TRUE(raisedWithStr(Interpreter::callKw(thread_, 2),
84 LayoutId::kTypeError,
85 "'foo' takes no arguments (1 given)"));
86}
87
88TEST_F(MethodTrampolinesTest, NoArgsKwWithKeywordArgRaisesTypeError) {
89 HandleScope scope(thread_);
90 Object key(&scope, runtime_->newStrFromCStr("key"));
91 Tuple kw_names(&scope, runtime_->newTupleWith1(key));
92 thread_->stackPush(newFunctionNoArgs(thread_));
93 thread_->stackPush(runtime_->newStrFromCStr("the self argument"));
94 thread_->stackPush(runtime_->newStrFromCStr("value"));
95 thread_->stackPush(*kw_names);
96 EXPECT_TRUE(raisedWithStr(Interpreter::callKw(thread_, 2),
97 LayoutId::kTypeError,
98 "'foo' takes no keyword arguments"));
99}
100
101TEST_F(MethodTrampolinesTest, NoArgsExWithoutKwargs) {
102 HandleScope scope(thread_);
103 Object self(&scope, runtime_->newStrFromCStr("the self argument"));
104 Tuple args(&scope, runtime_->newTupleWith1(self));
105 thread_->stackPush(newFunctionNoArgs(thread_));
106 thread_->stackPush(*args);
107 EXPECT_TRUE(isIntEqualsWord(Interpreter::callEx(thread_, 0), 1230));
108}
109
110TEST_F(MethodTrampolinesTest, NoArgsExWithKwargs) {
111 HandleScope scope(thread_);
112 Object self(&scope, runtime_->newStrFromCStr("the self argument"));
113 Tuple args(&scope, runtime_->newTupleWith1(self));
114 thread_->stackPush(newFunctionNoArgs(thread_));
115 thread_->stackPush(*args);
116 thread_->stackPush(runtime_->newDict());
117 EXPECT_TRUE(isIntEqualsWord(
118 Interpreter::callEx(thread_, CallFunctionExFlag::VAR_KEYWORDS), 1230));
119}
120
121TEST_F(MethodTrampolinesTest, NoArgsExWithoutSelfRaisesTypeError) {
122 thread_->stackPush(newFunctionNoArgs(thread_));
123 thread_->stackPush(runtime_->emptyTuple());
124 EXPECT_TRUE(raisedWithStr(Interpreter::callEx(thread_, 0),
125 LayoutId::kTypeError,
126 "'foo' must be bound to an object"));
127}
128
129TEST_F(MethodTrampolinesTest, NoArgsExWithArgRaisesTypeError) {
130 HandleScope scope(thread_);
131 Object self(&scope, runtime_->newStrFromCStr("the self argument"));
132 Object bar(&scope, runtime_->newStrFromCStr("bar"));
133 Object num(&scope, runtime_->newInt(88));
134 Tuple args(&scope, runtime_->newTupleWith3(self, bar, num));
135 thread_->stackPush(newFunctionNoArgs(thread_));
136 thread_->stackPush(*args);
137 EXPECT_TRUE(raisedWithStr(Interpreter::callEx(thread_, 0),
138 LayoutId::kTypeError,
139 "'foo' takes no arguments (2 given)"));
140}
141
142TEST_F(MethodTrampolinesTest, NoArgsExWithKeywordArgRaisesTypeError) {
143 HandleScope scope(thread_);
144 Object self(&scope, runtime_->newStrFromCStr("the self argument"));
145 Tuple args(&scope, runtime_->newTupleWith1(self));
146 Dict kwargs(&scope, runtime_->newDict());
147 Object value(&scope, runtime_->newStrFromCStr("value"));
148 dictAtPutById(thread_, kwargs, ID(key), value);
149 thread_->stackPush(newFunctionNoArgs(thread_));
150 thread_->stackPush(*args);
151 thread_->stackPush(*kwargs);
152 EXPECT_TRUE(raisedWithStr(
153 Interpreter::callEx(thread_, CallFunctionExFlag::VAR_KEYWORDS),
154 LayoutId::kTypeError, "'foo' takes no keyword arguments"));
155}
156
157static PyObject* capiFunctionOneArg(PyObject* self, PyObject* arg) {
158 Runtime* runtime = Thread::current()->runtime();
159 runtime->collectGarbage();
160 ApiHandle* handle = ApiHandle::fromPyObject(self);
161 EXPECT_GT(ApiHandle::refcnt(handle), 0);
162 EXPECT_TRUE(isStrEqualsCStr(ApiHandle::asObject(handle), "the self argument"));
163 ApiHandle* arg_handle = ApiHandle::fromPyObject(arg);
164 EXPECT_GT(ApiHandle::refcnt(arg_handle), 0);
165 EXPECT_TRUE(isFloatEqualsDouble(ApiHandle::asObject(arg_handle), 42.5));
166 return ApiHandle::newReference(runtime, SmallInt::fromWord(1231));
167}
168
169static RawObject newFunctionOneArg(Thread* thread) {
170 HandleScope scope(thread);
171 Runtime* runtime = thread->runtime();
172 Object name(&scope, runtime->newStrFromCStr("foo"));
173 PyCFunction function_ptr = capiFunctionOneArg;
174 return newExtensionFunction(thread, name,
175 reinterpret_cast<void*>(function_ptr), METH_O);
176}
177
178TEST_F(MethodTrampolinesTest, OneArg) {
179 HandleScope scope(thread_);
180 Function function(&scope, newFunctionOneArg(thread_));
181 Object arg0(&scope, runtime_->newStrFromCStr("the self argument"));
182 Object arg1(&scope, runtime_->newFloat(42.5));
183 EXPECT_TRUE(
184 isIntEqualsWord(Interpreter::call2(thread_, function, arg0, arg1), 1231));
185}
186
187TEST_F(MethodTrampolinesTest, OneArgWithoutSelfRaisesTypeError) {
188 HandleScope scope(thread_);
189 Function function(&scope, newFunctionOneArg(thread_));
190 EXPECT_TRUE(raisedWithStr(Interpreter::call0(thread_, function),
191 LayoutId::kTypeError,
192 "'foo' must be bound to an object"));
193}
194
195TEST_F(MethodTrampolinesTest, OneArgWithTooManyArgsRaisesTypeError) {
196 HandleScope scope(thread_);
197 Function function(&scope, newFunctionOneArg(thread_));
198 Object arg0(&scope, runtime_->newStrFromCStr("the self argument"));
199 Object arg1(&scope, runtime_->newFloat(42.5));
200 Object arg2(&scope, runtime_->newInt(5));
201 Object arg3(&scope, runtime_->newInt(7));
202 EXPECT_TRUE(raisedWithStr(
203 Interpreter::call4(thread_, function, arg0, arg1, arg2, arg3),
204 LayoutId::kTypeError, "'foo' takes exactly one argument (3 given)"));
205}
206
207TEST_F(MethodTrampolinesTest, OneArgKw) {
208 thread_->stackPush(newFunctionOneArg(thread_));
209 thread_->stackPush(runtime_->newStrFromCStr("the self argument"));
210 thread_->stackPush(runtime_->newFloat(42.5));
211 thread_->stackPush(runtime_->emptyTuple());
212 EXPECT_TRUE(isIntEqualsWord(Interpreter::callKw(thread_, 2), 1231));
213}
214
215TEST_F(MethodTrampolinesTest, OneArgKwWithoutSelfRaisesTypeError) {
216 thread_->stackPush(newFunctionOneArg(thread_));
217 thread_->stackPush(runtime_->emptyTuple());
218 EXPECT_TRUE(raisedWithStr(Interpreter::callKw(thread_, 0),
219 LayoutId::kTypeError,
220 "'foo' must be bound to an object"));
221}
222
223TEST_F(MethodTrampolinesTest, OneArgKwWithTooManyArgsRaisesTypeError) {
224 thread_->stackPush(newFunctionOneArg(thread_));
225 thread_->stackPush(runtime_->newStrFromCStr("the self argument"));
226 thread_->stackPush(runtime_->newFloat(42.5));
227 thread_->stackPush(runtime_->newInt(5));
228 thread_->stackPush(runtime_->emptyTuple());
229 EXPECT_TRUE(raisedWithStr(Interpreter::callKw(thread_, 3),
230 LayoutId::kTypeError,
231 "'foo' takes exactly one argument (2 given)"));
232}
233
234TEST_F(MethodTrampolinesTest, OneArgKwWithKeywordArgRaisesTypeError) {
235 HandleScope scope(thread_);
236 Object key(&scope, runtime_->newStrFromCStr("key"));
237 Tuple kw_names(&scope, runtime_->newTupleWith1(key));
238 thread_->stackPush(newFunctionOneArg(thread_));
239 thread_->stackPush(runtime_->newFloat(42.5));
240 thread_->stackPush(runtime_->newStrFromCStr("value"));
241 thread_->stackPush(*kw_names);
242 EXPECT_TRUE(raisedWithStr(Interpreter::callKw(thread_, 2),
243 LayoutId::kTypeError,
244 "'foo' takes no keyword arguments"));
245}
246
247TEST_F(MethodTrampolinesTest, OneArgExWithoutKwargs) {
248 HandleScope scope(thread_);
249 Object self(&scope, runtime_->newStrFromCStr("the self argument"));
250 Object num(&scope, runtime_->newFloat(42.5));
251 Tuple args(&scope, runtime_->newTupleWith2(self, num));
252 thread_->stackPush(newFunctionOneArg(thread_));
253 thread_->stackPush(*args);
254 EXPECT_TRUE(isIntEqualsWord(Interpreter::callEx(thread_, 0), 1231));
255}
256
257TEST_F(MethodTrampolinesTest, OneArgExWithKwargs) {
258 HandleScope scope(thread_);
259 Object self(&scope, runtime_->newStrFromCStr("the self argument"));
260 Object num(&scope, runtime_->newFloat(42.5));
261 Tuple args(&scope, runtime_->newTupleWith2(self, num));
262 thread_->stackPush(newFunctionOneArg(thread_));
263 thread_->stackPush(*args);
264 thread_->stackPush(runtime_->newDict());
265 EXPECT_TRUE(isIntEqualsWord(
266 Interpreter::callEx(thread_, CallFunctionExFlag::VAR_KEYWORDS), 1231));
267}
268
269TEST_F(MethodTrampolinesTest, OneArgExWithoutSelfRaisesTypeError) {
270 thread_->stackPush(newFunctionOneArg(thread_));
271 thread_->stackPush(runtime_->emptyTuple());
272 EXPECT_TRUE(raisedWithStr(Interpreter::callEx(thread_, 0),
273 LayoutId::kTypeError,
274 "'foo' must be bound to an object"));
275}
276
277TEST_F(MethodTrampolinesTest, OneArgExWithTooFewArgsRaisesTypeError) {
278 HandleScope scope(thread_);
279 Object self(&scope, runtime_->newStrFromCStr("the self argument"));
280 Tuple args(&scope, runtime_->newTupleWith1(self));
281 thread_->stackPush(newFunctionOneArg(thread_));
282 thread_->stackPush(*args);
283 thread_->stackPush(runtime_->newDict());
284 EXPECT_TRUE(raisedWithStr(
285 Interpreter::callEx(thread_, CallFunctionExFlag::VAR_KEYWORDS),
286 LayoutId::kTypeError, "'foo' takes exactly one argument (0 given)"));
287}
288
289TEST_F(MethodTrampolinesTest, OneArgExWithKeywordArgRaisesTypeError) {
290 HandleScope scope(thread_);
291 Object self(&scope, runtime_->newStrFromCStr("the self argument"));
292 Object num(&scope, runtime_->newFloat(42.5));
293 Tuple args(&scope, runtime_->newTupleWith2(self, num));
294 Dict kwargs(&scope, runtime_->newDict());
295 Object value(&scope, runtime_->newStrFromCStr("value"));
296 dictAtPutById(thread_, kwargs, ID(key), value);
297 thread_->stackPush(newFunctionOneArg(thread_));
298 thread_->stackPush(*args);
299 thread_->stackPush(*kwargs);
300 EXPECT_TRUE(raisedWithStr(
301 Interpreter::callEx(thread_, CallFunctionExFlag::VAR_KEYWORDS),
302 LayoutId::kTypeError, "'foo' takes no keyword arguments"));
303}
304
305static PyObject* capiFunctionVarArgs(PyObject* self, PyObject* args) {
306 Thread* thread = Thread::current();
307 HandleScope scope(thread);
308 Runtime* runtime = thread->runtime();
309 runtime->collectGarbage();
310 ApiHandle* self_handle = ApiHandle::fromPyObject(self);
311 EXPECT_GT(ApiHandle::refcnt(self_handle), 0);
312 EXPECT_TRUE(isStrEqualsCStr(ApiHandle::asObject(self_handle), "the self argument"));
313 ApiHandle* args_handle = ApiHandle::fromPyObject(args);
314 EXPECT_GT(ApiHandle::refcnt(args_handle), 0);
315 Object args_obj(&scope, ApiHandle::asObject(args_handle));
316 EXPECT_TRUE(args_obj.isTuple());
317 Tuple args_tuple(&scope, *args_obj);
318 EXPECT_EQ(args_tuple.length(), 2);
319 EXPECT_TRUE(isIntEqualsWord(args_tuple.at(0), 88));
320 EXPECT_TRUE(isIntEqualsWord(args_tuple.at(1), 33));
321 return ApiHandle::newReference(runtime, SmallInt::fromWord(1239));
322}
323
324static RawObject newFunctionVarArgs(Thread* thread) {
325 HandleScope scope(thread);
326 Runtime* runtime = thread->runtime();
327 Object name(&scope, runtime->newStrFromCStr("foo"));
328 PyCFunction function_ptr = capiFunctionVarArgs;
329 return newExtensionFunction(
330 thread, name, reinterpret_cast<void*>(function_ptr), METH_VARARGS);
331}
332
333TEST_F(MethodTrampolinesTest, VarArgs) {
334 HandleScope scope(thread_);
335 Object function(&scope, newFunctionVarArgs(thread_));
336 Object arg0(&scope, runtime_->newStrFromCStr("the self argument"));
337 Object arg1(&scope, runtime_->newInt(88));
338 Object arg2(&scope, runtime_->newInt(33));
339 EXPECT_TRUE(isIntEqualsWord(
340 Interpreter::call3(thread_, function, arg0, arg1, arg2), 1239));
341}
342
343TEST_F(MethodTrampolinesTest, VarArgsWithoutSelfRaisesTypeError) {
344 HandleScope scope(thread_);
345 Object function(&scope, newFunctionVarArgs(thread_));
346 EXPECT_TRUE(raisedWithStr(Interpreter::call0(thread_, function),
347 LayoutId::kTypeError,
348 "'foo' must be bound to an object"));
349}
350
351TEST_F(MethodTrampolinesTest, VarArgsKw) {
352 thread_->stackPush(newFunctionVarArgs(thread_));
353 thread_->stackPush(runtime_->newStrFromCStr("the self argument"));
354 thread_->stackPush(runtime_->newInt(88));
355 thread_->stackPush(runtime_->newInt(33));
356 thread_->stackPush(runtime_->emptyTuple());
357 EXPECT_TRUE(isIntEqualsWord(Interpreter::callKw(thread_, 3), 1239));
358}
359
360TEST_F(MethodTrampolinesTest, VarArgsKwWithoutSelfRausesTypeError) {
361 thread_->stackPush(newFunctionVarArgs(thread_));
362 thread_->stackPush(runtime_->emptyTuple());
363 EXPECT_TRUE(raisedWithStr(Interpreter::callKw(thread_, 0),
364 LayoutId::kTypeError,
365 "'foo' must be bound to an object"));
366}
367
368TEST_F(MethodTrampolinesTest, VarArgsKwWithKeywordArgRausesTypeError) {
369 HandleScope scope(thread_);
370 Object key(&scope, runtime_->newStrFromCStr("key"));
371 Tuple kw_names(&scope, runtime_->newTupleWith1(key));
372 thread_->stackPush(newFunctionVarArgs(thread_));
373 thread_->stackPush(runtime_->newStrFromCStr("the self argument"));
374 thread_->stackPush(runtime_->newInt(88));
375 thread_->stackPush(runtime_->newInt(33));
376 thread_->stackPush(runtime_->newStrFromCStr("value"));
377 thread_->stackPush(*kw_names);
378 EXPECT_TRUE(raisedWithStr(Interpreter::callKw(thread_, 4),
379 LayoutId::kTypeError,
380 "'foo' takes no keyword arguments"));
381}
382
383TEST_F(MethodTrampolinesTest, VarArgsExWithoutKwargs) {
384 HandleScope scope(thread_);
385 Object self(&scope, runtime_->newStrFromCStr("the self argument"));
386 Object num1(&scope, runtime_->newInt(88));
387 Object num2(&scope, runtime_->newInt(33));
388 Tuple args(&scope, runtime_->newTupleWith3(self, num1, num2));
389 thread_->stackPush(newFunctionVarArgs(thread_));
390 thread_->stackPush(*args);
391 EXPECT_TRUE(isIntEqualsWord(Interpreter::callEx(thread_, 0), 1239));
392}
393
394TEST_F(MethodTrampolinesTest, VarArgsExWithKwargs) {
395 HandleScope scope(thread_);
396 Object self(&scope, runtime_->newStrFromCStr("the self argument"));
397 Object num1(&scope, runtime_->newInt(88));
398 Object num2(&scope, runtime_->newInt(33));
399 Tuple args(&scope, runtime_->newTupleWith3(self, num1, num2));
400 thread_->stackPush(newFunctionVarArgs(thread_));
401 thread_->stackPush(*args);
402 thread_->stackPush(runtime_->newDict());
403 EXPECT_TRUE(isIntEqualsWord(
404 Interpreter::callEx(thread_, CallFunctionExFlag::VAR_KEYWORDS), 1239));
405}
406
407TEST_F(MethodTrampolinesTest, VarArgsExWithoutSelfRaisesTypeError) {
408 thread_->stackPush(newFunctionVarArgs(thread_));
409 thread_->stackPush(runtime_->emptyTuple());
410 EXPECT_TRUE(raisedWithStr(Interpreter::callEx(thread_, 0),
411 LayoutId::kTypeError,
412 "'foo' must be bound to an object"));
413}
414
415TEST_F(MethodTrampolinesTest, VarArgsExWithKeywordArgRaisesTypeError) {
416 HandleScope scope(thread_);
417 Object self(&scope, runtime_->newStrFromCStr("the self argument"));
418 Object num1(&scope, runtime_->newInt(88));
419 Object num2(&scope, runtime_->newInt(33));
420 Tuple args(&scope, runtime_->newTupleWith3(self, num1, num2));
421 Dict kwargs(&scope, runtime_->newDict());
422 Object value(&scope, runtime_->newStrFromCStr("value"));
423 dictAtPutById(thread_, kwargs, ID(key), value);
424 thread_->stackPush(newFunctionVarArgs(thread_));
425 thread_->stackPush(*args);
426 thread_->stackPush(*kwargs);
427 EXPECT_TRUE(raisedWithStr(
428 Interpreter::callEx(thread_, CallFunctionExFlag::VAR_KEYWORDS),
429 LayoutId::kTypeError, "'foo' takes no keyword arguments"));
430}
431
432static PyObject* capiFunctionKeywordsNullKwargs(PyObject* self, PyObject* args,
433 PyObject* kwargs) {
434 Thread* thread = Thread::current();
435 HandleScope scope(thread);
436 Runtime* runtime = thread->runtime();
437 runtime->collectGarbage();
438 ApiHandle* self_handle = ApiHandle::fromPyObject(self);
439 EXPECT_GT(ApiHandle::refcnt(self_handle), 0);
440 EXPECT_TRUE(isStrEqualsCStr(ApiHandle::asObject(self_handle), "the self argument"));
441 ApiHandle* args_handle = ApiHandle::fromPyObject(args);
442 EXPECT_GT(ApiHandle::refcnt(args_handle), 0);
443 Object args_obj(&scope, ApiHandle::asObject(args_handle));
444 EXPECT_TRUE(args_obj.isTuple());
445 Tuple args_tuple(&scope, *args_obj);
446 EXPECT_EQ(args_tuple.length(), 2);
447 EXPECT_TRUE(isIntEqualsWord(args_tuple.at(0), 17));
448 EXPECT_TRUE(isIntEqualsWord(args_tuple.at(1), -8));
449 EXPECT_EQ(kwargs, nullptr);
450 return ApiHandle::newReference(runtime, SmallInt::fromWord(1237));
451}
452
453static RawObject newFunctionKeywordsNullKwargs(Thread* thread) {
454 HandleScope scope(thread);
455 Runtime* runtime = thread->runtime();
456 Object name(&scope, runtime->newStrFromCStr("foo"));
457 PyCFunctionWithKeywords function_ptr = capiFunctionKeywordsNullKwargs;
458 return newExtensionFunction(thread, name,
459 reinterpret_cast<void*>(function_ptr),
460 METH_VARARGS | METH_KEYWORDS);
461}
462
463TEST_F(MethodTrampolinesTest, Keywords) {
464 HandleScope scope(thread_);
465 Object function(&scope, newFunctionKeywordsNullKwargs(thread_));
466 Object arg0(&scope, runtime_->newStrFromCStr("the self argument"));
467 Object arg1(&scope, runtime_->newInt(17));
468 Object arg2(&scope, runtime_->newInt(-8));
469 EXPECT_TRUE(isIntEqualsWord(
470 Interpreter::call3(thread_, function, arg0, arg1, arg2), 1237));
471}
472
473TEST_F(MethodTrampolinesTest, KeywordsWithoutSelfRaisesTypeError) {
474 HandleScope scope(thread_);
475 Object function(&scope, newFunctionKeywordsNullKwargs(thread_));
476 EXPECT_TRUE(raisedWithStr(Interpreter::call0(thread_, function),
477 LayoutId::kTypeError,
478 "'foo' must be bound to an object"));
479}
480
481static PyObject* capiFunctionKeywords(PyObject* self, PyObject* args,
482 PyObject* kwargs) {
483 Thread* thread = Thread::current();
484 HandleScope scope(thread);
485 Runtime* runtime = thread->runtime();
486 runtime->collectGarbage();
487 ApiHandle* self_handle = ApiHandle::fromPyObject(self);
488 EXPECT_GT(ApiHandle::refcnt(self_handle), 0);
489 EXPECT_TRUE(isStrEqualsCStr(ApiHandle::asObject(self_handle), "the self argument"));
490 ApiHandle* args_handle = ApiHandle::fromPyObject(args);
491 EXPECT_GT(ApiHandle::refcnt(args_handle), 0);
492 Object args_obj(&scope, ApiHandle::asObject(args_handle));
493 EXPECT_TRUE(args_obj.isTuple());
494 Tuple args_tuple(&scope, *args_obj);
495 EXPECT_EQ(args_tuple.length(), 2);
496 EXPECT_TRUE(isIntEqualsWord(args_tuple.at(0), 17));
497 EXPECT_TRUE(isIntEqualsWord(args_tuple.at(1), -8));
498
499 ApiHandle* kwargs_handle = ApiHandle::fromPyObject(kwargs);
500 EXPECT_GT(ApiHandle::refcnt(kwargs_handle), 0);
501 Object kwargs_obj(&scope, ApiHandle::asObject(kwargs_handle));
502 EXPECT_TRUE(kwargs_obj.isDict());
503 Dict kwargs_dict(&scope, *kwargs_obj);
504 EXPECT_EQ(kwargs_dict.numItems(), 1);
505 EXPECT_TRUE(
506 isStrEqualsCStr(dictAtById(thread, kwargs_dict, ID(key)), "value"));
507 return ApiHandle::newReference(runtime, SmallInt::fromWord(1237));
508}
509
510static RawObject newFunctionKeywords(Thread* thread) {
511 HandleScope scope(thread);
512 Runtime* runtime = thread->runtime();
513 Object name(&scope, runtime->newStrFromCStr("foo"));
514 PyCFunctionWithKeywords function_ptr = capiFunctionKeywords;
515 return newExtensionFunction(thread, name,
516 reinterpret_cast<void*>(function_ptr),
517 METH_VARARGS | METH_KEYWORDS);
518}
519
520TEST_F(MethodTrampolinesTest, KeywordsKw) {
521 HandleScope scope(thread_);
522 Object key(&scope, runtime_->newStrFromCStr("key"));
523 Tuple kw_names(&scope, runtime_->newTupleWith1(key));
524 thread_->stackPush(newFunctionKeywords(thread_));
525 thread_->stackPush(runtime_->newStrFromCStr("the self argument"));
526 thread_->stackPush(runtime_->newInt(17));
527 thread_->stackPush(runtime_->newInt(-8));
528 thread_->stackPush(runtime_->newStrFromCStr("value"));
529 thread_->stackPush(*kw_names);
530 EXPECT_TRUE(isIntEqualsWord(Interpreter::callKw(thread_, 4), 1237));
531}
532
533TEST_F(MethodTrampolinesTest, KeywordsKwWithoutSelfRaisesTypeError) {
534 HandleScope scope(thread_);
535 Object key(&scope, runtime_->newStrFromCStr("key"));
536 Tuple kw_names(&scope, runtime_->newTupleWith1(key));
537 thread_->stackPush(newFunctionKeywords(thread_));
538 thread_->stackPush(runtime_->newStrFromCStr("value"));
539 thread_->stackPush(*kw_names);
540 EXPECT_TRUE(raisedWithStr(Interpreter::callKw(thread_, 1),
541 LayoutId::kTypeError,
542 "'foo' must be bound to an object"));
543}
544
545TEST_F(MethodTrampolinesTest, KeywordsExWithoutKwargs) {
546 HandleScope scope(thread_);
547 Object self(&scope, runtime_->newStrFromCStr("the self argument"));
548 Object num1(&scope, runtime_->newInt(17));
549 Object num2(&scope, runtime_->newInt(-8));
550 Tuple args(&scope, runtime_->newTupleWith3(self, num1, num2));
551 thread_->stackPush(newFunctionKeywordsNullKwargs(thread_));
552 thread_->stackPush(*args);
553 EXPECT_TRUE(isIntEqualsWord(Interpreter::callEx(thread_, 0), 1237));
554}
555
556TEST_F(MethodTrampolinesTest, KeywordsExWithKwargs) {
557 HandleScope scope(thread_);
558 Object self(&scope, runtime_->newStrFromCStr("the self argument"));
559 Object num1(&scope, runtime_->newInt(17));
560 Object num2(&scope, runtime_->newInt(-8));
561 Tuple args(&scope, runtime_->newTupleWith3(self, num1, num2));
562 Dict kwargs(&scope, runtime_->newDict());
563 Object value(&scope, runtime_->newStrFromCStr("value"));
564 dictAtPutById(thread_, kwargs, ID(key), value);
565 thread_->stackPush(newFunctionKeywords(thread_));
566 thread_->stackPush(*args);
567 thread_->stackPush(*kwargs);
568 EXPECT_TRUE(isIntEqualsWord(
569 Interpreter::callEx(thread_, CallFunctionExFlag::VAR_KEYWORDS), 1237));
570}
571
572TEST_F(MethodTrampolinesTest, KeywordsExWithoutSelfRaisesTypeError) {
573 thread_->stackPush(newFunctionKeywordsNullKwargs(thread_));
574 thread_->stackPush(runtime_->emptyTuple());
575 EXPECT_TRUE(raisedWithStr(Interpreter::callEx(thread_, 0),
576 LayoutId::kTypeError,
577 "'foo' must be bound to an object"));
578}
579
580static PyObject* capiFunctionFast(PyObject* self, PyObject* const* args,
581 Py_ssize_t nargs) {
582 Runtime* runtime = Thread::current()->runtime();
583 runtime->collectGarbage();
584 ApiHandle* self_handle = ApiHandle::fromPyObject(self);
585 EXPECT_GT(ApiHandle::refcnt(self_handle), 0);
586 EXPECT_TRUE(isStrEqualsCStr(ApiHandle::asObject(ApiHandle::fromPyObject(self)),
587 "the self argument"));
588 EXPECT_EQ(nargs, 2);
589 ApiHandle* arg0_handle = ApiHandle::fromPyObject(args[0]);
590 EXPECT_GT(ApiHandle::refcnt(arg0_handle), 0);
591 ApiHandle* arg1_handle = ApiHandle::fromPyObject(args[1]);
592 EXPECT_GT(ApiHandle::refcnt(arg1_handle), 0);
593 EXPECT_TRUE(isFloatEqualsDouble(ApiHandle::asObject(arg0_handle), -13.));
594 EXPECT_TRUE(isFloatEqualsDouble(ApiHandle::asObject(arg1_handle), 0.125));
595 return ApiHandle::newReference(runtime, SmallInt::fromWord(1236));
596}
597
598static RawObject newExtensionFunctionFast(Thread* thread) {
599 HandleScope scope(thread);
600 Runtime* runtime = thread->runtime();
601 Object name(&scope, runtime->newStrFromCStr("foo"));
602 _PyCFunctionFast function_ptr = capiFunctionFast;
603 return newExtensionFunction(
604 thread, name, reinterpret_cast<void*>(function_ptr), METH_FASTCALL);
605}
606
607TEST_F(MethodTrampolinesTest, Fast) {
608 HandleScope scope(thread_);
609 Object function(&scope, newExtensionFunctionFast(thread_));
610 Object arg0(&scope, runtime_->newStrFromCStr("the self argument"));
611 Object arg1(&scope, runtime_->newFloat(-13.));
612 Object arg2(&scope, runtime_->newFloat(0.125));
613 EXPECT_TRUE(isIntEqualsWord(
614 Interpreter::call3(thread_, function, arg0, arg1, arg2), 1236));
615}
616
617TEST_F(MethodTrampolinesTest, FastWithoutSelfRaisesTypeError) {
618 HandleScope scope(thread_);
619 Object function(&scope, newExtensionFunctionFast(thread_));
620 EXPECT_TRUE(raisedWithStr(Interpreter::call0(thread_, function),
621 LayoutId::kTypeError,
622 "'foo' must be bound to an object"));
623}
624
625TEST_F(MethodTrampolinesTest, FastKw) {
626 thread_->stackPush(newExtensionFunctionFast(thread_));
627 thread_->stackPush(runtime_->newStrFromCStr("the self argument"));
628 thread_->stackPush(runtime_->newFloat(-13.));
629 thread_->stackPush(runtime_->newFloat(0.125));
630 thread_->stackPush(runtime_->emptyTuple());
631 EXPECT_TRUE(isIntEqualsWord(Interpreter::callKw(thread_, 3), 1236));
632}
633
634TEST_F(MethodTrampolinesTest, FastKwWithoutSelfRaisesTypeError) {
635 thread_->stackPush(newExtensionFunctionFast(thread_));
636 thread_->stackPush(runtime_->emptyTuple());
637 EXPECT_TRUE(raisedWithStr(Interpreter::callKw(thread_, 0),
638 LayoutId::kTypeError,
639 "'foo' must be bound to an object"));
640}
641
642TEST_F(MethodTrampolinesTest, FastKwWithKeywordRaisesTypeError) {
643 HandleScope scope(thread_);
644 Object key(&scope, runtime_->newStrFromCStr("key"));
645 Tuple kw_names(&scope, runtime_->newTupleWith1(key));
646 thread_->stackPush(newExtensionFunctionFast(thread_));
647 thread_->stackPush(runtime_->newStrFromCStr("the self argument"));
648 thread_->stackPush(runtime_->newFloat(-13.));
649 thread_->stackPush(runtime_->newFloat(0.125));
650 thread_->stackPush(runtime_->newStrFromCStr("value"));
651 thread_->stackPush(*kw_names);
652 EXPECT_TRUE(raisedWithStr(Interpreter::callKw(thread_, 4),
653 LayoutId::kTypeError,
654 "'foo' takes no keyword arguments"));
655}
656
657TEST_F(MethodTrampolinesTest, FastExWithoutKwargs) {
658 HandleScope scope(thread_);
659 Object self(&scope, runtime_->newStrFromCStr("the self argument"));
660 Object num1(&scope, runtime_->newFloat(-13.));
661 Object num2(&scope, runtime_->newFloat(0.125));
662 Tuple args(&scope, runtime_->newTupleWith3(self, num1, num2));
663 thread_->stackPush(newExtensionFunctionFast(thread_));
664 thread_->stackPush(*args);
665 EXPECT_TRUE(isIntEqualsWord(Interpreter::callEx(thread_, 0), 1236));
666}
667
668TEST_F(MethodTrampolinesTest, FastExWithKwargs) {
669 HandleScope scope(thread_);
670 Object self(&scope, runtime_->newStrFromCStr("the self argument"));
671 Object num1(&scope, runtime_->newFloat(-13.));
672 Object num2(&scope, runtime_->newFloat(0.125));
673 Tuple args(&scope, runtime_->newTupleWith3(self, num1, num2));
674 thread_->stackPush(newExtensionFunctionFast(thread_));
675 thread_->stackPush(*args);
676 thread_->stackPush(runtime_->newDict());
677 EXPECT_TRUE(isIntEqualsWord(
678 Interpreter::callEx(thread_, CallFunctionExFlag::VAR_KEYWORDS), 1236));
679}
680
681TEST_F(MethodTrampolinesTest, FastExWithoutSelfRaisesTypeError) {
682 thread_->stackPush(newExtensionFunctionFast(thread_));
683 thread_->stackPush(runtime_->emptyTuple());
684 EXPECT_TRUE(raisedWithStr(Interpreter::callEx(thread_, 0),
685 LayoutId::kTypeError,
686 "'foo' must be bound to an object"));
687}
688
689TEST_F(MethodTrampolinesTest, FastExWithKeywordArgRaisesTypeError) {
690 HandleScope scope(thread_);
691 Object self(&scope, runtime_->newStrFromCStr("the self argument"));
692 Object num1(&scope, runtime_->newFloat(-13.));
693 Object num2(&scope, runtime_->newFloat(0.125));
694 Tuple args(&scope, runtime_->newTupleWith3(self, num1, num2));
695 Dict kwargs(&scope, runtime_->newDict());
696 Object value(&scope, runtime_->newStrFromCStr("value"));
697 dictAtPutById(thread_, kwargs, ID(key), value);
698 thread_->stackPush(newExtensionFunctionFast(thread_));
699 thread_->stackPush(*args);
700 thread_->stackPush(*kwargs);
701 EXPECT_TRUE(raisedWithStr(
702 Interpreter::callEx(thread_, CallFunctionExFlag::VAR_KEYWORDS),
703 LayoutId::kTypeError, "'foo' takes no keyword arguments"));
704}
705
706static PyObject* capiFunctionFastWithKeywordsNullKwnames(PyObject* self,
707 PyObject* const* args,
708 Py_ssize_t nargs,
709 PyObject* kwnames) {
710 Runtime* runtime = Thread::current()->runtime();
711 runtime->collectGarbage();
712 ApiHandle* self_handle = ApiHandle::fromPyObject(self);
713 EXPECT_GT(ApiHandle::refcnt(self_handle), 0);
714 EXPECT_TRUE(isStrEqualsCStr(ApiHandle::asObject(ApiHandle::fromPyObject(self)),
715 "the self argument"));
716 EXPECT_EQ(nargs, 2);
717 ApiHandle* arg0_handle = ApiHandle::fromPyObject(args[0]);
718 EXPECT_GT(ApiHandle::refcnt(arg0_handle), 0);
719 ApiHandle* arg1_handle = ApiHandle::fromPyObject(args[1]);
720 EXPECT_GT(ApiHandle::refcnt(arg1_handle), 0);
721 EXPECT_TRUE(isFloatEqualsDouble(ApiHandle::asObject(arg0_handle), 42.5));
722 EXPECT_TRUE(isFloatEqualsDouble(ApiHandle::asObject(arg1_handle), -8.8));
723 EXPECT_EQ(kwnames, nullptr);
724 return ApiHandle::newReference(runtime, SmallInt::fromWord(1238));
725}
726
727static RawObject newExtensionFunctionFastWithKeywordsNullKwnames(
728 Thread* thread) {
729 HandleScope scope(thread);
730 Runtime* runtime = thread->runtime();
731 Object name(&scope, runtime->newStrFromCStr("foo"));
732 _PyCFunctionFastWithKeywords function_ptr =
733 capiFunctionFastWithKeywordsNullKwnames;
734 return newExtensionFunction(thread, name,
735 reinterpret_cast<void*>(function_ptr),
736 METH_FASTCALL | METH_KEYWORDS);
737}
738
739TEST_F(MethodTrampolinesTest, FastWithKeywords) {
740 HandleScope scope(thread_);
741 Object function(&scope,
742 newExtensionFunctionFastWithKeywordsNullKwnames(thread_));
743 Object arg0(&scope, runtime_->newStrFromCStr("the self argument"));
744 Object arg1(&scope, runtime_->newFloat(42.5));
745 Object arg2(&scope, runtime_->newFloat(-8.8));
746 EXPECT_TRUE(isIntEqualsWord(
747 Interpreter::call3(thread_, function, arg0, arg1, arg2), 1238));
748}
749
750TEST_F(MethodTrampolinesTest, FastWithKeywordsWithoutSelfRaisesTypeError) {
751 HandleScope scope(thread_);
752 Object function(&scope,
753 newExtensionFunctionFastWithKeywordsNullKwnames(thread_));
754 EXPECT_TRUE(raisedWithStr(Interpreter::call0(thread_, function),
755 LayoutId::kTypeError,
756 "'foo' must be bound to an object"));
757}
758
759static PyObject* capiFunctionFastWithKeywords(PyObject* self,
760 PyObject* const* args,
761 Py_ssize_t nargs,
762 PyObject* kwnames) {
763 Thread* thread = Thread::current();
764 HandleScope scope(thread);
765 Runtime* runtime = thread->runtime();
766 runtime->collectGarbage();
767 ApiHandle* self_handle = ApiHandle::fromPyObject(self);
768 EXPECT_GT(ApiHandle::refcnt(self_handle), 0);
769 EXPECT_TRUE(isStrEqualsCStr(ApiHandle::asObject(ApiHandle::fromPyObject(self)),
770 "the self argument"));
771 EXPECT_EQ(nargs, 2);
772 ApiHandle* arg0_handle = ApiHandle::fromPyObject(args[0]);
773 EXPECT_GT(ApiHandle::refcnt(arg0_handle), 0);
774 ApiHandle* arg1_handle = ApiHandle::fromPyObject(args[1]);
775 EXPECT_GT(ApiHandle::refcnt(arg1_handle), 0);
776 EXPECT_TRUE(isFloatEqualsDouble(ApiHandle::asObject(arg0_handle), 42.5));
777 EXPECT_TRUE(isFloatEqualsDouble(ApiHandle::asObject(arg1_handle), -8.8));
778
779 ApiHandle* kwnames_handle = ApiHandle::fromPyObject(kwnames);
780 EXPECT_GT(ApiHandle::refcnt(kwnames_handle), 0);
781 Object kwnames_obj(&scope, ApiHandle::asObject(kwnames_handle));
782 EXPECT_TRUE(kwnames_obj.isTuple());
783 Tuple kwnames_tuple(&scope, *kwnames_obj);
784 EXPECT_EQ(kwnames_tuple.length(), 2);
785 EXPECT_TRUE(isStrEqualsCStr(kwnames_tuple.at(0), "foo"));
786 EXPECT_TRUE(isStrEqualsCStr(kwnames_tuple.at(1), "bar"));
787 EXPECT_TRUE(isStrEqualsCStr(ApiHandle::asObject(ApiHandle::fromPyObject(args[2])),
788 "foo_value"));
789 EXPECT_TRUE(isStrEqualsCStr(ApiHandle::asObject(ApiHandle::fromPyObject(args[3])),
790 "bar_value"));
791 return ApiHandle::newReference(runtime, SmallInt::fromWord(1238));
792}
793
794static RawObject newExtensionFunctionFastWithKeywords(Thread* thread) {
795 HandleScope scope(thread);
796 Runtime* runtime = thread->runtime();
797 Object name(&scope, runtime->newStrFromCStr("foo"));
798 _PyCFunctionFastWithKeywords function_ptr = capiFunctionFastWithKeywords;
799 return newExtensionFunction(thread, name,
800 reinterpret_cast<void*>(function_ptr),
801 METH_FASTCALL | METH_KEYWORDS);
802}
803
804TEST_F(MethodTrampolinesTest, FastWithKeywordsKw) {
805 HandleScope scope(thread_);
806 Object foo(&scope, runtime_->newStrFromCStr("foo"));
807 Object bar(&scope, runtime_->newStrFromCStr("bar"));
808 Tuple kw_names(&scope, runtime_->newTupleWith2(foo, bar));
809 thread_->stackPush(newExtensionFunctionFastWithKeywords(thread_));
810 thread_->stackPush(runtime_->newStrFromCStr("the self argument"));
811 thread_->stackPush(runtime_->newFloat(42.5));
812 thread_->stackPush(runtime_->newFloat(-8.8));
813 thread_->stackPush(runtime_->newStrFromCStr("foo_value"));
814 thread_->stackPush(runtime_->newStrFromCStr("bar_value"));
815 thread_->stackPush(*kw_names);
816 EXPECT_TRUE(isIntEqualsWord(Interpreter::callKw(thread_, 5), 1238));
817}
818
819TEST_F(MethodTrampolinesTest, FastWithKeywordsKwWihoutSelfRaisesTypeError) {
820 thread_->stackPush(newExtensionFunctionFastWithKeywords(thread_));
821 thread_->stackPush(runtime_->emptyTuple());
822 EXPECT_TRUE(raisedWithStr(Interpreter::callKw(thread_, 0),
823 LayoutId::kTypeError,
824 "'foo' must be bound to an object"));
825}
826
827TEST_F(MethodTrampolinesTest, FastWithKeywordsExWithoutKwargs) {
828 HandleScope scope(thread_);
829 Object self(&scope, runtime_->newStrFromCStr("the self argument"));
830 Object num1(&scope, runtime_->newFloat(42.5));
831 Object num2(&scope, runtime_->newFloat(-8.8));
832 Tuple args(&scope, runtime_->newTupleWith3(self, num1, num2));
833 thread_->stackPush(newExtensionFunctionFastWithKeywordsNullKwnames(thread_));
834 thread_->stackPush(*args);
835 EXPECT_TRUE(isIntEqualsWord(Interpreter::callEx(thread_, 0), 1238));
836}
837
838TEST_F(MethodTrampolinesTest, FastWithKeywordsExWithKwargs) {
839 HandleScope scope(thread_);
840 Object self(&scope, runtime_->newStrFromCStr("the self argument"));
841 Object num1(&scope, runtime_->newFloat(42.5));
842 Object num2(&scope, runtime_->newFloat(-8.8));
843 Tuple args(&scope, runtime_->newTupleWith3(self, num1, num2));
844 Dict kwargs(&scope, runtime_->newDict());
845 Object foo(&scope, runtime_->newStrFromCStr("foo"));
846 Object foo_value(&scope, runtime_->newStrFromCStr("foo_value"));
847 dictAtPutByStr(thread_, kwargs, foo, foo_value);
848 Object bar(&scope, runtime_->newStrFromCStr("bar"));
849 Object bar_value(&scope, runtime_->newStrFromCStr("bar_value"));
850 dictAtPutByStr(thread_, kwargs, bar, bar_value);
851 thread_->stackPush(newExtensionFunctionFastWithKeywords(thread_));
852 thread_->stackPush(*args);
853 thread_->stackPush(*kwargs);
854 EXPECT_TRUE(isIntEqualsWord(
855 Interpreter::callEx(thread_, CallFunctionExFlag::VAR_KEYWORDS), 1238));
856}
857
858TEST_F(MethodTrampolinesTest, FastWithKeywordsExWithoutSelfRaisesTypeError) {
859 thread_->stackPush(newExtensionFunctionFastWithKeywords(thread_));
860 thread_->stackPush(runtime_->emptyTuple());
861 EXPECT_TRUE(raisedWithStr(Interpreter::callEx(thread_, 0),
862 LayoutId::kTypeError,
863 "'foo' must be bound to an object"));
864}
865
866} // namespace testing
867} // namespace py