runtime/thread-test.cpp (1,726 lines of code) (raw):
// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
#include "thread.h"
#include <memory>
#include "gtest/gtest.h"
#include "builtins-module.h"
#include "bytecode.h"
#include "dict-builtins.h"
#include "frame.h"
#include "globals.h"
#include "interpreter.h"
#include "marshal.h"
#include "module-builtins.h"
#include "modules.h"
#include "runtime.h"
#include "test-utils.h"
#include "type-builtins.h"
namespace py {
namespace testing {
using BuildSlice = RuntimeFixture;
using BuildString = RuntimeFixture;
using FormatTest = RuntimeFixture;
using ListAppendTest = RuntimeFixture;
using ListInsertTest = RuntimeFixture;
using ListIterTest = RuntimeFixture;
using ThreadTest = RuntimeFixture;
using UnpackList = RuntimeFixture;
using UnpackSeq = RuntimeFixture;
TEST_F(ThreadTest, CheckMainThreadRuntime) {
ASSERT_EQ(thread_->runtime(), runtime_);
}
TEST_F(ThreadTest, RunEmptyFunction) {
HandleScope scope(thread_);
Object none(&scope, NoneType::object());
Tuple consts(&scope, runtime_->newTupleWith1(none));
const byte bytecode[] = {LOAD_CONST, 0, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytesConsts(bytecode, consts));
Thread thread2(runtime_, 1 * kKiB);
runtime_->interpreter()->setupThread(&thread2);
Thread::setCurrentThread(&thread2);
EXPECT_TRUE(runCode(code).isNoneType());
// Avoid assertion in runtime destructor.
Thread::setCurrentThread(thread_);
}
TEST_F(ThreadTest, ModuleBodyCallsHelloWorldFunction) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
def hello():
return 'hello, world'
result = hello()
)")
.isError());
EXPECT_TRUE(
isStrEqualsCStr(mainModuleAt(runtime_, "result"), "hello, world"));
}
TEST_F(ThreadTest, DunderCallInstance) {
HandleScope scope(thread_);
const char* src = R"(
class C:
def __init__(self, x, y):
self.value = x + y
def __call__(self, y):
return self.value * y
c = C(10, 2)
g = c(3)
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
Object global(&scope, mainModuleAt(runtime_, "g"));
EXPECT_TRUE(isIntEqualsWord(*global, 36));
}
TEST_F(ThreadTest, DunderCallInstanceWithDescriptor) {
HandleScope scope(thread_);
const char* src = R"(
result = None
def stage2(x):
global result
result = x
class Stage1:
def __get__(self, instance, owner):
return stage2
class Stage0:
__call__ = Stage1()
c = Stage0()
c(1111)
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_TRUE(isIntEqualsWord(*result, 1111));
}
TEST_F(ThreadTest, DunderCallInstanceKw) {
HandleScope scope(thread_);
const char* src = R"(
class C:
def __init__(self):
self.value = None
def __call__(self, y):
return y
c = C()
result = c(y=3)
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_TRUE(isIntEqualsWord(*result, 3));
}
TEST_F(ThreadTest, DunderCallInstanceSplatArgs) {
HandleScope scope(thread_);
const char* src = R"(
class C:
def __call__(self, y):
return y
c = C()
args = (3,)
result = c(*args)
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_TRUE(isIntEqualsWord(*result, 3));
}
TEST_F(ThreadTest, DunderCallInstanceSplatKw) {
HandleScope scope(thread_);
const char* src = R"(
class C:
def __call__(self, y):
return y
c = C()
kwargs = {'y': 3}
result = c(**kwargs)
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_TRUE(isIntEqualsWord(*result, 3));
}
TEST_F(ThreadTest, DunderCallInstanceSplatArgsAndKw) {
HandleScope scope(thread_);
const char* src = R"(
result_x = None
result_y = None
class C:
def __call__(self, x, y):
global result_x
global result_y
result_x = x
result_y = y
c = C()
args = (1,)
kwargs = {'y': 3}
c(*args, **kwargs)
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
Object result_x(&scope, mainModuleAt(runtime_, "result_x"));
EXPECT_TRUE(isIntEqualsWord(*result_x, 1));
Object result_y(&scope, mainModuleAt(runtime_, "result_y"));
EXPECT_TRUE(isIntEqualsWord(*result_y, 3));
}
TEST_F(ThreadTest, OverlappingFrames) {
HandleScope scope(thread_);
// Push a frame for a code object with space for 3 items on the value stack
Object name(&scope, Str::empty());
Code caller_code(&scope, newCodeWithBytes(View<byte>(nullptr, 0)));
caller_code.setStacksize(3);
Module module(&scope, findMainModule(runtime_));
Function caller(&scope, runtime_->newFunctionWithCode(thread_, name,
caller_code, module));
thread_->stackPush(*caller);
thread_->pushCallFrame(*caller);
// Push a frame for a code object that expects 3 arguments and needs space
// for 3 local variables
Locals locals;
locals.argcount = 3;
Tuple consts(&scope, runtime_->emptyTuple());
Tuple names(&scope, runtime_->emptyTuple());
Code code(&scope, newCodeWithBytesConstsNamesLocals(View<byte>(nullptr, 0),
consts, names, &locals));
Function function(&scope,
runtime_->newFunctionWithCode(thread_, name, code, module));
// Push args on the stack in the sequence generated by CPython
thread_->stackPush(*function);
thread_->stackPush(runtime_->newInt(1111));
thread_->stackPush(runtime_->newInt(2222));
thread_->stackPush(runtime_->newInt(3333));
Frame* frame = thread_->pushCallFrame(*function);
// Make sure we can read the args from the frame
EXPECT_TRUE(isIntEqualsWord(frame->local(0), 1111));
EXPECT_TRUE(isIntEqualsWord(frame->local(1), 2222));
EXPECT_TRUE(isIntEqualsWord(frame->local(2), 3333));
}
TEST_F(ThreadTest, EncodeTryBlock) {
TryBlock block(TryBlock::kExceptHandler, 200, 300);
EXPECT_EQ(block.kind(), TryBlock::kExceptHandler);
EXPECT_EQ(block.handler(), 200);
EXPECT_EQ(block.level(), 300);
TryBlock decoded(block.asSmallInt());
EXPECT_EQ(decoded.kind(), block.kind());
EXPECT_EQ(decoded.handler(), block.handler());
EXPECT_EQ(decoded.level(), block.level());
}
TEST_F(ThreadTest, PushPopFrame) {
HandleScope scope(thread_);
Tuple consts(&scope, runtime_->emptyTuple());
Tuple names(&scope, runtime_->emptyTuple());
Locals locals;
locals.varcount = 2;
Code code(&scope, newCodeWithBytesConstsNamesLocals(View<byte>(nullptr, 0),
consts, names, &locals));
Module module(&scope, findMainModule(runtime_));
Object name(&scope, Str::empty());
Function function(&scope,
runtime_->newFunctionWithCode(thread_, name, code, module));
RawObject* prev_sp = thread_->stackPointer();
thread_->stackPush(*function);
Frame* frame = thread_->pushCallFrame(*function);
// Verify frame invariants post-push
EXPECT_TRUE(frame->previousFrame()->isSentinel());
EXPECT_EQ(thread_->stackPointer(), reinterpret_cast<RawObject*>(frame));
EXPECT_EQ(thread_->valueStackBase(), thread_->stackPointer());
// Make sure we restore the thread's stack pointer back to its previous
// location
thread_->popFrame();
EXPECT_EQ(thread_->stackPointer(), prev_sp);
}
TEST_F(ThreadTest, PushFrameWithNoCellVars) {
HandleScope scope(thread_);
Code code(&scope, newCodeWithBytes(View<byte>(nullptr, 0)));
Module module(&scope, findMainModule(runtime_));
Object name(&scope, Str::empty());
Function function(&scope,
runtime_->newFunctionWithCode(thread_, name, code, module));
thread_->stackPush(*function);
byte* prev_sp = reinterpret_cast<byte*>(thread_->stackPointer());
thread_->pushCallFrame(*function);
EXPECT_EQ(reinterpret_cast<byte*>(thread_->stackPointer()),
prev_sp - Frame::kSize);
}
TEST_F(ThreadTest, PushFrameWithNoFreeVars) {
HandleScope scope(thread_);
Code code(&scope, newCodeWithBytes(View<byte>(nullptr, 0)));
Module module(&scope, findMainModule(runtime_));
Object name(&scope, Str::empty());
Function function(&scope,
runtime_->newFunctionWithCode(thread_, name, code, module));
thread_->stackPush(*function);
byte* prev_sp = reinterpret_cast<byte*>(thread_->stackPointer());
thread_->pushCallFrame(*function);
EXPECT_EQ(reinterpret_cast<byte*>(thread_->stackPointer()),
prev_sp - Frame::kSize);
}
TEST_F(ThreadTest, ManipulateValueStack) {
// Push 3 items on the value stack
RawObject* sp = thread_->stackPointer();
*--sp = SmallInt::fromWord(1111);
*--sp = SmallInt::fromWord(2222);
*--sp = SmallInt::fromWord(3333);
thread_->setStackPointer(sp);
ASSERT_EQ(thread_->stackPointer(), sp);
// Verify the value stack is laid out as we expect
EXPECT_TRUE(isIntEqualsWord(thread_->stackPeek(0), 3333));
EXPECT_TRUE(isIntEqualsWord(thread_->stackPeek(1), 2222));
EXPECT_TRUE(isIntEqualsWord(thread_->stackPeek(2), 1111));
// Pop 2 items off the stack and check the stack is still as we expect
thread_->setStackPointer(sp + 2);
EXPECT_TRUE(isIntEqualsWord(thread_->stackPeek(0), 1111));
}
TEST_F(ThreadTest, ManipulateBlockStack) {
Frame* frame = thread_->currentFrame();
TryBlock pushed1(TryBlock::kFinally, 100, 10);
frame->blockStackPush(pushed1);
TryBlock pushed2(TryBlock::kExceptHandler, 200, 20);
frame->blockStackPush(pushed2);
TryBlock popped2 = frame->blockStackPop();
EXPECT_EQ(popped2.kind(), pushed2.kind());
EXPECT_EQ(popped2.handler(), pushed2.handler());
EXPECT_EQ(popped2.level(), pushed2.level());
TryBlock popped1 = frame->blockStackPop();
EXPECT_EQ(popped1.kind(), pushed1.kind());
EXPECT_EQ(popped1.handler(), pushed1.handler());
EXPECT_EQ(popped1.level(), pushed1.level());
}
TEST_F(ThreadTest, CallFunction) {
HandleScope scope(thread_);
// Build the code object for the following function
//
// def noop(arg0, arg1):
// return 2222
//
Locals locals;
locals.argcount = 2;
Object obj(&scope, runtime_->newInt(2222));
Tuple callee_consts(&scope, runtime_->newTupleWith1(obj));
Tuple names(&scope, runtime_->emptyTuple());
const byte callee_bytecode[] = {LOAD_CONST, 0, RETURN_VALUE, 0};
Code callee_code(&scope, newCodeWithBytesConstsNamesLocals(
callee_bytecode, callee_consts, names, &locals));
// Create the function object and bind it to the code object
Object qualname(&scope, Str::empty());
Module module(&scope, findMainModule(runtime_));
Function callee(&scope, runtime_->newFunctionWithCode(thread_, qualname,
callee_code, module));
// Build a code object to call the function defined above
Object obj1(&scope, SmallInt::fromWord(1111));
Object obj2(&scope, SmallInt::fromWord(2222));
Tuple caller_consts(&scope, runtime_->newTupleWith3(callee, obj1, obj2));
const byte caller_bytecode[] = {LOAD_CONST, 0, LOAD_CONST, 1,
LOAD_CONST, 2, CALL_FUNCTION, 2,
RETURN_VALUE, 0};
Code caller_code(&scope,
newCodeWithBytesConsts(caller_bytecode, caller_consts));
// Execute the caller and make sure we get back the expected result
EXPECT_TRUE(isIntEqualsWord(runCode(caller_code), 2222));
}
TEST_F(ThreadTest, ExtendedArg) {
HandleScope scope(thread_);
const word num_consts = 258;
const byte bytecode[] = {EXTENDED_ARG, 1, LOAD_CONST, 1, RETURN_VALUE, 0};
MutableTuple constants(&scope, runtime_->newMutableTuple(num_consts));
auto zero = SmallInt::fromWord(0);
auto non_zero = SmallInt::fromWord(0xDEADBEEF);
for (word i = 0; i < num_consts - 2; i++) {
constants.atPut(i, zero);
}
constants.atPut(num_consts - 1, non_zero);
Tuple consts(&scope, constants.becomeImmutable());
Code code(&scope, newCodeWithBytesConsts(bytecode, consts));
EXPECT_TRUE(isIntEqualsWord(runCode(code), 0xDEADBEEF));
}
TEST_F(ThreadTest, ExecuteDupTop) {
HandleScope scope(thread_);
Object obj(&scope, SmallInt::fromWord(1111));
Tuple consts(&scope, runtime_->newTupleWith1(obj));
const byte bytecode[] = {LOAD_CONST, 0, DUP_TOP, 0, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytesConsts(bytecode, consts));
EXPECT_TRUE(isIntEqualsWord(runCode(code), 1111));
}
TEST_F(ThreadTest, ExecuteDupTopTwo) {
HandleScope scope(thread_);
Object obj1(&scope, SmallInt::fromWord(1111));
Object obj2(&scope, SmallInt::fromWord(2222));
Tuple consts(&scope, runtime_->newTupleWith2(obj1, obj2));
const byte bytecode[] = {LOAD_CONST, 0, LOAD_CONST, 1,
DUP_TOP_TWO, 0, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytesConsts(bytecode, consts));
EXPECT_TRUE(isIntEqualsWord(runCode(code), 2222));
}
TEST_F(ThreadTest, ExecuteRotTwo) {
HandleScope scope(thread_);
Object obj1(&scope, SmallInt::fromWord(1111));
Object obj2(&scope, SmallInt::fromWord(2222));
Tuple consts(&scope, runtime_->newTupleWith2(obj1, obj2));
const byte bytecode[] = {LOAD_CONST, 0, LOAD_CONST, 1,
ROT_TWO, 0, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytesConsts(bytecode, consts));
EXPECT_TRUE(isIntEqualsWord(runCode(code), 1111));
}
TEST_F(ThreadTest, ExecuteRotThree) {
HandleScope scope(thread_);
Object obj1(&scope, SmallInt::fromWord(1111));
Object obj2(&scope, SmallInt::fromWord(2222));
Object obj3(&scope, SmallInt::fromWord(3333));
Tuple consts(&scope, runtime_->newTupleWith3(obj1, obj2, obj3));
const byte bytecode[] = {LOAD_CONST, 0, LOAD_CONST, 1, LOAD_CONST, 2,
ROT_THREE, 0, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytesConsts(bytecode, consts));
EXPECT_TRUE(isIntEqualsWord(runCode(code), 2222));
}
TEST_F(ThreadTest, ExecuteRotFour) {
HandleScope scope(thread_);
Object obj1(&scope, SmallInt::fromWord(1111));
Object obj2(&scope, SmallInt::fromWord(2222));
Object obj3(&scope, SmallInt::fromWord(3333));
Object obj4(&scope, SmallInt::fromWord(4444));
Tuple consts(&scope, runtime_->newTupleWith4(obj1, obj2, obj3, obj4));
const byte bytecode[] = {LOAD_CONST, 0, LOAD_CONST, 1, LOAD_CONST, 2,
LOAD_CONST, 3, ROT_FOUR, 0, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytesConsts(bytecode, consts));
EXPECT_TRUE(isIntEqualsWord(runCode(code), 3333));
}
TEST_F(ThreadTest, ExecuteJumpAbsolute) {
HandleScope scope(thread_);
Object obj1(&scope, SmallInt::fromWord(1111));
Object obj2(&scope, SmallInt::fromWord(2222));
Tuple consts(&scope, runtime_->newTupleWith2(obj1, obj2));
const byte bytecode[] = {JUMP_ABSOLUTE, 4, LOAD_CONST, 0,
LOAD_CONST, 1, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytesConsts(bytecode, consts));
EXPECT_TRUE(isIntEqualsWord(runCode(code), 2222));
}
TEST_F(ThreadTest, ExecuteJumpForward) {
HandleScope scope(thread_);
Object obj1(&scope, SmallInt::fromWord(1111));
Object obj2(&scope, SmallInt::fromWord(2222));
Tuple consts(&scope, runtime_->newTupleWith2(obj1, obj2));
const byte bytecode[] = {JUMP_FORWARD, 2, LOAD_CONST, 0,
LOAD_CONST, 1, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytesConsts(bytecode, consts));
EXPECT_TRUE(isIntEqualsWord(runCode(code), 2222));
}
TEST_F(ThreadTest, ExecuteStoreLoadFast) {
HandleScope scope(thread_);
Object obj(&scope, SmallInt::fromWord(1111));
Tuple consts(&scope, runtime_->newTupleWith1(obj));
Tuple names(&scope, runtime_->emptyTuple());
Locals locals;
locals.varcount = 2;
const byte bytecode[] = {LOAD_CONST, 0, STORE_FAST, 1,
LOAD_FAST, 1, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytesConstsNamesLocals(bytecode, consts, names,
&locals));
EXPECT_TRUE(isIntEqualsWord(runCode(code), 1111));
}
TEST_F(ThreadTest, LoadGlobal) {
HandleScope scope(thread_);
Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
Tuple names(&scope, runtime_->newTupleWith1(name));
Tuple consts(&scope, runtime_->emptyTuple());
const byte bytecode[] = {LOAD_GLOBAL, 0, RETURN_VALUE, 0};
Code code(&scope,
newCodeWithBytesConstsNamesFlags(bytecode, consts, names, 0));
Module module(&scope, findMainModule(runtime_));
Object value(&scope, runtime_->newInt(1234));
moduleAtPut(thread_, module, name, value);
Object none(&scope, NoneType::object());
EXPECT_TRUE(isIntEqualsWord(thread_->exec(code, module, none), 1234));
}
TEST_F(ThreadTest, StoreGlobalCreateValueCell) {
HandleScope scope(thread_);
Object obj(&scope, SmallInt::fromWord(42));
Tuple consts(&scope, runtime_->newTupleWith1(obj));
Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
Tuple names(&scope, runtime_->newTupleWith1(name));
const byte bytecode[] = {LOAD_CONST, 0, STORE_GLOBAL, 0,
LOAD_CONST, 0, RETURN_VALUE, 0};
Code code(&scope,
newCodeWithBytesConstsNamesFlags(bytecode, consts, names, 0));
Module module(&scope, findMainModule(runtime_));
Object none(&scope, NoneType::object());
EXPECT_TRUE(isIntEqualsWord(thread_->exec(code, module, none), 42));
EXPECT_TRUE(isIntEqualsWord(moduleAt(module, name), 42));
}
TEST_F(ThreadTest, StoreGlobalReuseValueCell) {
HandleScope scope(thread_);
Object obj(&scope, SmallInt::fromWord(42));
Tuple consts(&scope, runtime_->newTupleWith1(obj));
Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
Tuple names(&scope, runtime_->newTupleWith1(name));
const byte bytecode[] = {LOAD_CONST, 0, STORE_GLOBAL, 0,
LOAD_CONST, 0, RETURN_VALUE, 0};
Code code(&scope,
newCodeWithBytesConstsNamesFlags(bytecode, consts, names, 0));
Module module(&scope, findMainModule(runtime_));
Object value(&scope, runtime_->newInt(99));
moduleAtPut(thread_, module, name, value);
Object none(&scope, NoneType::object());
EXPECT_TRUE(isIntEqualsWord(thread_->exec(code, module, none), 42));
EXPECT_TRUE(isIntEqualsWord(moduleAt(module, name), 42));
}
TEST_F(ThreadTest, LoadNameInModuleBodyFromBuiltins) {
HandleScope scope(thread_);
Tuple consts(&scope, runtime_->emptyTuple());
Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
Tuple names(&scope, runtime_->newTupleWith1(name));
const byte bytecode[] = {LOAD_NAME, 0, RETURN_VALUE, 0};
Code code(&scope,
newCodeWithBytesConstsNamesFlags(bytecode, consts, names, 0));
Object builtins_name(&scope, runtime_->symbols()->at(ID(builtins)));
Module builtins(&scope, runtime_->newModule(builtins_name));
Module module(&scope, findMainModule(runtime_));
moduleAtPutById(thread_, module, ID(__builtins__), builtins);
Object value(&scope, runtime_->newInt(123));
moduleAtPut(thread_, builtins, name, value);
Dict locals(&scope, runtime_->newDict());
EXPECT_TRUE(isIntEqualsWord(thread_->exec(code, module, locals), 123));
}
TEST_F(ThreadTest, LoadNameFromGlobals) {
HandleScope scope(thread_);
Tuple consts(&scope, runtime_->emptyTuple());
Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
Tuple names(&scope, runtime_->newTupleWith1(name));
const byte bytecode[] = {LOAD_NAME, 0, RETURN_VALUE, 0};
Code code(&scope,
newCodeWithBytesConstsNamesFlags(bytecode, consts, names, 0));
Module module(&scope, findMainModule(runtime_));
Object value(&scope, runtime_->newInt(321));
moduleAtPut(thread_, module, name, value);
Dict locals(&scope, runtime_->newDict());
EXPECT_TRUE(isIntEqualsWord(thread_->exec(code, module, locals), 321));
}
TEST_F(ThreadTest, LoadNameFromLocals) {
HandleScope scope(thread_);
Tuple consts(&scope, runtime_->emptyTuple());
Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
Tuple names(&scope, runtime_->newTupleWith1(name));
const byte bytecode[] = {LOAD_NAME, 0, RETURN_VALUE, 0};
Code code(&scope,
newCodeWithBytesConstsNamesFlags(bytecode, consts, names, 0));
Module module(&scope, findMainModule(runtime_));
Object globals_value(&scope, runtime_->newInt(456));
moduleAtPut(thread_, module, name, globals_value);
Dict locals(&scope, runtime_->newDict());
Object locals_value(&scope, runtime_->newInt(654));
dictAtPutByStr(thread_, locals, name, locals_value);
EXPECT_TRUE(isIntEqualsWord(thread_->exec(code, module, locals), 654));
}
TEST_F(ThreadTest, MakeFunction) {
HandleScope scope(thread_);
Code func_code(&scope, newCodeWithBytes(View<byte>(nullptr, 0)));
func_code.setName(runtime_->newStrFromCStr("hello"));
Object obj1(&scope, runtime_->newStrFromCStr("hello_qualname"));
Object obj2(&scope, NoneType::object());
Tuple consts(&scope, runtime_->newTupleWith3(func_code, obj1, obj2));
Object name(&scope, runtime_->newStrFromCStr("hello"));
Tuple names(&scope, runtime_->newTupleWith1(name));
const byte bytecode[] = {LOAD_CONST, 0, LOAD_CONST, 1, MAKE_FUNCTION, 0,
STORE_NAME, 0, LOAD_CONST, 2, RETURN_VALUE, 0};
Code code(&scope,
newCodeWithBytesConstsNamesFlags(bytecode, consts, names, 0));
Module module(&scope, findMainModule(runtime_));
Dict locals(&scope, runtime_->newDict());
ASSERT_TRUE(thread_->exec(code, module, locals).isNoneType());
Object function_obj(&scope, dictAtByStr(thread_, locals, name));
ASSERT_TRUE(function_obj.isFunction());
Function function(&scope, *function_obj);
EXPECT_EQ(function.code(), func_code);
EXPECT_TRUE(isStrEqualsCStr(function.name(), "hello"));
EXPECT_TRUE(isStrEqualsCStr(function.qualname(), "hello_qualname"));
EXPECT_EQ(function.entry(), &interpreterTrampoline);
}
TEST_F(ThreadTest, BuildList) {
HandleScope scope(thread_);
Object obj1(&scope, SmallInt::fromWord(111));
Object obj2(&scope, runtime_->newStrFromCStr("qqq"));
Object obj3(&scope, NoneType::object());
Tuple consts(&scope, runtime_->newTupleWith3(obj1, obj2, obj3));
const byte bytecode[] = {LOAD_CONST, 0, LOAD_CONST, 1, LOAD_CONST, 2,
BUILD_LIST, 3, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytesConsts(bytecode, consts));
Object result_obj(&scope, runCode(code));
ASSERT_TRUE(result_obj.isList());
List list(&scope, *result_obj);
EXPECT_EQ(list.numItems(), 3);
EXPECT_TRUE(isIntEqualsWord(list.at(0), 111));
EXPECT_TRUE(isStrEqualsCStr(list.at(1), "qqq"));
EXPECT_TRUE(list.at(2).isNoneType());
}
TEST_F(ThreadTest, BuildSetEmpty) {
HandleScope scope(thread_);
const byte bytecode[] = {BUILD_SET, 0, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytes(bytecode));
Object result_obj(&scope, runCode(code));
ASSERT_TRUE(result_obj.isSet());
Set result(&scope, *result_obj);
EXPECT_EQ(result.numItems(), 0);
}
TEST_F(ThreadTest, BuildSetWithOneItem) {
HandleScope scope(thread_);
Object obj(&scope, runtime_->newInt(111));
Tuple consts(&scope, runtime_->newTupleWith2(obj, obj)); // dup
const byte bytecode[] = {LOAD_CONST, 0, LOAD_CONST, 1,
BUILD_SET, 2, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytesConsts(bytecode, consts));
Object result_obj(&scope, runCode(code));
ASSERT_TRUE(result_obj.isSet());
Set result(&scope, *result_obj);
EXPECT_EQ(result.numItems(), 1);
Object int_val(&scope, SmallInt::fromWord(111));
EXPECT_TRUE(setIncludes(thread_, result, int_val));
}
TEST_F(ThreadTest, BuildSet) {
HandleScope scope(thread_);
Object obj1(&scope, runtime_->newInt(111));
Object obj2(&scope, runtime_->newStrFromCStr("qqq"));
Object obj3(&scope, NoneType::object());
Tuple consts(&scope, runtime_->newTupleWith4(obj1, obj1, obj2, obj3)); // dup
const byte bytecode[] = {LOAD_CONST, 0, LOAD_CONST, 1, LOAD_CONST, 2,
LOAD_CONST, 3, BUILD_SET, 4, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytesConsts(bytecode, consts));
Object result_obj(&scope, runCode(code));
ASSERT_TRUE(result_obj.isSet());
Set result(&scope, *result_obj);
EXPECT_EQ(result.numItems(), 3);
Object int_val(&scope, runtime_->newInt(111));
EXPECT_TRUE(setIncludes(thread_, result, int_val));
Object str(&scope, runtime_->newStrFromCStr("qqq"));
EXPECT_TRUE(setIncludes(thread_, result, str));
Object none(&scope, NoneType::object());
EXPECT_TRUE(setIncludes(thread_, result, none));
}
TEST_F(ThreadTest, PopJumpIfFalseWithTruthy) {
HandleScope scope(thread_);
Object obj1(&scope, Bool::trueObj());
Object obj2(&scope, SmallInt::fromWord(1111));
Object obj3(&scope, SmallInt::fromWord(2222));
Tuple consts(&scope, runtime_->newTupleWith3(obj1, obj2, obj3));
// Bytecode for the snippet:
// if x:
// return 1111
// return 2222
const byte bytecode[] = {LOAD_CONST, 0, POP_JUMP_IF_FALSE, 8,
LOAD_CONST, 1, RETURN_VALUE, 0,
LOAD_CONST, 2, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytesConsts(bytecode, consts));
EXPECT_TRUE(isIntEqualsWord(runCode(code), 1111));
}
TEST_F(ThreadTest, PopJumpIfFalseWithFalsey) {
HandleScope scope(thread_);
Object obj1(&scope, Bool::falseObj());
Object obj2(&scope, SmallInt::fromWord(1111));
Object obj3(&scope, SmallInt::fromWord(2222));
Tuple consts(&scope, runtime_->newTupleWith3(obj1, obj2, obj3));
// Bytecode for the snippet:
// if x:
// return 1111
// return 2222
const byte bytecode[] = {LOAD_CONST, 0, POP_JUMP_IF_FALSE, 8,
LOAD_CONST, 1, RETURN_VALUE, 0,
LOAD_CONST, 2, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytesConsts(bytecode, consts));
EXPECT_TRUE(isIntEqualsWord(runCode(code), 2222));
}
TEST_F(ThreadTest, PopJumpIfTrueWithFalsey) {
HandleScope scope(thread_);
Object obj1(&scope, Bool::falseObj());
Object obj2(&scope, SmallInt::fromWord(1111));
Object obj3(&scope, SmallInt::fromWord(2222));
Tuple consts(&scope, runtime_->newTupleWith3(obj1, obj2, obj3));
// Bytecode for the snippet:
// if not x:
// return 1111
// return 2222
const byte bytecode[] = {LOAD_CONST, 0, POP_JUMP_IF_TRUE, 8,
LOAD_CONST, 1, RETURN_VALUE, 0,
LOAD_CONST, 2, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytesConsts(bytecode, consts));
EXPECT_TRUE(isIntEqualsWord(runCode(code), 1111));
}
TEST_F(ThreadTest, PopJumpIfTrueWithTruthy) {
HandleScope scope(thread_);
Object obj1(&scope, Bool::trueObj());
Object obj2(&scope, SmallInt::fromWord(1111));
Object obj3(&scope, SmallInt::fromWord(2222));
Tuple consts(&scope, runtime_->newTupleWith3(obj1, obj2, obj3));
// Bytecode for the snippet:
// if not x:
// return 1111
// return 2222
const byte bytecode[] = {LOAD_CONST, 0, POP_JUMP_IF_TRUE, 8,
LOAD_CONST, 1, RETURN_VALUE, 0,
LOAD_CONST, 2, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytesConsts(bytecode, consts));
EXPECT_TRUE(isIntEqualsWord(runCode(code), 2222));
}
TEST_F(ThreadTest, JumpIfFalseOrPopWithFalsey) {
HandleScope scope(thread_);
Object obj1(&scope, Bool::falseObj());
Object obj2(&scope, SmallInt::fromWord(1111));
Tuple consts(&scope, runtime_->newTupleWith2(obj1, obj2));
const byte bytecode[] = {LOAD_CONST, 0, JUMP_IF_FALSE_OR_POP, 6,
LOAD_CONST, 1, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytesConsts(bytecode, consts));
// If the condition is false, we should return the top of the stack, which is
// the condition itself
EXPECT_EQ(runCode(code), Bool::falseObj());
}
TEST_F(ThreadTest, JumpIfFalseOrPopWithTruthy) {
HandleScope scope(thread_);
Object obj1(&scope, Bool::trueObj());
Object obj2(&scope, SmallInt::fromWord(1111));
Tuple consts(&scope, runtime_->newTupleWith2(obj1, obj2));
const byte bytecode[] = {LOAD_CONST, 0, JUMP_IF_FALSE_OR_POP, 6,
LOAD_CONST, 1, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytesConsts(bytecode, consts));
// If the condition is true, we should pop the top of the stack (the
// condition) and continue execution. In our case that loads a const and
// returns it.
EXPECT_TRUE(isIntEqualsWord(runCode(code), 1111));
}
TEST_F(ThreadTest, JumpIfTrueOrPopWithTruthy) {
HandleScope scope(thread_);
Object obj1(&scope, Bool::trueObj());
Object obj2(&scope, SmallInt::fromWord(1111));
Tuple consts(&scope, runtime_->newTupleWith2(obj1, obj2));
const byte bytecode[] = {LOAD_CONST, 0, JUMP_IF_TRUE_OR_POP, 6,
LOAD_CONST, 1, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytesConsts(bytecode, consts));
// If the condition is true, we should return the top of the stack, which is
// the condition itself
EXPECT_EQ(runCode(code), Bool::trueObj());
}
TEST_F(ThreadTest, JumpIfTrueOrPopWithFalsey) {
HandleScope scope(thread_);
Object obj1(&scope, Bool::falseObj());
Object obj2(&scope, SmallInt::fromWord(1111));
Tuple consts(&scope, runtime_->newTupleWith2(obj1, obj2));
const byte bytecode[] = {LOAD_CONST, 0, JUMP_IF_TRUE_OR_POP, 6,
LOAD_CONST, 1, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytesConsts(bytecode, consts));
// If the condition is false, we should pop the top of the stack (the
// condition) and continue execution. In our case that loads a const and
// returns it.
EXPECT_TRUE(isIntEqualsWord(runCode(code), 1111));
}
TEST_F(ThreadTest, UnaryNotWithTruthy) {
HandleScope scope(thread_);
Object obj(&scope, Bool::trueObj());
Tuple consts(&scope, runtime_->newTupleWith1(obj));
// Bytecode for the snippet:
// return not x
const byte bytecode[] = {LOAD_CONST, 0, UNARY_NOT, 0, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytesConsts(bytecode, consts));
// If the condition is true, we should return false
EXPECT_EQ(runCode(code), Bool::falseObj());
}
TEST_F(ThreadTest, UnaryNotWithFalsey) {
HandleScope scope(thread_);
Object obj(&scope, Bool::falseObj());
Tuple consts(&scope, runtime_->newTupleWith1(obj));
// Bytecode for the snippet:
// return not x
const byte bytecode[] = {LOAD_CONST, 0, UNARY_NOT, 0, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytesConsts(bytecode, consts));
// If the condition is false, we should return true
EXPECT_EQ(runCode(code), Bool::trueObj());
}
TEST_F(ThreadTest, LoadBuildTypeEmptyType) {
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, R"(
class C:
pass
)")
.isError());
Type cls(&scope, mainModuleAt(runtime_, "C"));
ASSERT_TRUE(cls.name().isSmallStr());
EXPECT_EQ(cls.name(), SmallStr::fromCStr("C"));
Tuple mro(&scope, cls.mro());
EXPECT_EQ(mro.length(), 2);
EXPECT_EQ(mro.at(0), *cls);
EXPECT_EQ(mro.at(1), runtime_->typeAt(LayoutId::kObject));
}
TEST_F(ThreadTest, LoadBuildTypeTypeWithInit) {
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, R"(
class C:
def __init__(self):
pass
)")
.isError());
Module mod(&scope, findMainModule(runtime_));
ASSERT_TRUE(mod.isModule());
Str cls_name(&scope, Runtime::internStrFromCStr(thread_, "C"));
Object value(&scope, moduleAt(mod, cls_name));
ASSERT_TRUE(value.isType());
Type cls(&scope, *value);
// Check class MRO
Tuple mro(&scope, cls.mro());
EXPECT_EQ(mro.length(), 2);
EXPECT_EQ(mro.at(0), *cls);
EXPECT_EQ(mro.at(1), runtime_->typeAt(LayoutId::kObject));
// Check class name
ASSERT_TRUE(cls.name().isSmallStr());
EXPECT_EQ(cls.name(), SmallStr::fromCStr("C"));
// Check for the __init__ method name in the dict
value = typeAtById(thread_, cls, ID(__init__));
ASSERT_FALSE(value.isError());
EXPECT_TRUE(value.isFunction());
}
static ALIGN_16 RawObject nativeExceptionTest(Thread* thread, Arguments) {
HandleScope scope(thread);
Str msg(&scope,
Str::cast(thread->runtime()->newStrFromCStr("test exception")));
return thread->raise(LayoutId::kRuntimeError, *msg);
}
TEST_F(ThreadTest, NativeExceptions) {
HandleScope scope(thread_);
Object name(&scope, runtime_->newStrFromCStr("fn"));
Object empty_tuple(&scope, runtime_->emptyTuple());
Code fn_code(&scope, runtime_->newBuiltinCode(
/*argcount=*/0, /*posonlyargcount=*/0,
/*kwonlyargcount=*/0, /*flags=*/0,
nativeExceptionTest, empty_tuple, name));
Module module(&scope, findMainModule(runtime_));
Function fn(&scope,
runtime_->newFunctionWithCode(thread_, name, fn_code, module));
Tuple consts(&scope, runtime_->newTupleWith1(fn));
// Call the native fn and check that a pending exception is left in the
// Thread.
const byte bytecode[] = {LOAD_CONST, 0, CALL_FUNCTION, 0, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytesConsts(bytecode, consts));
EXPECT_TRUE(raised(runCode(code), LayoutId::kRuntimeError));
Object value(&scope, thread_->pendingExceptionValue());
ASSERT_TRUE(value.isStr());
Str str(&scope, *value);
EXPECT_TRUE(str.equalsCStr("test exception"));
}
TEST_F(ThreadTest, PendingStopIterationValueInspectsTuple) {
HandleScope scope(thread_);
Object obj1(&scope, runtime_->newInt(123));
Object obj2(&scope, runtime_->newInt(456));
Tuple tuple(&scope, runtime_->newTupleWith2(obj1, obj2));
thread_->raise(LayoutId::kStopIteration, *tuple);
ASSERT_TRUE(thread_->hasPendingStopIteration());
EXPECT_TRUE(isIntEqualsWord(thread_->pendingStopIterationValue(), 123));
}
TEST_F(ThreadTest,
RaiseStopIterationWithTupleReturnedByPendingStopIterationValue) {
HandleScope scope(thread_);
Object obj1(&scope, runtime_->newInt(123));
Object obj2(&scope, runtime_->newInt(456));
Tuple tuple(&scope, runtime_->newTupleWith2(obj1, obj2));
thread_->raiseStopIterationWithValue(tuple);
ASSERT_TRUE(thread_->hasPendingStopIteration());
EXPECT_EQ(thread_->pendingStopIterationValue(), tuple);
}
TEST_F(ThreadTest,
RaiseStopIterationWithStopIterationReturnedByPendingStopIterationValue) {
HandleScope scope(thread_);
Object obj1(&scope, runtime_->newInt(123));
Type stop_iteration_type(&scope, runtime_->typeAt(LayoutId::kStopIteration));
Object stop_iteration(&scope,
Interpreter::call1(thread_, stop_iteration_type, obj1));
thread_->raiseStopIterationWithValue(stop_iteration);
ASSERT_TRUE(thread_->hasPendingStopIteration());
EXPECT_EQ(thread_->pendingStopIterationValue(), stop_iteration);
}
TEST_F(ThreadTest,
RaiseStopIterationWithIntReturnedByPendingStopIterationValue) {
HandleScope scope(thread_);
Object obj1(&scope, runtime_->newInt(123));
thread_->raiseStopIterationWithValue(obj1);
ASSERT_TRUE(thread_->hasPendingStopIteration());
EXPECT_EQ(thread_->pendingStopIterationValue(), obj1);
}
TEST_F(ThreadTest, RaiseWithTypePreservesTraceback) {
HandleScope scope(thread_);
Layout layout(&scope, runtime_->layoutAt(LayoutId::kBaseException));
BaseException exc(&scope, runtime_->newInstance(layout));
Object tb(&scope, runtime_->newTraceback());
exc.setTraceback(*tb);
thread_->raiseWithType(runtime_->typeOf(*exc), *exc);
Object pending_exc_tb(&scope, thread_->pendingExceptionTraceback());
EXPECT_EQ(tb, pending_exc_tb);
}
// MRO tests
TEST_F(ThreadTest, LoadBuildTypeVerifyMro) {
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, R"(
class A: pass
class B: pass
class C(A,B): pass
)")
.isError());
Object a(&scope, mainModuleAt(runtime_, "A"));
Object b(&scope, mainModuleAt(runtime_, "B"));
Type c(&scope, mainModuleAt(runtime_, "C"));
Object object(&scope, moduleAtByCStr(runtime_, "builtins", "object"));
Tuple mro(&scope, c.mro());
ASSERT_EQ(mro.length(), 4);
EXPECT_EQ(mro.at(0), c);
EXPECT_EQ(mro.at(1), a);
EXPECT_EQ(mro.at(2), b);
EXPECT_EQ(mro.at(3), object);
}
TEST_F(ThreadTest, LoadBuildTypeVerifyMroInheritance) {
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, R"(
class A: pass
class B(A): pass
class C(B): pass
)")
.isError());
Object a(&scope, mainModuleAt(runtime_, "A"));
Object b(&scope, mainModuleAt(runtime_, "B"));
Type c(&scope, mainModuleAt(runtime_, "C"));
Object object(&scope, moduleAtByCStr(runtime_, "builtins", "object"));
Tuple mro(&scope, c.mro());
ASSERT_EQ(mro.length(), 4);
EXPECT_EQ(mro.at(0), c);
EXPECT_EQ(mro.at(1), b);
EXPECT_EQ(mro.at(2), a);
EXPECT_EQ(mro.at(3), object);
}
TEST_F(ThreadTest, LoadBuildTypeVerifyMroMultiInheritance) {
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, R"(
class A: pass
class B(A): pass
class C: pass
class D(B,C): pass
)")
.isError());
Object a(&scope, mainModuleAt(runtime_, "A"));
Object b(&scope, mainModuleAt(runtime_, "B"));
Object c(&scope, mainModuleAt(runtime_, "C"));
Type d(&scope, mainModuleAt(runtime_, "D"));
Object object(&scope, moduleAtByCStr(runtime_, "builtins", "object"));
Tuple mro(&scope, d.mro());
ASSERT_EQ(mro.length(), 5);
EXPECT_EQ(mro.at(0), d);
EXPECT_EQ(mro.at(1), b);
EXPECT_EQ(mro.at(2), a);
EXPECT_EQ(mro.at(3), c);
EXPECT_EQ(mro.at(4), object);
}
TEST_F(ThreadTest, LoadBuildTypeVerifyMroDiamond) {
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, R"(
class A: pass
class B(A): pass
class C(A): pass
class D(B,C): pass
)")
.isError());
Object a(&scope, mainModuleAt(runtime_, "A"));
Object b(&scope, mainModuleAt(runtime_, "B"));
Object c(&scope, mainModuleAt(runtime_, "C"));
Type d(&scope, mainModuleAt(runtime_, "D"));
Object object(&scope, moduleAtByCStr(runtime_, "builtins", "object"));
Tuple mro(&scope, d.mro());
ASSERT_EQ(mro.length(), 5);
EXPECT_EQ(mro.at(0), d);
EXPECT_EQ(mro.at(1), b);
EXPECT_EQ(mro.at(2), c);
EXPECT_EQ(mro.at(3), a);
EXPECT_EQ(mro.at(4), object);
}
TEST_F(ThreadTest, LoadBuildTypeVerifyMroError) {
const char* src = R"(
class A: pass
class B(A): pass
class C(A, B): pass
)";
EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, src), LayoutId::kTypeError,
"Cannot create a consistent method resolution "
"order (MRO) for bases A, B"));
}
// iteration
TEST_F(ThreadTest, RaiseVarargs) {
EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, "raise 1"),
LayoutId::kTypeError,
"exceptions must derive from BaseException"));
}
TEST_F(ThreadTest, InheritFromObject) {
HandleScope scope(thread_);
const char* src = R"(
class Foo(object):
pass
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
// Look up the class Foo
Object main_obj(&scope, findMainModule(runtime_));
ASSERT_TRUE(main_obj.isModule());
Object foo_obj(&scope, mainModuleAt(runtime_, "Foo"));
ASSERT_TRUE(foo_obj.isType());
Type foo(&scope, *foo_obj);
// Check that its MRO is itself and object
ASSERT_TRUE(foo.mro().isTuple());
Tuple mro(&scope, foo.mro());
ASSERT_EQ(mro.length(), 2);
EXPECT_EQ(mro.at(0), *foo);
EXPECT_EQ(mro.at(1), runtime_->typeAt(LayoutId::kObject));
}
// imports
TEST_F(ThreadTest, ImportTest) {
HandleScope scope(thread_);
Object module_src(&scope, runtime_->newStrFromCStr(R"(
def say_hello():
return "hello"
)"));
Object filename(&scope, runtime_->newStrFromCStr("<test string>"));
const char* main_src = R"(
import hello
result = hello.say_hello()
)";
// Pre-load the hello module so is cached.
Code code(&scope, compile(thread_, module_src, filename, ID(exec),
/*flags=*/0, /*optimize=*/0));
Object name(&scope, runtime_->newStrFromCStr("hello"));
ASSERT_FALSE(executeModuleFromCode(thread_, code, name).isError());
ASSERT_FALSE(runFromCStr(runtime_, main_src).isError());
EXPECT_TRUE(isStrEqualsCStr(mainModuleAt(runtime_, "result"), "hello"));
}
TEST_F(ThreadTest, FailedImportTest) {
const char* main_src = R"(
import hello
hello.say_hello()
)";
EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, main_src),
LayoutId::kModuleNotFoundError,
"No module named 'hello'"));
}
TEST_F(ThreadTest, ImportMissingAttributeTest) {
HandleScope scope(thread_);
Object module_src(&scope, runtime_->newStrFromCStr(R"(
def say_hello():
print("hello");
)"));
Object filename(&scope, runtime_->newStrFromCStr("<test string>"));
const char* main_src = R"(
import hello
hello.foo()
)";
// Pre-load the hello module so is cached.
Code code(&scope, compile(thread_, module_src, filename, ID(exec),
/*flags=*/0, /*optimize=*/0));
Object name(&scope, runtime_->newStrFromCStr("hello"));
ASSERT_FALSE(executeModuleFromCode(thread_, code, name).isError());
EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, main_src),
LayoutId::kAttributeError,
"module 'hello' has no attribute 'foo'"));
}
TEST_F(ThreadTest, ModuleSetAttrTest) {
HandleScope scope(thread_);
Object module_src(&scope, runtime_->newStrFromCStr(R"(
def say_hello():
return "hello"
)"));
Object filename(&scope, runtime_->newStrFromCStr("<test string>"));
const char* main_src = R"(
import hello
def goodbye():
return "goodbye"
hello.say_hello = goodbye
result = hello.say_hello()
)";
// Pre-load the hello module so is cached.
Code code(&scope, compile(thread_, module_src, filename, ID(exec),
/*flags=*/0, /*optimize=*/0));
Object name(&scope, runtime_->newStrFromCStr("hello"));
ASSERT_FALSE(executeModuleFromCode(thread_, code, name).isError());
ASSERT_FALSE(runFromCStr(runtime_, main_src).isError());
EXPECT_TRUE(isStrEqualsCStr(mainModuleAt(runtime_, "result"), "goodbye"));
}
TEST_F(ThreadTest, StoreFastStackEffect) {
const char* src = R"(
def printit(x, y, z):
return (x, y, z)
def test():
x = 1
y = 2
z = 3
return printit(x, y, z)
result = test()
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
HandleScope scope(thread_);
Object result(&scope, mainModuleAt(runtime_, "result"));
ASSERT_TRUE(result.isTuple());
Tuple tuple(&scope, *result);
ASSERT_EQ(tuple.length(), 3);
EXPECT_TRUE(isIntEqualsWord(tuple.at(0), 1));
EXPECT_TRUE(isIntEqualsWord(tuple.at(1), 2));
EXPECT_TRUE(isIntEqualsWord(tuple.at(2), 3));
}
TEST_F(ThreadTest, Closure) {
const char* src = R"(
result = []
def f():
a = 1
def g():
b = 2
def h():
result.append(b)
result.append(a)
h()
b = 3
h()
g()
f()
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
HandleScope scope(thread_);
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_PYLIST_EQ(result, {1, 2, 3});
}
TEST_F(ThreadTest, StackOverflowRaisesRecursionError) {
// TODO(T59724033): Not using binop doesn't raise RecursionError, which
// signifies that our recursion depth checking is problematic.
EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, R"(
def foo(x):
return foo(x + 1)
foo(1.0)
)"),
LayoutId::kRecursionError,
"maximum recursion depth exceeded"));
}
TEST_F(FormatTest, NoConvEmpty) {
const char* src = R"(
result = f''
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
EXPECT_TRUE(isStrEqualsCStr(mainModuleAt(runtime_, "result"), ""));
}
TEST_F(FormatTest, NoConvOneElement) {
const char* src = R"(
a = "hello"
result = f'a={a}'
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
EXPECT_TRUE(isStrEqualsCStr(mainModuleAt(runtime_, "result"), "a=hello"));
}
TEST_F(FormatTest, NoConvMultiElements) {
const char* src = R"(
a = "hello"
b = "world"
c = "python"
result = f'{a} {b} {c}'
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
EXPECT_TRUE(
isStrEqualsCStr(mainModuleAt(runtime_, "result"), "hello world python"));
}
TEST_F(FormatTest, NoConvMultiElementsLarge) {
const char* src = R"(
a = "Python"
b = "is"
c = "an interpreted high-level programming language for general-purpose programming."
result = f'{a} {b} {c}'
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
EXPECT_TRUE(isStrEqualsCStr(
mainModuleAt(runtime_, "result"),
"Python is an interpreted high-level programming language for "
"general-purpose programming."));
}
TEST_F(ThreadTest, BuildTupleUnpack) {
HandleScope scope(thread_);
const char* src = R"(
t = (*[0], *[1, 2], *[], *[3, 4, 5])
t1 = (*(0,), *(1, 2), *(), *(3, 4, 5))
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
Object t(&scope, mainModuleAt(runtime_, "t"));
EXPECT_TRUE(t.isTuple());
Tuple tuple_t(&scope, *t);
EXPECT_EQ(tuple_t.length(), 6);
for (word i = 0; i < tuple_t.length(); i++) {
EXPECT_TRUE(isIntEqualsWord(tuple_t.at(i), i));
}
Object t1(&scope, mainModuleAt(runtime_, "t1"));
EXPECT_TRUE(t1.isTuple());
Tuple tuple_t1(&scope, *t1);
EXPECT_EQ(tuple_t1.length(), 6);
EXPECT_TRUE(isIntEqualsWord(tuple_t1.at(0), 0));
EXPECT_TRUE(isIntEqualsWord(tuple_t1.at(1), 1));
EXPECT_TRUE(isIntEqualsWord(tuple_t1.at(2), 2));
EXPECT_TRUE(isIntEqualsWord(tuple_t1.at(3), 3));
EXPECT_TRUE(isIntEqualsWord(tuple_t1.at(4), 4));
EXPECT_TRUE(isIntEqualsWord(tuple_t1.at(5), 5));
}
TEST_F(ThreadTest, BuildListUnpack) {
HandleScope scope(thread_);
const char* src = R"(
l = [*[0], *[1, 2], *[], *[3, 4, 5]]
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
Object l(&scope, mainModuleAt(runtime_, "l"));
EXPECT_TRUE(l.isList());
List list_l(&scope, *l);
EXPECT_EQ(list_l.numItems(), 6);
EXPECT_TRUE(isIntEqualsWord(list_l.at(0), 0));
EXPECT_TRUE(isIntEqualsWord(list_l.at(1), 1));
EXPECT_TRUE(isIntEqualsWord(list_l.at(2), 2));
EXPECT_TRUE(isIntEqualsWord(list_l.at(3), 3));
EXPECT_TRUE(isIntEqualsWord(list_l.at(4), 4));
EXPECT_TRUE(isIntEqualsWord(list_l.at(5), 5));
}
TEST_F(ThreadTest, BuildSetUnpack) {
HandleScope scope(thread_);
const char* src = R"(
s = {*[0, 1], *{2, 3}, *(4, 5), *[]}
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
Object s(&scope, mainModuleAt(runtime_, "s"));
EXPECT_TRUE(s.isSet());
Set set_s(&scope, *s);
EXPECT_EQ(set_s.numItems(), 6);
Object small_int(&scope, SmallInt::fromWord(0));
EXPECT_TRUE(setIncludes(thread_, set_s, small_int));
small_int = SmallInt::fromWord(1);
EXPECT_TRUE(setIncludes(thread_, set_s, small_int));
small_int = SmallInt::fromWord(2);
EXPECT_TRUE(setIncludes(thread_, set_s, small_int));
small_int = SmallInt::fromWord(3);
EXPECT_TRUE(setIncludes(thread_, set_s, small_int));
small_int = SmallInt::fromWord(4);
EXPECT_TRUE(setIncludes(thread_, set_s, small_int));
small_int = SmallInt::fromWord(5);
EXPECT_TRUE(setIncludes(thread_, set_s, small_int));
}
TEST_F(BuildString, BuildStringEmpty) {
HandleScope scope(thread_);
const byte bytecode[] = {BUILD_STRING, 0, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytes(bytecode));
EXPECT_TRUE(isStrEqualsCStr(runCode(code), ""));
}
TEST_F(BuildString, BuildStringSingle) {
HandleScope scope(thread_);
Object str(&scope, SmallStr::fromCStr("foo"));
Tuple consts(&scope, runtime_->newTupleWith1(str));
const byte bytecode[] = {LOAD_CONST, 0, BUILD_STRING, 1, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytesConsts(bytecode, consts));
EXPECT_TRUE(isStrEqualsCStr(runCode(code), "foo"));
}
TEST_F(BuildString, BuildStringMultiSmall) {
HandleScope scope(thread_);
Object str(&scope, SmallStr::fromCStr("foo"));
Object str1(&scope, SmallStr::fromCStr("bar"));
Tuple consts(&scope, runtime_->newTupleWith2(str, str1));
const byte bytecode[] = {LOAD_CONST, 0, LOAD_CONST, 1,
BUILD_STRING, 2, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytesConsts(bytecode, consts));
EXPECT_TRUE(isStrEqualsCStr(runCode(code), "foobar"));
}
TEST_F(BuildString, BuildStringMultiLarge) {
HandleScope scope(thread_);
Object str(&scope, SmallStr::fromCStr("hello"));
Object str1(&scope, SmallStr::fromCStr("world"));
Object str2(&scope, SmallStr::fromCStr("python"));
Tuple consts(&scope, runtime_->newTupleWith3(str, str1, str2));
const byte bytecode[] = {LOAD_CONST, 0, LOAD_CONST, 1, LOAD_CONST, 2,
BUILD_STRING, 3, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytesConsts(bytecode, consts));
EXPECT_TRUE(isStrEqualsCStr(runCode(code), "helloworldpython"));
}
TEST_F(UnpackSeq, UnpackRange) {
const char* src = R"(
[a ,b, c] = range(2, 5)
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "a"), 2));
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "b"), 3));
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "c"), 4));
}
// LIST_APPEND(listAdd) in list_comp, followed by unpack
// TODO(rkng): list support in BINARY_ADD
TEST_F(UnpackList, UnpackListCompAppend) {
const char* src = R"(
a = [1, 2, 3]
b = [x for x in a]
b1, b2, b3 = b
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "b1"), 1));
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "b2"), 2));
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "b3"), 3));
}
TEST_F(ThreadTest, SetAdd) {
HandleScope scope(thread_);
const char* src = R"(
a = [1, 2, 3]
b = {x for x in a}
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
Object b(&scope, mainModuleAt(runtime_, "b"));
ASSERT_TRUE(b.isSet());
Set set_b(&scope, *b);
EXPECT_EQ(set_b.numItems(), 3);
}
TEST_F(ThreadTest, MapAdd) {
HandleScope scope(thread_);
const char* src = R"(
a = ['a', 'b', 'c']
b = {x:x for x in a}
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
Object b(&scope, mainModuleAt(runtime_, "b"));
EXPECT_EQ(b.isDict(), true);
Dict dict_b(&scope, *b);
EXPECT_EQ(dict_b.numItems(), 3);
}
TEST_F(UnpackList, UnpackNestedLists) {
const char* src = R"(
b = [[1,2], [3,4,5]]
b1, b2 = b
b11, b12 = b1
b21, b22, b23 = b2
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
HandleScope scope(thread_);
List b(&scope, mainModuleAt(runtime_, "b"));
EXPECT_EQ(b.numItems(), 2);
List b1(&scope, mainModuleAt(runtime_, "b1"));
EXPECT_EQ(b1.numItems(), 2);
List b2(&scope, mainModuleAt(runtime_, "b2"));
EXPECT_EQ(b2.numItems(), 3);
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "b11"), 1));
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "b12"), 2));
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "b21"), 3));
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "b22"), 4));
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "b23"), 5));
}
TEST_F(UnpackSeq, UnpackRangeStep) {
const char* src = R"(
[a ,b, c, d] = range(2, 10, 2)
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "a"), 2));
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "b"), 4));
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "c"), 6));
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "d"), 8));
}
TEST_F(UnpackSeq, UnpackRangeNeg) {
const char* src = R"(
[a ,b, c, d, e] = range(-10, 0, 2)
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "a"), -10));
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "b"), -8));
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "c"), -6));
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "d"), -4));
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "e"), -2));
}
TEST_F(ListIterTest, Build) {
const char* src = R"(
a = [1, 2, 3]
result = []
for x in a:
result.append(x)
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
HandleScope scope(thread_);
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_PYLIST_EQ(result, {1, 2, 3});
}
TEST_F(ListAppendTest, BuildAndUnpack) {
const char* src = R"(
a = [1, 2]
b = [x for x in [a] * 3]
b1, b2, b3 = b
b11, b12 = b1
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
HandleScope scope(thread_);
List b(&scope, mainModuleAt(runtime_, "b"));
EXPECT_EQ(b.numItems(), 3);
List b1(&scope, mainModuleAt(runtime_, "b1"));
EXPECT_EQ(b1.numItems(), 2);
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "b11"), 1));
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "b12"), 2));
}
TEST_F(ListInsertTest, InsertToList) {
HandleScope scope(thread_);
const char* src = R"(
l = []
for i in range(16):
if i == 2 or i == 12:
continue
l.append(i)
a, b = l[2], l[12]
l.insert(2, 2)
l.insert(12, 12)
s = 0
for el in l:
s += el
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
Object a(&scope, mainModuleAt(runtime_, "a"));
Object b(&scope, mainModuleAt(runtime_, "b"));
Object l(&scope, mainModuleAt(runtime_, "l"));
Object s(&scope, mainModuleAt(runtime_, "s"));
EXPECT_FALSE(isIntEqualsWord(*a, 2));
EXPECT_FALSE(isIntEqualsWord(*b, 12));
List list_l(&scope, *l);
ASSERT_EQ(list_l.numItems(), 16);
EXPECT_TRUE(isIntEqualsWord(list_l.at(2), 2));
EXPECT_TRUE(isIntEqualsWord(list_l.at(12), 12));
// sum(0..16) = 120
EXPECT_TRUE(isIntEqualsWord(*s, 120));
}
TEST_F(ListInsertTest, InsertToListBounds) {
HandleScope scope(thread_);
const char* src = R"(
l = [x for x in range(1, 5)]
l.insert(100, 5)
l.insert(400, 6)
l.insert(-100, 0)
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
Object l(&scope, mainModuleAt(runtime_, "l"));
List list_l(&scope, *l);
EXPECT_TRUE(isIntEqualsWord(list_l.at(0), 0));
EXPECT_TRUE(isIntEqualsWord(list_l.at(1), 1));
EXPECT_TRUE(isIntEqualsWord(list_l.at(2), 2));
EXPECT_TRUE(isIntEqualsWord(list_l.at(3), 3));
EXPECT_TRUE(isIntEqualsWord(list_l.at(4), 4));
EXPECT_TRUE(isIntEqualsWord(list_l.at(5), 5));
EXPECT_TRUE(isIntEqualsWord(list_l.at(6), 6));
}
TEST_F(ListInsertTest, InsertToNegativeIndex) {
HandleScope scope(thread_);
const char* src = R"(
l = [0, 2, 4]
l.insert(-2, 1)
l.insert(-1, 3)
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
Object l(&scope, mainModuleAt(runtime_, "l"));
List list_l(&scope, *l);
ASSERT_EQ(list_l.numItems(), 5);
EXPECT_TRUE(isIntEqualsWord(list_l.at(0), 0));
EXPECT_TRUE(isIntEqualsWord(list_l.at(1), 1));
EXPECT_TRUE(isIntEqualsWord(list_l.at(2), 2));
EXPECT_TRUE(isIntEqualsWord(list_l.at(3), 3));
EXPECT_TRUE(isIntEqualsWord(list_l.at(4), 4));
}
TEST_F(ThreadTest, BaseTypeConflict) {
const char* src = R"(
class Foo(list, dict): pass
)";
EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, src), LayoutId::kTypeError,
"multiple bases have instance lay-out conflict"));
}
TEST_F(ThreadTest, BreakLoopWhileLoop) {
const char* src = R"(
a = 0
result = []
while 1:
a = a + 1
result.append(a)
if a == 3:
break
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
HandleScope scope(thread_);
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_PYLIST_EQ(result, {1, 2, 3});
}
TEST_F(ThreadTest, BreakLoopWhileLoop1) {
const char* src = R"(
a = 0
result = []
while 1:
a = a + 1
result.append(a)
if a == 3:
break
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
HandleScope scope(thread_);
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_PYLIST_EQ(result, {1, 2, 3});
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "a"), 3));
}
TEST_F(ThreadTest, BreakLoopRangeLoop) {
const char* src = R"(
result = []
for x in range(1,6):
if x == 3:
break;
result.append(x)
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
HandleScope scope(thread_);
Object result(&scope, mainModuleAt(runtime_, "result"));
EXPECT_PYLIST_EQ(result, {1, 2});
}
TEST_F(ThreadTest, ContinueLoopRangeLoop) {
HandleScope scope(thread_);
const char* src = R"(
l = []
for x in range(4):
if x == 3:
try:
continue
except:
# This is to prevent the peephole optimizer
# from turning the CONTINUE_LOOP op
# into a JUMP_ABSOLUTE op
pass
l.append(x)
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
Object l(&scope, mainModuleAt(runtime_, "l"));
EXPECT_TRUE(l.isList());
List list_l(&scope, *l);
ASSERT_GE(list_l.numItems(), 3);
EXPECT_EQ(list_l.at(0), SmallInt::fromWord(0));
EXPECT_EQ(list_l.at(1), SmallInt::fromWord(1));
EXPECT_EQ(list_l.at(2), SmallInt::fromWord(2));
}
TEST_F(ThreadTest, Func2TestPyStone) { // mimic pystone.py Func2
const char* src = R"(
def f1(x, y):
return x + y
def f2():
return f1(1, 2)
result = f2()
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 3));
}
TEST_F(ThreadTest, BinSubscrString) { // pystone dependency
const char* src = R"(
a = 'Hello'
r0 = a[0]
r1 = a[1]
r2 = a[2]
r3 = a[3]
r4 = a[4]
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
EXPECT_TRUE(isStrEqualsCStr(mainModuleAt(runtime_, "r0"), "H"));
EXPECT_TRUE(isStrEqualsCStr(mainModuleAt(runtime_, "r1"), "e"));
EXPECT_TRUE(isStrEqualsCStr(mainModuleAt(runtime_, "r2"), "l"));
EXPECT_TRUE(isStrEqualsCStr(mainModuleAt(runtime_, "r3"), "l"));
EXPECT_TRUE(isStrEqualsCStr(mainModuleAt(runtime_, "r4"), "o"));
}
TEST_F(ThreadTest, SetupExceptNoOp) { // pystone dependency
const char* src = R"(
def f(x):
try:
return x
except ValueError:
return "Invalid Argument"
result = f(100)
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
EXPECT_TRUE(isIntEqualsWord(mainModuleAt(runtime_, "result"), 100));
}
TEST_F(ThreadTest, BuildTypeWithMetaclass) {
HandleScope scope(thread_);
const char* src = R"(
class Foo(metaclass=type):
pass
a = Foo()
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
Object foo(&scope, mainModuleAt(runtime_, "Foo"));
EXPECT_TRUE(foo.isType());
Object a(&scope, mainModuleAt(runtime_, "a"));
EXPECT_TRUE(runtime_->typeOf(*a) == *foo);
}
TEST_F(ThreadTest, BuildTypeWithMetaclass2) {
HandleScope scope(thread_);
const char* src = R"(
class Foo(type):
def __new__(mcls, name, bases, dict):
cls = super(Foo, mcls).__new__(mcls, name, bases, dict)
cls.lalala = 123
return cls
class Bar(metaclass=Foo):
def __init__(self):
self.hahaha = 456
b = Bar.lalala
a = Bar()
c = a.hahaha
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
Object bar(&scope, mainModuleAt(runtime_, "Bar"));
EXPECT_TRUE(runtime_->isInstanceOfType(*bar));
Object a(&scope, mainModuleAt(runtime_, "a"));
EXPECT_TRUE(runtime_->typeOf(*a) == *bar);
Object b(&scope, mainModuleAt(runtime_, "b"));
EXPECT_TRUE(isIntEqualsWord(*b, 123));
Object c(&scope, mainModuleAt(runtime_, "c"));
EXPECT_TRUE(isIntEqualsWord(*c, 456));
}
TEST_F(ThreadTest, NameLookupInTypeBodyFindsImplicitGlobal) {
HandleScope scope(thread_);
const char* src = R"(
a = 0
b = 0
class C:
global a
global b
PI = 3
a = PI
PIPI = PI * 2
b = PIPI
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
Object a(&scope, mainModuleAt(runtime_, "a"));
EXPECT_TRUE(isIntEqualsWord(*a, 3));
Object b(&scope, mainModuleAt(runtime_, "b"));
EXPECT_TRUE(isIntEqualsWord(*b, 6));
}
TEST_F(ThreadTest, NameLookupInTypeBodyFindsGlobal) {
HandleScope scope(thread_);
const char* src = R"(
var = 1
class C:
global one
global two
one = var
var = 2
two = var
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
Object one(&scope, mainModuleAt(runtime_, "one"));
EXPECT_TRUE(isIntEqualsWord(*one, 1));
Object two(&scope, mainModuleAt(runtime_, "two"));
EXPECT_TRUE(isIntEqualsWord(*two, 2));
}
TEST_F(ThreadTest, ExecuteDeleteName) {
HandleScope scope(thread_);
const char* src = R"(
var = 1
del var
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
Object var(&scope, mainModuleAt(runtime_, "var"));
EXPECT_TRUE(var.isError());
}
TEST_F(ThreadTest, SetupFinally) {
HandleScope scope(thread_);
const char* src = R"(
x = 1
try:
pass
finally:
x = 2
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
Object x(&scope, mainModuleAt(runtime_, "x"));
EXPECT_EQ(*x, SmallInt::fromWord(2));
}
TEST_F(ThreadTest, SetupAnnotationsAndStoreAnnotations) {
HandleScope scope(thread_);
const char* src = R"(
global_with_annotation: int = 1
class ClassWithAnnotation:
attribute_with_annotation: int = 2
class_anno_dict = ClassWithAnnotation.__annotations__
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
Dict module_anno_dict(&scope, mainModuleAt(runtime_, "__annotations__"));
Str m_key(&scope, runtime_->newStrFromCStr("global_with_annotation"));
Object m_value(&scope, dictAtByStr(thread_, module_anno_dict, m_key));
EXPECT_EQ(*m_value, runtime_->typeAt(LayoutId::kInt));
Dict class_anno_dict(&scope, mainModuleAt(runtime_, "class_anno_dict"));
Str c_key(&scope, runtime_->newStrFromCStr("attribute_with_annotation"));
Object c_value(&scope, dictAtByStr(thread_, class_anno_dict, c_key));
EXPECT_EQ(*c_value, runtime_->typeAt(LayoutId::kInt));
}
TEST_F(ThreadTest, DeleteFastRaisesUnboundLocalError) {
const char* src = R"(
def foo(a, b, c):
del a
return a
foo(1, 2, 3)
)";
EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, src),
LayoutId::kUnboundLocalError,
"local variable 'a' referenced before assignment"));
}
TEST_F(ThreadTest, DeleteFast) {
HandleScope scope(thread_);
const char* src = R"(
def foo(a, b, c):
del a
return b
x = foo(1, 2, 3)
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
Object x(&scope, mainModuleAt(runtime_, "x"));
EXPECT_EQ(*x, SmallInt::fromWord(2));
}
TEST_F(ThreadTest, ConstructInstanceWithKwargs) {
HandleScope scope(thread_);
const char* src = R"(
result_a = None
result_b = None
result_c = None
class Foo:
def __init__(self, a, b=None, c=None):
global result_a, result_b, result_c
result_a = a
result_b = b
result_c = c
foo = Foo(1111, b=2222, c=3333)
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
Object result_a(&scope, mainModuleAt(runtime_, "result_a"));
EXPECT_TRUE(isIntEqualsWord(*result_a, 1111));
Object result_b(&scope, mainModuleAt(runtime_, "result_b"));
EXPECT_TRUE(isIntEqualsWord(*result_b, 2222));
Object result_c(&scope, mainModuleAt(runtime_, "result_c"));
EXPECT_TRUE(isIntEqualsWord(*result_c, 3333));
}
TEST_F(ThreadTest, LoadTypeDeref) {
HandleScope scope(thread_);
const char* src = R"(
def foo():
a = 1
class Foo:
b = a
return Foo.b
x = foo()
)";
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
Object x(&scope, mainModuleAt(runtime_, "x"));
EXPECT_EQ(*x, SmallInt::fromWord(1));
}
TEST_F(ThreadTest, LoadTypeDerefFromLocal) {
HandleScope scope(thread_);
Object obj1(&scope, SmallInt::fromWord(1111));
Tuple consts(&scope, runtime_->newTupleWith1(obj1));
Object obj3(&scope, Runtime::internStrFromCStr(thread_, "lalala"));
Tuple names(&scope, runtime_->newTupleWith1(obj3));
const byte bytecode[] = {LOAD_CONST, 0, STORE_NAME, 0,
LOAD_CLASSDEREF, 0, RETURN_VALUE, 0};
Code code(&scope,
newCodeWithBytesConstsNamesFlags(bytecode, consts, names, 0));
Object obj2(&scope, Runtime::internStrFromCStr(thread_, "lalala"));
Tuple freevars(&scope, runtime_->newTupleWith1(obj2));
code.setFreevars(*freevars);
Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
Module module(&scope, findMainModule(runtime_));
Function function(&scope,
runtime_->newFunctionWithCode(thread_, name, code, module));
Tuple closure(&scope, runtime_->newTupleWith1(obj3));
function.setClosure(*closure);
Dict locals(&scope, runtime_->newDict());
EXPECT_TRUE(isIntEqualsWord(
thread_->callFunctionWithImplicitGlobals(function, locals), 1111));
}
TEST_F(ThreadTest, PushCallFrameWithSameGlobalsPropagatesBuiltins) {
HandleScope scope(thread_);
Code code(&scope, newCodeWithBytes(View<byte>(nullptr, 0)));
Object qualname(&scope, runtime_->newStrFromCStr("<anonymous>"));
Module module(&scope, findMainModule(runtime_));
Function function(
&scope, runtime_->newFunctionWithCode(thread_, qualname, code, module));
Frame* frame = thread_->currentFrame();
thread_->stackPush(*function);
Frame* new_frame = thread_->pushCallFrame(*function);
EXPECT_NE(new_frame, frame);
}
TEST_F(ThreadTest, ExecSetsMissingDunderBuiltins) {
HandleScope scope(thread_);
Object obj(&scope, NoneType::object());
Tuple consts(&scope, runtime_->newTupleWith1(obj));
const byte bytecode[] = {LOAD_CONST, 0, RETURN_VALUE, 0};
Code code(&scope, newCodeWithBytesConsts(bytecode, consts));
code.setFlags(0);
Object name(&scope, runtime_->newStrFromCStr("<test module>"));
Module module(&scope, runtime_->newModule(name));
Object none(&scope, NoneType::object());
ASSERT_TRUE(thread_->exec(code, module, none).isNoneType());
Module builtins_module(&scope, runtime_->findModuleById(ID(builtins)));
Object proxy(&scope, builtins_module.moduleProxy());
EXPECT_EQ(moduleAtById(thread_, module, ID(__builtins__)), proxy);
}
TEST_F(ThreadTest, CallFunctionInDifferentModule) {
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, R"(
def a():
def inner_a():
return object
return inner_a
result0 = a()()
)")
.isError());
// Object a(&scope, mainModuleAt(runtime_, "a"));
Object result0(&scope, mainModuleAt(runtime_, "result0"));
Object object(&scope, moduleAtByCStr(runtime_, "builtins", "object"));
EXPECT_EQ(result0, object);
}
TEST_F(ThreadTest, RaiseWithFmtFormatsString) {
EXPECT_TRUE(raisedWithStr(
thread_->raiseWithFmt(LayoutId::kTypeError, "hello %Y", ID(dict)),
LayoutId::kTypeError, "hello dict"));
}
TEST_F(ThreadTest, RaiseOSErrorFromErrnoWithInvalidErrnoReturnsOSError) {
EXPECT_TRUE(raised(thread_->raiseOSErrorFromErrno(-5), LayoutId::kOSError));
}
TEST_F(ThreadTest, RaiseOSErrorFromErrnoPicksOSErrorSubclass) {
EXPECT_TRUE(raised(thread_->raiseOSErrorFromErrno(EACCES),
LayoutId::kPermissionError));
}
TEST_F(ThreadTest, StrOffsetWithNegativeReturnsNegativeOne) {
HandleScope scope(thread_);
Str str(&scope, SmallStr::fromCStr("foo"));
EXPECT_EQ(thread_->strOffset(str, -10), -1);
}
TEST_F(ThreadTest, StrOffsetWithLargeNumberReturnsLength) {
HandleScope scope(thread_);
Str str(&scope, SmallStr::fromCStr("foo"));
EXPECT_EQ(thread_->strOffset(str, 5), 3);
}
TEST_F(ThreadTest, StrOffsetWithIndexReturnsOffset) {
HandleScope scope(thread_);
Str str(&scope, SmallStr::fromCStr("\u00e9tude"));
EXPECT_EQ(thread_->strOffset(str, 2), 3);
}
TEST_F(ThreadTest, StrOffsetWithCacheHitReturnsCorrectOffsets) {
HandleScope scope(thread_);
Str str(&scope, runtime_->newStrFromCStr("\u00e9l\u00e9gie"));
EXPECT_EQ(thread_->strOffset(str, 2), 3);
EXPECT_EQ(thread_->strOffset(str, 4), 6);
EXPECT_EQ(thread_->strOffset(str, 1), 2);
}
TEST_F(ThreadTest, StrOffsetWithCacheMissReturnsCorrectOffsets) {
HandleScope scope(thread_);
Str str(&scope, runtime_->newStrFromCStr("\u00e9l\u00e9gie"));
EXPECT_EQ(thread_->strOffset(str, 2), 3);
// out of bounds
EXPECT_EQ(thread_->strOffset(str, 10), 8);
EXPECT_EQ(thread_->strOffset(str, 1), 2);
// new string
str = runtime_->newStrFromCStr("foo");
EXPECT_EQ(thread_->strOffset(str, 2), 2);
}
} // namespace testing
} // namespace py