runtime/trampolines-test.cpp (960 lines of code) (raw):
// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
#include <memory>
#include "gtest/gtest.h"
#include "dict-builtins.h"
#include "frame.h"
#include "function-builtins.h"
#include "int-builtins.h"
#include "modules.h"
#include "runtime.h"
#include "test-utils.h"
namespace py {
namespace testing {
using CallTest = RuntimeFixture;
using TrampolinesTest = RuntimeFixture;
TEST_F(CallTest, CallBoundMethod) {
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, R"(
def func(self):
return self
def test(callable):
return callable()
)")
.isError());
Object function(&scope, mainModuleAt(runtime_, "func"));
ASSERT_TRUE(function.isFunction());
Object self(&scope, SmallInt::fromWord(1111));
BoundMethod method(&scope, runtime_->newBoundMethod(function, self));
Object test(&scope, mainModuleAt(runtime_, "test"));
ASSERT_TRUE(test.isFunction());
Function func(&scope, *test);
Object result(&scope, Interpreter::call1(thread_, func, method));
EXPECT_TRUE(isIntEqualsWord(*result, 1111));
}
TEST_F(CallTest, CallBoundMethodWithArgs) {
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, R"(
def func(self, a, b):
return [self, a, b]
def test(callable):
return callable(2222, 3333)
)")
.isError());
Object function(&scope, mainModuleAt(runtime_, "func"));
ASSERT_TRUE(function.isFunction());
Object self(&scope, SmallInt::fromWord(1111));
BoundMethod method(&scope, runtime_->newBoundMethod(function, self));
Object test(&scope, mainModuleAt(runtime_, "test"));
ASSERT_TRUE(test.isFunction());
Function func(&scope, *test);
Object result(&scope, Interpreter::call1(thread_, func, method));
EXPECT_PYLIST_EQ(result, {1111, 2222, 3333});
}
TEST_F(CallTest, CallBoundMethodKw) {
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, R"(
result_self = None
result_a = None
result_b = None
def func(self, a, b):
global result_self, result_a, result_b
result_self = self
result_a = a
result_b = b
def test(callable):
return callable(a=2222, b=3333)
)")
.isError());
Object function(&scope, mainModuleAt(runtime_, "func"));
ASSERT_TRUE(function.isFunction());
Object self(&scope, SmallInt::fromWord(1111));
BoundMethod method(&scope, runtime_->newBoundMethod(function, self));
Object test(&scope, mainModuleAt(runtime_, "test"));
ASSERT_TRUE(test.isFunction());
Function func(&scope, *test);
Tuple args(&scope, runtime_->newTupleWith1(method));
callFunction(func, args);
Object result_self(&scope, mainModuleAt(runtime_, "result_self"));
EXPECT_TRUE(isIntEqualsWord(*result_self, 1111));
Object result_a(&scope, mainModuleAt(runtime_, "result_a"));
EXPECT_TRUE(isIntEqualsWord(*result_a, 2222));
Object result_b(&scope, mainModuleAt(runtime_, "result_b"));
EXPECT_TRUE(isIntEqualsWord(*result_b, 3333));
}
TEST_F(CallTest, CallBoundMethodExArgs) {
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, R"(
result_self = None
result_a = None
result_b = None
def func(self, a, b):
global result_self, result_a, result_b
result_self = self
result_a = a
result_b = b
def test(callable):
args = (2222, 3333)
return callable(*args)
)")
.isError());
Object function(&scope, mainModuleAt(runtime_, "func"));
ASSERT_TRUE(function.isFunction());
Object self(&scope, SmallInt::fromWord(1111));
BoundMethod method(&scope, runtime_->newBoundMethod(function, self));
Object test(&scope, mainModuleAt(runtime_, "test"));
ASSERT_TRUE(test.isFunction());
Function func(&scope, *test);
Tuple args(&scope, runtime_->newTupleWith1(method));
callFunction(func, args);
Object result_self(&scope, mainModuleAt(runtime_, "result_self"));
EXPECT_TRUE(isIntEqualsWord(*result_self, 1111));
Object result_a(&scope, mainModuleAt(runtime_, "result_a"));
EXPECT_TRUE(isIntEqualsWord(*result_a, 2222));
Object result_b(&scope, mainModuleAt(runtime_, "result_b"));
EXPECT_TRUE(isIntEqualsWord(*result_b, 3333));
}
TEST_F(CallTest, CallBoundMethodExKwargs) {
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, R"(
result_self = None
result_a = None
result_b = None
def func(self, a, b):
global result_self, result_a, result_b
result_self = self
result_a = a
result_b = b
def test(callable):
kwargs = {'a': 2222, 'b': 3333}
return callable(**kwargs)
)")
.isError());
Object function(&scope, mainModuleAt(runtime_, "func"));
ASSERT_TRUE(function.isFunction());
Object self(&scope, SmallInt::fromWord(1111));
BoundMethod method(&scope, runtime_->newBoundMethod(function, self));
Object test(&scope, mainModuleAt(runtime_, "test"));
ASSERT_TRUE(test.isFunction());
Function func(&scope, *test);
Tuple args(&scope, runtime_->newTupleWith1(method));
callFunction(func, args);
Object result_self(&scope, mainModuleAt(runtime_, "result_self"));
EXPECT_TRUE(isIntEqualsWord(*result_self, 1111));
Object result_a(&scope, mainModuleAt(runtime_, "result_a"));
EXPECT_TRUE(isIntEqualsWord(*result_a, 2222));
Object result_b(&scope, mainModuleAt(runtime_, "result_b"));
EXPECT_TRUE(isIntEqualsWord(*result_b, 3333));
}
TEST_F(CallTest, CallBoundMethodExArgsAndKwargs) {
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, R"(
result_self = None
result_a = None
result_b = None
def func(self, a, b):
global result_self, result_a, result_b
result_self = self
result_a = a
result_b = b
def test(callable):
args = (2222,)
kwargs = {'b': 3333}
return callable(*args, **kwargs)
)")
.isError());
Object function(&scope, mainModuleAt(runtime_, "func"));
ASSERT_TRUE(function.isFunction());
Object self(&scope, SmallInt::fromWord(1111));
BoundMethod method(&scope, runtime_->newBoundMethod(function, self));
Object test(&scope, mainModuleAt(runtime_, "test"));
ASSERT_TRUE(test.isFunction());
Function func(&scope, *test);
Tuple args(&scope, runtime_->newTupleWith1(method));
callFunction(func, args);
Object result_self(&scope, mainModuleAt(runtime_, "result_self"));
EXPECT_TRUE(isIntEqualsWord(*result_self, 1111));
Object result_a(&scope, mainModuleAt(runtime_, "result_a"));
EXPECT_TRUE(isIntEqualsWord(*result_a, 2222));
Object result_b(&scope, mainModuleAt(runtime_, "result_b"));
EXPECT_TRUE(isIntEqualsWord(*result_b, 3333));
}
TEST_F(CallTest, CallDefaultArgs) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
def foo(a=1, b=2, c=3):
return [a, b, c]
result0 = foo(33, 22, 11)
result1 = foo()
result2 = foo(1001)
result3 = foo(1001, 1002)
result4 = foo(1001, 1002, 1003)
)")
.isError());
HandleScope scope(thread_);
Object result0(&scope, mainModuleAt(runtime_, "result0"));
EXPECT_PYLIST_EQ(result0, {33, 22, 11});
Object result1(&scope, mainModuleAt(runtime_, "result1"));
EXPECT_PYLIST_EQ(result1, {1, 2, 3});
Object result2(&scope, mainModuleAt(runtime_, "result2"));
EXPECT_PYLIST_EQ(result2, {1001, 2, 3});
Object result3(&scope, mainModuleAt(runtime_, "result3"));
EXPECT_PYLIST_EQ(result3, {1001, 1002, 3});
Object result4(&scope, mainModuleAt(runtime_, "result4"));
EXPECT_PYLIST_EQ(result4, {1001, 1002, 1003});
}
TEST_F(CallTest, CallMethodMixPosDefaultArgs) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
def foo(a, b=2):
return [a, b]
result = foo(1)
)")
.isError());
HandleScope scope(thread_);
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_PYLIST_EQ(result, {1, 2});
}
TEST_F(CallTest, CallBoundMethodMixed) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
class R:
def m(self, a, b=2):
return [a, b]
r = R()
result = r.m(9)
)")
.isError());
HandleScope scope(thread_);
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_PYLIST_EQ(result, {9, 2});
}
TEST_F(CallTest, SingleKW) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
def foo(bar):
return bar
result = foo(bar=2)
)")
.isError());
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 2));
}
TEST_F(CallTest, MixedKW) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
def foo(a, b, c):
return [a, b, c]
result = foo(1, b = 2, c = 3)
)")
.isError());
HandleScope scope(thread_);
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_PYLIST_EQ(result, {1, 2, 3});
}
TEST_F(CallTest, FullKW) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
def foo(a, b, c):
return [a, b, c]
result = foo(a = 1, b = 2, c = 3)
)")
.isError());
HandleScope scope(thread_);
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_PYLIST_EQ(result, {1, 2, 3});
}
TEST_F(CallTest, KWOutOfOrder1) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
def foo(a, b, c):
return [a, b, c]
result = foo(c = 3, a = 1, b = 2)
)")
.isError());
HandleScope scope(thread_);
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_PYLIST_EQ(result, {1, 2, 3});
}
TEST_F(CallTest, KWOutOfOrder2) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
def foo(a, b, c):
return [a, b, c]
result = foo(1, c = 3, b = 2)
)")
.isError());
HandleScope scope(thread_);
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_PYLIST_EQ(result, {1, 2, 3});
}
TEST_F(CallTest, KeywordOnly1) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
def foo(a,b, *, c):
return [a,b,c]
result = foo(1, 2, c = 3)
)")
.isError());
HandleScope scope(thread_);
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_PYLIST_EQ(result, {1, 2, 3});
}
TEST_F(CallTest, KeywordOnly2) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
def foo(a,b, *, c):
return [a,b,c]
result = foo(1, b = 2, c = 3)
)")
.isError());
HandleScope scope(thread_);
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_PYLIST_EQ(result, {1, 2, 3});
}
TEST_F(CallTest, KeyWordDefaults) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
def foo(a, b = 22, c = 33):
return [a,b,c]
result = foo(11, c = 3)
)")
.isError());
HandleScope scope(thread_);
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_PYLIST_EQ(result, {11, 22, 3});
}
TEST_F(CallTest, VarArgsWithExcess) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
def foo(a, b, *c):
return [a,b,c]
result = foo(1,2,3,4,5,6)
)")
.isError());
HandleScope scope(thread_);
List result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_TRUE(isIntEqualsWord(result.at(0), 1));
EXPECT_TRUE(isIntEqualsWord(result.at(1), 2));
Tuple tuple(&scope, result.at(2));
ASSERT_EQ(tuple.length(), 4);
EXPECT_TRUE(isIntEqualsWord(tuple.at(0), 3));
EXPECT_TRUE(isIntEqualsWord(tuple.at(1), 4));
EXPECT_TRUE(isIntEqualsWord(tuple.at(2), 5));
EXPECT_TRUE(isIntEqualsWord(tuple.at(3), 6));
}
TEST_F(CallTest, VarArgsEmpty) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
def foo(a, b, *c):
return [a,b,c]
result = foo(1,2)
)")
.isError());
HandleScope scope(thread_);
List result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_TRUE(isIntEqualsWord(result.at(0), 1));
EXPECT_TRUE(isIntEqualsWord(result.at(1), 2));
Tuple tuple(&scope, result.at(2));
EXPECT_EQ(tuple.length(), 0);
}
TEST_F(CallTest, CallWithKeywordsCalleeWithVarkeyword) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
def foo(a,b,c,**d):
return [a,b,c,d]
result = foo(1,2,c=3,g=4,h=5,j="bar")
)")
.isError());
HandleScope scope(thread_);
List result(&scope, testing::mainModuleAt(runtime_, "result"));
EXPECT_TRUE(isIntEqualsWord(result.at(0), 1));
EXPECT_TRUE(isIntEqualsWord(result.at(1), 2));
EXPECT_TRUE(isIntEqualsWord(result.at(2), 3));
Dict dict(&scope, result.at(3));
Str name_g(&scope, runtime_->newStrFromCStr("g"));
EXPECT_TRUE(isIntEqualsWord(dictAtByStr(thread_, dict, name_g), 4));
Str name_h(&scope, runtime_->newStrFromCStr("h"));
EXPECT_TRUE(isIntEqualsWord(dictAtByStr(thread_, dict, name_h), 5));
Str name_j(&scope, runtime_->newStrFromCStr("j"));
EXPECT_TRUE(isStrEqualsCStr(dictAtByStr(thread_, dict, name_j), "bar"));
}
TEST_F(CallTest, CallWithNoArgsCalleeDefaultArgsVarargsVarkeyargs) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
def bar(a=1, b=2, *c, **d):
return [a,b,c,d]
result = bar()
)")
.isError());
HandleScope scope(thread_);
List result(&scope, testing::mainModuleAt(runtime_, "result"));
EXPECT_TRUE(isIntEqualsWord(result.at(0), 1));
EXPECT_TRUE(isIntEqualsWord(result.at(1), 2));
Tuple tuple(&scope, result.at(2));
EXPECT_EQ(tuple.length(), 0);
Dict dict(&scope, result.at(3));
EXPECT_EQ(dict.numItems(), 0);
}
TEST_F(CallTest, CallPositionalCalleeVargsEmptyVarkeyargs) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
def bar(a=1, b=2, *c, **d):
return [a,b,c,d]
result = bar(1,2,3,4,5,6,7)
)")
.isError());
HandleScope scope(thread_);
List result(&scope, testing::mainModuleAt(runtime_, "result"));
EXPECT_TRUE(isIntEqualsWord(result.at(0), 1));
EXPECT_TRUE(isIntEqualsWord(result.at(1), 2));
Tuple tuple(&scope, result.at(2));
ASSERT_EQ(tuple.length(), 5);
EXPECT_TRUE(isIntEqualsWord(tuple.at(0), 3));
EXPECT_TRUE(isIntEqualsWord(tuple.at(1), 4));
EXPECT_TRUE(isIntEqualsWord(tuple.at(2), 5));
EXPECT_TRUE(isIntEqualsWord(tuple.at(3), 6));
EXPECT_TRUE(isIntEqualsWord(tuple.at(4), 7));
Dict dict(&scope, result.at(3));
EXPECT_EQ(dict.numItems(), 0);
}
TEST_F(CallTest, CallWithKeywordsCalleeEmptyVarargsFullVarkeyargs) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
def bar(a=1, b=2, *c, **d):
return [a,b,c,d]
result = bar(a1=11, a2=12, a3=13)
)")
.isError());
HandleScope scope(thread_);
List result(&scope, testing::mainModuleAt(runtime_, "result"));
EXPECT_TRUE(isIntEqualsWord(result.at(0), 1));
EXPECT_TRUE(isIntEqualsWord(result.at(1), 2));
Object tuple_obj(&scope, result.at(2));
ASSERT_TRUE(tuple_obj.isTuple());
Tuple tuple(&scope, *tuple_obj);
EXPECT_EQ(tuple.length(), 0);
Dict dict(&scope, result.at(3));
Str name0(&scope, runtime_->newStrFromCStr("a3"));
EXPECT_TRUE(isIntEqualsWord(dictAtByStr(thread_, dict, name0), 13));
Str name1(&scope, runtime_->newStrFromCStr("a1"));
EXPECT_TRUE(isIntEqualsWord(dictAtByStr(thread_, dict, name1), 11));
Str name2(&scope, runtime_->newStrFromCStr("a2"));
EXPECT_TRUE(isIntEqualsWord(dictAtByStr(thread_, dict, name2), 12));
}
TEST_F(CallTest, CallWithKeywordsCalleeFullVarargsFullVarkeyargs) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
def bar(a=1, b=2, *c, **d):
return [a,b,c,d]
result = bar(1,2,3,4,5,6,7,a9=9)
)")
.isError());
HandleScope scope(thread_);
List result(&scope, testing::mainModuleAt(runtime_, "result"));
EXPECT_TRUE(isIntEqualsWord(result.at(0), 1));
EXPECT_TRUE(isIntEqualsWord(result.at(1), 2));
Tuple tuple(&scope, result.at(2));
EXPECT_TRUE(isIntEqualsWord(tuple.at(0), 3));
EXPECT_TRUE(isIntEqualsWord(tuple.at(1), 4));
EXPECT_TRUE(isIntEqualsWord(tuple.at(2), 5));
EXPECT_TRUE(isIntEqualsWord(tuple.at(3), 6));
EXPECT_TRUE(isIntEqualsWord(tuple.at(4), 7));
Dict dict(&scope, result.at(3));
Str name_g(&scope, runtime_->newStrFromCStr("a9"));
EXPECT_TRUE(isIntEqualsWord(dictAtByStr(thread_, dict, name_g), 9));
}
TEST_F(CallTest, CallWithOutOfOrderKeywords) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
def foobar(a,b,*,c):
return [a,b,c]
result = foobar(c=3,a=1,b=2)
)")
.isError());
HandleScope scope(thread_);
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_PYLIST_EQ(result, {1, 2, 3});
}
TEST_F(CallTest, CallWithKeywordsCalleeVarargsKeywordOnly) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
def foobar1(a,b,*c,d):
return [a,b,c,d]
result = foobar1(1,2,3,4,5,d=9)
)")
.isError());
HandleScope scope(thread_);
List result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_TRUE(isIntEqualsWord(result.at(0), 1));
EXPECT_TRUE(isIntEqualsWord(result.at(1), 2));
Tuple tuple(&scope, result.at(2));
ASSERT_EQ(tuple.length(), 3);
EXPECT_TRUE(isIntEqualsWord(tuple.at(0), 3));
EXPECT_TRUE(isIntEqualsWord(tuple.at(1), 4));
EXPECT_TRUE(isIntEqualsWord(tuple.at(2), 5));
EXPECT_TRUE(isIntEqualsWord(result.at(3), 9));
}
TEST_F(CallTest, CallWithKeywordsCalleeVarargsVarkeyargsKeywordOnly) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
def foobar2(a,b,*c, e, **d):
return [a,b,c,d,e]
result = foobar2(1,e=9,b=2,f1="a",f11=12)
)")
.isError());
HandleScope scope(thread_);
List result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_TRUE(isIntEqualsWord(result.at(0), 1));
EXPECT_TRUE(isIntEqualsWord(result.at(1), 2));
Tuple tuple(&scope, result.at(2));
ASSERT_EQ(tuple.length(), 0);
Dict dict(&scope, result.at(3));
Str f1(&scope, runtime_->newStrFromCStr("f1"));
EXPECT_TRUE(isStrEqualsCStr(dictAtByStr(thread_, dict, f1), "a"));
Str f11(&scope, runtime_->newStrFromCStr("f11"));
EXPECT_TRUE(isIntEqualsWord(dictAtByStr(thread_, dict, f11), 12));
EXPECT_TRUE(isIntEqualsWord(result.at(4), 9));
}
TEST_F(CallTest, CallEx) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
def foo(a,b,c,d):
return [a,b,c,d]
a = (1,2,3,4)
result = foo(*a)
)")
.isError());
HandleScope scope(thread_);
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_PYLIST_EQ(result, {1, 2, 3, 4});
}
TEST_F(CallTest, CallExBuildTupleUnpackWithCall) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
def foo(a,b,c,d):
return [a,b,c,d]
a = (3,4)
result = foo(1,2,*a)
)")
.isError());
HandleScope scope(thread_);
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_PYLIST_EQ(result, {1, 2, 3, 4});
}
TEST_F(CallTest, CallExKw) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
def foo(a,b,c,d):
return [a,b,c,d]
a = {'d': 4, 'b': 2, 'a': 1, 'c': 3}
result = foo(**a)
)")
.isError());
HandleScope scope(thread_);
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_PYLIST_EQ(result, {1, 2, 3, 4});
}
TEST_F(CallTest, KeywordOnly) {
const char* src = R"(
def foo(a,b, *, c):
print(a,b,c)
foo(1, 2, 3);
)";
EXPECT_TRUE(raised(runFromCStr(runtime_, src), LayoutId::kTypeError));
}
TEST_F(CallTest, MissingKeyword) {
const char* src = R"(
def foo(a,b, *, c):
print(a,b,c)
foo(1, 2);
)";
EXPECT_TRUE(raised(runFromCStr(runtime_, src), LayoutId::kTypeError));
}
TEST_F(CallTest, ArgNameMismatch) {
const char* src = R"(
def foo(a,b, *, c):
print(a,b,c)
foo(1, d = 2, c = 3);
)";
EXPECT_TRUE(raised(runFromCStr(runtime_, src), LayoutId::kTypeError));
}
TEST_F(CallTest, TooManyKWArgs) {
const char* src = R"(
def foo(a,b, *, c):
print(a,b,c)
foo(1, 2, 4, c = 3);
)";
EXPECT_TRUE(raised(runFromCStr(runtime_, src), LayoutId::kTypeError));
}
TEST_F(CallTest, TooManyArgs) {
const char* src = R"(
def foo(a,b, c):
print(a,b,c)
foo(1, 2, 3, 4);
)";
EXPECT_TRUE(raised(runFromCStr(runtime_, src), LayoutId::kTypeError));
}
TEST_F(CallTest, TooFewArgs) {
const char* src = R"(
def foo(a,b, c):
print(a,b,c)
foo(3, 4);
)";
EXPECT_TRUE(raised(runFromCStr(runtime_, src), LayoutId::kTypeError));
}
static ALIGN_16 RawObject builtinReturnSecondArg(Thread*, Arguments args) {
return args.get(1);
}
static void createAndPatchBuiltinReturnSecondArg(Runtime* runtime) {
// Ensure we have a __main__ module.
ASSERT_FALSE(runFromCStr(runtime, "").isError());
// def dummy(first, second):
const char* parameter_names[] = {"first", "second"};
addBuiltin("dummy", builtinReturnSecondArg, parameter_names, 0);
}
TEST_F(TrampolinesTest, BuiltinTrampolineKwPassesKwargs) {
HandleScope scope(thread_);
createAndPatchBuiltinReturnSecondArg(runtime_);
ASSERT_FALSE(runFromCStr(runtime_, "result = dummy(second=12345, first=None)")
.isError());
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_TRUE(isIntEqualsWord(*result, 12345));
}
TEST_F(TrampolinesTest, BuiltinTrampolineKwWithInvalidArgRaisesTypeError) {
createAndPatchBuiltinReturnSecondArg(runtime_);
EXPECT_TRUE(raisedWithStr(
runFromCStr(runtime_, "dummy(third=3, first=1)"), LayoutId::kTypeError,
"dummy() got an unexpected keyword argument 'third'"));
}
TEST_F(TrampolinesTest, InterpreterClosureUsesArgOverCellValue) {
HandleScope scope(thread_);
// Create code object
word nlocals = 1;
Object bar(&scope, Runtime::internStrFromCStr(thread_, "bar"));
Tuple varnames(&scope, runtime_->newTupleWithN(nlocals, &bar));
Tuple cellvars(&scope, runtime_->newTupleWith1(bar));
const byte bytecode[] = {LOAD_CLOSURE, 0, LOAD_DEREF, 0, RETURN_VALUE, 0};
Bytes bc(&scope, runtime_->newBytesWithAll(bytecode));
Tuple empty_tuple(&scope, runtime_->emptyTuple());
Object empty_str(&scope, Str::empty());
Object empty_bytes(&scope, Bytes::empty());
word flags = Code::Flags::kOptimized | Code::Flags::kNewlocals;
Code code(&scope,
runtime_->newCode(/*argcount=*/1, /*posonlyargcount=*/0,
/*kwonlyargcount=*/0, nlocals, /*stacksize=*/0,
flags, /*code=*/bc, /*consts=*/empty_tuple,
/*names=*/empty_tuple, varnames,
/*freevars=*/empty_tuple, cellvars,
/*filename=*/empty_str, /*name=*/empty_str,
/*firstlineno=*/0, /*lnotab=*/empty_bytes));
ASSERT_TRUE(!code.cell2arg().isNoneType());
Object qualname(&scope, runtime_->newStrFromCStr("foo"));
Module module(&scope, findMainModule(runtime_));
Function foo(&scope,
runtime_->newFunctionWithCode(thread_, qualname, code, module));
Object obj(&scope, runtime_->newInt(99));
Tuple closure_tuple(&scope, runtime_->newTupleWith1(obj));
foo.setClosure(*closure_tuple);
Object argument(&scope, runtime_->newInt(3));
EXPECT_TRUE(isIntEqualsWord(Interpreter::call1(thread_, foo, argument), 3));
}
TEST_F(TrampolinesTest, InterpreterClosureUsesCellValue) {
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, R"(
def foo(arg):
def bar():
return arg * 3
arg = 5
return bar()
result = foo(-2)
)")
.isError());
Function foo(&scope, mainModuleAt(runtime_, "foo"));
ASSERT_EQ(foo.entry(), &interpreterTrampoline);
// Ensure that cellvar was populated.
Code code(&scope, foo.code());
ASSERT_TRUE(!code.cell2arg().isNoneType());
Tuple cellvars(&scope, code.cellvars());
ASSERT_EQ(cellvars.length(), 1);
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 15));
}
static RawObject makeFunctionWithPosOnlyArg(Thread* thread) {
// Create:
// def foo(a, /, b):
// return (a, b)
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object name(&scope, Runtime::internStrFromCStr(thread, "foo"));
const byte bytecode[] = {LOAD_FAST, 0, LOAD_FAST, 1,
BUILD_TUPLE, 2, RETURN_VALUE, 0};
Object a(&scope, Runtime::internStrFromCStr(thread, "a"));
Object b(&scope, Runtime::internStrFromCStr(thread, "b"));
Tuple varnames(&scope, runtime->newTupleWith2(a, b));
Bytes bc(&scope, runtime->newBytesWithAll(bytecode));
Object empty_tuple(&scope, runtime->emptyTuple());
Object empty_str(&scope, Str::empty());
Object empty_bytes(&scope, Bytes::empty());
Code code(&scope,
runtime->newCode(/*argcount=*/2, /*posonlyargcount=*/1,
/*kwonlyargcount=*/0, /*nlocals=*/2,
/*stacksize=*/2,
Code::Flags::kNewlocals | Code::Flags::kOptimized,
bc, /*consts=*/empty_tuple,
/*names=*/empty_tuple, varnames,
/*freevars=*/empty_tuple,
/*cellvars=*/empty_tuple,
/*filename=*/empty_str, name,
/*firstlineno=*/0, /*lnotab=*/empty_bytes));
Module module(&scope, findMainModule(runtime));
return runtime->newFunctionWithCode(thread, name, code, module);
}
TEST_F(TrampolinesTest, KeywordCallRejectsPositionalOnlyArgumentNames) {
HandleScope scope(thread_);
Function function(&scope, makeFunctionWithPosOnlyArg(thread_));
// `foo(a=2, b=4)`
thread_->stackPush(*function);
thread_->stackPush(runtime_->newInt(2));
thread_->stackPush(runtime_->newInt(4));
Object a(&scope, Runtime::internStrFromCStr(thread_, "a"));
Object b(&scope, Runtime::internStrFromCStr(thread_, "b"));
thread_->stackPush(runtime_->newTupleWith2(a, b));
Object result_obj(&scope, Interpreter::callKw(thread_, 2));
EXPECT_TRUE(raisedWithStr(
*result_obj, LayoutId::kTypeError,
"keyword argument specified for positional-only argument 'a'"));
}
TEST_F(TrampolinesTest, KeywordCallAcceptsNonPositionalOnlyArgumentNames) {
HandleScope scope(thread_);
Function function(&scope, makeFunctionWithPosOnlyArg(thread_));
// `foo(2, b=9)`
thread_->stackPush(*function);
thread_->stackPush(runtime_->newInt(2));
thread_->stackPush(runtime_->newInt(9));
Object b(&scope, Runtime::internStrFromCStr(thread_, "b"));
thread_->stackPush(runtime_->newTupleWith1(b));
Object result_obj(&scope, Interpreter::callKw(thread_, 2));
ASSERT_TRUE(result_obj.isTuple());
Tuple result(&scope, *result_obj);
ASSERT_EQ(result.length(), 2);
EXPECT_TRUE(isIntEqualsWord(result.at(0), 2));
EXPECT_TRUE(isIntEqualsWord(result.at(1), 9));
}
TEST_F(TrampolinesTest, KeywordCallWithPositionalOnlyArgumentsAndVarKeyArgs) {
HandleScope scope(thread_);
// Create:
// def foo(a, b=7, /, c=10, **kwargs):
// return (a, b, c, kwargs)
Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
const byte bytecode[] = {LOAD_FAST, 0, LOAD_FAST, 1, LOAD_FAST, 2,
LOAD_FAST, 3, BUILD_TUPLE, 4, RETURN_VALUE, 0};
Object a(&scope, Runtime::internStrFromCStr(thread_, "a"));
Object b(&scope, Runtime::internStrFromCStr(thread_, "b"));
Object c(&scope, Runtime::internStrFromCStr(thread_, "c"));
Object kwargs(&scope, Runtime::internStrFromCStr(thread_, "kwargs"));
Tuple varnames(&scope, runtime_->newTupleWith4(a, b, c, kwargs));
Bytes bc(&scope, runtime_->newBytesWithAll(bytecode));
Object empty_tuple(&scope, runtime_->emptyTuple());
Object empty_str(&scope, Str::empty());
Object empty_bytes(&scope, Bytes::empty());
Code code(&scope, runtime_->newCode(
/*argcount=*/3, /*posonlyargcount=*/2,
/*kwonlyargcount=*/0, /*nlocals=*/4,
/*stacksize=*/4,
Code::Flags::kNewlocals | Code::Flags::kOptimized |
Code::Flags::kVarkeyargs,
bc,
/*consts=*/empty_tuple,
/*names=*/empty_tuple, varnames,
/*freevars=*/empty_tuple,
/*cellvars=*/empty_tuple,
/*filename=*/empty_str, name,
/*firstlineno=*/0, /*lnotab=*/empty_bytes));
Module module(&scope, findMainModule(runtime_));
Function foo(&scope,
runtime_->newFunctionWithCode(thread_, name, code, module));
Object seven(&scope, runtime_->newInt(7));
Object ten(&scope, runtime_->newInt(10));
foo.setDefaults(runtime_->newTupleWith2(seven, ten));
// Call foo(1, c=13, b=5).
thread_->stackPush(*foo);
thread_->stackPush(runtime_->newInt(1));
thread_->stackPush(runtime_->newInt(13));
thread_->stackPush(runtime_->newInt(5));
thread_->stackPush(runtime_->newTupleWith2(c, b));
Object result_obj(&scope, Interpreter::callKw(thread_, 3));
// Expect a `(1, 7, 13, {'b': 5})` result.
ASSERT_TRUE(result_obj.isTuple());
Tuple result(&scope, *result_obj);
ASSERT_EQ(result.length(), 4);
EXPECT_TRUE(isIntEqualsWord(result.at(0), 1));
EXPECT_TRUE(isIntEqualsWord(result.at(1), 7));
EXPECT_TRUE(isIntEqualsWord(result.at(2), 13));
ASSERT_TRUE(result.at(3).isDict());
Dict result_dict(&scope, result.at(3));
EXPECT_EQ(result_dict.numItems(), 1);
Object b_name(&scope, Runtime::internStrFromCStr(thread_, "b"));
EXPECT_TRUE(isIntEqualsWord(dictAtByStr(thread_, result_dict, b_name), 5));
}
TEST_F(TrampolinesTest, ExplodeCallWithBadKeywordFails) {
EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, R"(
def take_kwargs(a): pass
kwargs = {12: 34}
take_kwargs(**kwargs)
)"),
LayoutId::kTypeError, "keywords must be strings"));
}
TEST_F(TrampolinesTest, ExplodeCallWithZeroKeywords) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
def foo(a=10): return a
result = foo(**{})
)")
.isError());
HandleScope scope(thread_);
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_EQ(result, SmallInt::fromWord(10));
}
static ALIGN_16 RawObject numArgs(Thread* thread, Arguments) {
return SmallInt::fromWord(thread->currentFrame()->function().totalArgs());
}
static void createAndPatchBuiltinNumArgs(Runtime* runtime) {
// Ensure we have a __main__ module.
ASSERT_FALSE(runFromCStr(runtime, "").isError());
// def dummy(first, second):
const char* parameter_names[] = {"first", "second"};
addBuiltin("dummy", numArgs, parameter_names, 0);
}
TEST_F(TrampolinesTest, BuiltinTrampolineExReceivesExArgs) {
createAndPatchBuiltinNumArgs(runtime_);
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, "result = dummy(*(1,2))").isError());
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_TRUE(isIntEqualsWord(*result, 2));
}
TEST_F(TrampolinesTest, BuiltinTrampolineExReceivesMixOfPositionalAndExArgs1) {
createAndPatchBuiltinNumArgs(runtime_);
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, "result = dummy(1, *(2,))").isError());
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_TRUE(isIntEqualsWord(*result, 2));
}
static void createAndPatchBuiltinNumArgsVariadic(Runtime* runtime) {
// Ensure we have a __main__ module.
ASSERT_FALSE(runFromCStr(runtime, "").isError());
// def dummy(*args):
const char* parameter_names[] = {"args"};
addBuiltin("dummy", numArgs, parameter_names, Code::Flags::kVarargs);
}
TEST_F(TrampolinesTest,
BuiltinTrampolineExReceivesOnePositionalArgAndTwoVariableArgs) {
createAndPatchBuiltinNumArgsVariadic(runtime_);
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, "result = dummy(1, *(2, 3))").isError());
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_TRUE(isIntEqualsWord(*result, 1));
}
static void createAndPatchBuiltinNumArgsArgsKwargs(Runtime* runtime) {
// Ensure we have a __main__ module.
ASSERT_FALSE(runFromCStr(runtime, "").isError());
// def dummy(*args, **kwargs):
const char* parameter_names[] = {"args", "kwargs"};
addBuiltin("dummy", numArgs, parameter_names,
Code::Flags::kVarargs | Code::Flags::kVarkeyargs);
}
TEST_F(TrampolinesTest,
BuiltinTrampolineExReceivesTwoPositionalOneVariableAndTwoKwArgs) {
createAndPatchBuiltinNumArgsArgsKwargs(runtime_);
HandleScope scope(thread_);
ASSERT_FALSE(
runFromCStr(runtime_,
"result = dummy(1, 2, *(3,), **{'foo': 1, 'bar': 2})")
.isError());
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_TRUE(isIntEqualsWord(*result, 2));
}
TEST_F(TrampolinesTest, BuiltinTrampolineExReceivesVarArgs) {
createAndPatchBuiltinNumArgs(runtime_);
HandleScope scope(thread_);
ASSERT_FALSE(
runFromCStr(runtime_, "result = dummy(*(1,), second=5)").isError());
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_TRUE(isIntEqualsWord(*result, 2));
}
TEST_F(TrampolinesTest, BuiltinTrampolineExWithTooFewArgsRaisesTypeError) {
createAndPatchBuiltinNumArgs(runtime_);
EXPECT_TRUE(
raisedWithStr(runFromCStr(runtime_, "dummy(*(1,))"), LayoutId::kTypeError,
"'dummy' takes min 2 positional arguments but 1 given"));
}
TEST_F(TrampolinesTest, BuiltinTrampolineExWithTooManyArgsRaisesTypeError) {
createAndPatchBuiltinNumArgs(runtime_);
EXPECT_TRUE(raisedWithStr(
runFromCStr(runtime_, "dummy(*(1,2,3,4,5))"), LayoutId::kTypeError,
"'dummy' takes max 2 positional arguments but 5 given"));
}
TEST_F(TrampolinesTest, CallFunctionExWithNamedArgAndExplodeKwargs) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
def f(description, conflict_handler):
return [description, conflict_handler]
result = f(description="foo", **{"conflict_handler": "conflict_handler value"})
)")
.isError());
HandleScope scope(thread_);
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_PYLIST_EQ(result, {"foo", "conflict_handler value"});
}
TEST_F(TrampolinesTest, CallFunctionExWithExplodeKwargsStrSubclassAlwaysEq) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
class C(str):
def __eq__(self, other):
return True
__hash__ = str.__hash__
def f(param):
return param
actual = C("foo")
result = f(**{actual: 5})
)")
.isError());
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 5));
}
TEST_F(TrampolinesTest,
CallFunctionExWithExplodeKwargsStrSubclassReturnNonBool) {
EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, R"(
class D:
def __bool__(self):
raise UserWarning('foo')
class C(str):
def __eq__(self, other):
return D()
__hash__ = str.__hash__
def f(param):
return param
actual = C("foo")
result = f(**{actual: 5})
)"),
LayoutId::kUserWarning, "foo"));
}
TEST_F(TrampolinesTest,
CallFunctionExWithExplodeKwargsStrSubclassRaiseException) {
EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, R"(
class C(str):
def __eq__(self, other):
raise UserWarning('foo')
__hash__ = str.__hash__
def f(param):
return param
actual = C("foo")
result = f(**{actual: 5})
)"),
LayoutId::kUserWarning, "foo"));
}
TEST_F(TrampolinesTest, CallFunctionExWithExplodeKwargsStrSubclassNotEq) {
EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, R"(
class C(str):
__hash__ = str.__hash__
def f(param):
return param
actual = C("foo")
result = f(**{actual: 5})
)"),
LayoutId::kTypeError,
"f() got an unexpected keyword argument 'foo'"));
}
TEST_F(TrampolinesTest, CallFunctionExWithExplodeKwargsStrSubclassNeverEq) {
EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, R"(
class C(str):
def __eq__(self, other):
return False
__hash__ = str.__hash__
def f(param):
return param
actual = C("param")
result = f(**{actual: 5})
)"),
LayoutId::kTypeError,
"f() got an unexpected keyword argument 'param'"));
}
TEST_F(TrampolinesTest, CallFunctionWithParameterInVarnames) {
EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, R"(
def add_argument(*args, **kwargs):
action = action_class(**kwargs)
def init():
add_argument(action='help')
init()
)"),
LayoutId::kNameError,
"name 'action_class' is not defined"));
}
TEST_F(TrampolinesTest, CallFunctionWithParameterInVarargname) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
def test(*args, **kwargs):
return kwargs['args']
result = test(args=5)
)")
.isError());
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 5));
}
TEST_F(TrampolinesTest, CallFunctionWithPositionalArgAndParameterInVarargname) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
def test(pos, *args, **kwargs):
return kwargs['args']
result = test(1, args=5)
)")
.isError());
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 5));
}
} // namespace testing
} // namespace py