// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #include "platform/assert.h" #include "vm/globals.h" #if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64) #include "vm/ast.h" #include "vm/assembler.h" #include "vm/class_finalizer.h" #include "vm/code_generator.h" #include "vm/compiler.h" #include "vm/dart_entry.h" #include "vm/native_entry.h" #include "vm/native_entry_test.h" #include "vm/unit_test.h" #include "vm/virtual_memory.h" namespace dart { static const intptr_t kPos = Scanner::kDummyTokenIndex; // Helper to allocate and return a LocalVariable. static LocalVariable* NewTestLocalVariable(const char* name) { const String& variable_name = String::ZoneHandle(String::New(name)); const Type& variable_type = Type::ZoneHandle(Type::DynamicType()); return new LocalVariable(kPos, variable_name, variable_type); } CODEGEN_TEST_GENERATE(SimpleReturnCodegen, test) { test->node_sequence()->Add(new ReturnNode(kPos)); } CODEGEN_TEST_RUN(SimpleReturnCodegen, Instance::null()) CODEGEN_TEST_GENERATE(SmiReturnCodegen, test) { LiteralNode* l = new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(3))); test->node_sequence()->Add(new ReturnNode(kPos, l)); } CODEGEN_TEST_RUN(SmiReturnCodegen, Smi::New(3)) CODEGEN_TEST2_GENERATE(SimpleStaticCallCodegen, function, test) { // Wrap the SmiReturnCodegen test above as a static function and call it. ArgumentListNode* no_arguments = new ArgumentListNode(kPos); test->node_sequence()->Add( new ReturnNode(kPos, new StaticCallNode(kPos, function, no_arguments))); } CODEGEN_TEST2_RUN(SimpleStaticCallCodegen, SmiReturnCodegen, Smi::New(3)) CODEGEN_TEST_GENERATE(ReturnParameterCodegen, test) { SequenceNode* node_seq = test->node_sequence(); const int num_params = 1; LocalVariable* parameter = NewTestLocalVariable("parameter"); LocalScope* local_scope = node_seq->scope(); local_scope->AddVariable(parameter); ASSERT(local_scope->num_variables() == num_params); const Function& function = test->function(); function.set_num_fixed_parameters(num_params); ASSERT(function.num_optional_parameters() == 0); node_seq->Add(new ReturnNode(kPos, new LoadLocalNode(kPos, *parameter))); } CODEGEN_TEST2_GENERATE(StaticCallReturnParameterCodegen, function, test) { // Wrap and call the ReturnParameterCodegen test above as a static function. SequenceNode* node_seq = test->node_sequence(); ArgumentListNode* arguments = new ArgumentListNode(kPos); arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(3)))); node_seq->Add(new ReturnNode(kPos, new StaticCallNode(kPos, function, arguments))); } CODEGEN_TEST2_RUN(StaticCallReturnParameterCodegen, ReturnParameterCodegen, Smi::New(3)) CODEGEN_TEST_GENERATE(SmiParamSumCodegen, test) { SequenceNode* node_seq = test->node_sequence(); const int num_params = 2; LocalVariable* param1 = NewTestLocalVariable("param1"); LocalVariable* param2 = NewTestLocalVariable("param2"); const int num_locals = 1; LocalVariable* sum = NewTestLocalVariable("sum"); LocalScope* local_scope = node_seq->scope(); local_scope->AddVariable(param1); local_scope->AddVariable(param2); local_scope->AddVariable(sum); ASSERT(local_scope->num_variables() == num_params + num_locals); const Function& function = test->function(); function.set_num_fixed_parameters(num_params); ASSERT(function.num_optional_parameters() == 0); BinaryOpNode* add = new BinaryOpNode(kPos, Token::kADD, new LoadLocalNode(kPos, *param1), new LoadLocalNode(kPos, *param2)); node_seq->Add(new StoreLocalNode(kPos, *sum, add)); node_seq->Add(new ReturnNode(kPos, new LoadLocalNode(kPos, *sum))); } CODEGEN_TEST2_GENERATE(StaticCallSmiParamSumCodegen, function, test) { // Wrap and call the SmiParamSumCodegen test above as a static function. SequenceNode* node_seq = test->node_sequence(); ArgumentListNode* arguments = new ArgumentListNode(kPos); arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(3)))); arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(2)))); node_seq->Add(new ReturnNode(kPos, new StaticCallNode(kPos, function, arguments))); } CODEGEN_TEST2_RUN(StaticCallSmiParamSumCodegen, SmiParamSumCodegen, Smi::New(5)) CODEGEN_TEST_GENERATE(SmiAddCodegen, test) { SequenceNode* node_seq = test->node_sequence(); LiteralNode* a = new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(3))); LiteralNode* b = new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(2))); BinaryOpNode* add_node = new BinaryOpNode(kPos, Token::kADD, a, b); node_seq->Add(new ReturnNode(kPos, add_node)); } CODEGEN_TEST_RUN(SmiAddCodegen, Smi::New(5)) CODEGEN_TEST_GENERATE(GenericAddCodegen, test) { SequenceNode* node_seq = test->node_sequence(); LiteralNode* a = new LiteralNode(kPos, Double::ZoneHandle(Double::New(12.2))); LiteralNode* b = new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(2))); BinaryOpNode* add_node_1 = new BinaryOpNode(kPos, Token::kADD, a, b); LiteralNode* c = new LiteralNode(kPos, Double::ZoneHandle(Double::New(0.8))); BinaryOpNode* add_node_2 = new BinaryOpNode(kPos, Token::kADD, add_node_1, c); node_seq->Add(new ReturnNode(kPos, add_node_2)); } CODEGEN_TEST_RUN(GenericAddCodegen, Double::New(15.0)) CODEGEN_TEST_GENERATE(SmiBinaryOpCodegen, test) { SequenceNode* node_seq = test->node_sequence(); LiteralNode* a = new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(4))); LiteralNode* b = new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(2))); LiteralNode* c = new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(3))); BinaryOpNode* sub_node = new BinaryOpNode(kPos, Token::kSUB, a, b); // 4 - 2 -> 2. BinaryOpNode* mul_node = new BinaryOpNode(kPos, Token::kMUL, sub_node, c); // 2 * 3 -> 6. BinaryOpNode* div_node = new BinaryOpNode(kPos, Token::kTRUNCDIV, mul_node, b); // 6 ~/ 2 -> 3. node_seq->Add(new ReturnNode(kPos, div_node)); } CODEGEN_TEST_RUN(SmiBinaryOpCodegen, Smi::New(3)) CODEGEN_TEST_GENERATE(BoolNotCodegen, test) { SequenceNode* node_seq = test->node_sequence(); const Bool& bool_false = Bool::ZoneHandle(Bool::False()); LiteralNode* b = new LiteralNode(kPos, bool_false); UnaryOpNode* not_node = new UnaryOpNode(kPos, Token::kNOT, b); node_seq->Add(new ReturnNode(kPos, not_node)); } CODEGEN_TEST_RUN(BoolNotCodegen, Bool::True()) CODEGEN_TEST_GENERATE(BoolAndCodegen, test) { SequenceNode* node_seq = test->node_sequence(); const Bool& bool_true = Bool::ZoneHandle(Bool::True()); const Bool& bool_false = Bool::ZoneHandle(Bool::False()); LiteralNode* a = new LiteralNode(kPos, bool_true); LiteralNode* b = new LiteralNode(kPos, bool_false); BinaryOpNode* and_node = new BinaryOpNode(kPos, Token::kAND, a, b); node_seq->Add(new ReturnNode(kPos, and_node)); } CODEGEN_TEST_RUN(BoolAndCodegen, Bool::False()) CODEGEN_TEST_GENERATE(BinaryOpCodegen, test) { SequenceNode* node_seq = test->node_sequence(); LiteralNode* a = new LiteralNode(kPos, Double::ZoneHandle(Double::New(12))); LiteralNode* b = new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(2))); LiteralNode* c = new LiteralNode(kPos, Double::ZoneHandle(Double::New(0.5))); BinaryOpNode* sub_node = new BinaryOpNode(kPos, Token::kSUB, a, b); BinaryOpNode* mul_node = new BinaryOpNode(kPos, Token::kMUL, sub_node, c); BinaryOpNode* div_node = new BinaryOpNode(kPos, Token::kDIV, mul_node, b); node_seq->Add(new ReturnNode(kPos, div_node)); } CODEGEN_TEST_RUN(BinaryOpCodegen, Double::New(2.5)); // Tested Dart code: // int dec(int a, int b = 1) native: "TestSmiSub"; // The native entry TestSmiSub implements dec natively. CODEGEN_TEST_GENERATE(NativeDecCodegen, test) { // A NativeBodyNode, preceded by an EnterNode and followed by a ReturnNode, // implements the body of a native Dart function. Let's take this native // function as an example: int dec(int a, int b = 1) native; // Since this function has an optional parameter, its prologue will copy // incoming parameters to locals. SequenceNode* node_seq = test->node_sequence(); const int num_fixed_params = 1; const int num_opt_params = 1; const int num_params = num_fixed_params + num_opt_params; LocalScope* local_scope = node_seq->scope(); local_scope->AddVariable(NewTestLocalVariable("a")); local_scope->AddVariable(NewTestLocalVariable("b")); ASSERT(local_scope->num_variables() == num_params); const Array& default_values = Array::ZoneHandle(Array::New(num_opt_params)); default_values.SetAt(0, Smi::ZoneHandle(Smi::New(1))); // b = 1. test->set_default_parameter_values(default_values); const Function& function = test->function(); function.set_num_fixed_parameters(num_fixed_params); function.set_num_optional_parameters(num_opt_params); const bool has_opt_params = true; const String& native_name = String::ZoneHandle(String::NewSymbol("TestSmiSub")); NativeFunction native_function = reinterpret_cast(NATIVE_ENTRY_FUNCTION(TestSmiSub)); node_seq->Add(new ReturnNode(kPos, new NativeBodyNode(kPos, native_name, native_function, num_params, has_opt_params))); } // Tested Dart code: // return dec(5); CODEGEN_TEST2_GENERATE(StaticDecCallCodegen, function, test) { SequenceNode* node_seq = test->node_sequence(); ArgumentListNode* arguments = new ArgumentListNode(kPos); arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(5)))); node_seq->Add(new ReturnNode(kPos, new StaticCallNode(kPos, function, arguments))); } CODEGEN_TEST2_RUN(StaticDecCallCodegen, NativeDecCodegen, Smi::New(4)) CODEGEN_TEST_GENERATE(SmiUnaryOpCodegen, test) { SequenceNode* node_seq = test->node_sequence(); LiteralNode* a = new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(12))); UnaryOpNode* neg_node = new UnaryOpNode(kPos, Token::kSUB, a); node_seq->Add(new ReturnNode(kPos, neg_node)); } CODEGEN_TEST_RUN(SmiUnaryOpCodegen, Smi::New(-12)) CODEGEN_TEST_GENERATE(DoubleUnaryOpCodegen, test) { SequenceNode* node_seq = test->node_sequence(); LiteralNode* a = new LiteralNode(kPos, Double::ZoneHandle(Double::New(12.0))); UnaryOpNode* neg_node = new UnaryOpNode(kPos, Token::kSUB, a); node_seq->Add(new ReturnNode(kPos, neg_node)); } CODEGEN_TEST_RUN(DoubleUnaryOpCodegen, Double::New(-12.0)) static Library& MakeTestLibrary(const char* url) { const String& lib_url = String::ZoneHandle(String::NewSymbol(url)); Library& lib = Library::ZoneHandle(Library::New(lib_url)); lib.Register(); return lib; } static RawClass* LookupClass(const Library& lib, const char* name) { const String& cls_name = String::ZoneHandle(String::NewSymbol(name)); return lib.LookupClass(cls_name); } CODEGEN_TEST_GENERATE(StaticCallCodegen, test) { const char* kScriptChars = "class A {\n" " static bar() { return 42; }\n" " static fly() { return 5; }\n" "}\n"; String& url = String::Handle(String::New("dart-test:CompileScript")); String& source = String::Handle(String::New(kScriptChars)); Script& script = Script::Handle(Script::New(url, source, RawScript::kSource)); Library& lib = MakeTestLibrary("TestLib"); EXPECT(CompilerTest::TestCompileScript(lib, script)); EXPECT(ClassFinalizer::FinalizePendingClasses()); Class& cls = Class::Handle(LookupClass(lib, "A")); EXPECT(!cls.IsNull()); // 'bar' will not be compiled. String& function_bar_name = String::Handle(String::New("bar")); Function& function_bar = Function::ZoneHandle(cls.LookupStaticFunction(function_bar_name)); EXPECT(!function_bar.IsNull()); EXPECT(!function_bar.HasCode()); // 'fly' will be compiled. String& function_fly_name = String::Handle(String::New("fly")); Function& function_fly = Function::ZoneHandle(cls.LookupStaticFunction(function_fly_name)); EXPECT(!function_fly.IsNull()); EXPECT(CompilerTest::TestCompileFunction(function_fly)); EXPECT(function_fly.HasCode()); ArgumentListNode* no_arguments = new ArgumentListNode(kPos); StaticCallNode* call_bar = new StaticCallNode(kPos, function_bar, no_arguments); StaticCallNode* call_fly = new StaticCallNode(kPos, function_fly, no_arguments); BinaryOpNode* add_node = new BinaryOpNode(kPos, Token::kADD, call_bar, call_fly); test->node_sequence()->Add(new ReturnNode(kPos, add_node)); } CODEGEN_TEST_RUN(StaticCallCodegen, Smi::New(42 + 5)) CODEGEN_TEST_GENERATE(InstanceCallCodegen, test) { const char* kScriptChars = "class A {\n" " A() {}\n" " int bar() { return 42; }\n" "}\n"; String& url = String::Handle(String::New("dart-test:CompileScript")); String& source = String::Handle(String::New(kScriptChars)); Script& script = Script::Handle(Script::New(url, source, RawScript::kSource)); Library& lib = MakeTestLibrary("TestLib"); EXPECT(CompilerTest::TestCompileScript(lib, script)); EXPECT(ClassFinalizer::FinalizePendingClasses()); Class& cls = Class::ZoneHandle(LookupClass(lib, "A")); EXPECT(!cls.IsNull()); String& constructor_name = String::Handle(String::New("A.")); Function& constructor = Function::ZoneHandle(cls.LookupConstructor(constructor_name)); EXPECT(!constructor.IsNull()); // The unit test creates an instance of class A and calls function 'bar'. String& function_bar_name = String::ZoneHandle(String::NewSymbol("bar")); ArgumentListNode* no_arguments = new ArgumentListNode(kPos); const TypeArguments& no_type_arguments = TypeArguments::ZoneHandle(); InstanceCallNode* call_bar = new InstanceCallNode( kPos, new ConstructorCallNode( kPos, no_type_arguments, constructor, no_arguments), function_bar_name, no_arguments); test->node_sequence()->Add(new ReturnNode(kPos, call_bar)); } CODEGEN_TEST_RUN(InstanceCallCodegen, Smi::New(42)) // Tested Dart code: // int sum(int a, int b, // [int c = 10, int d = 21, int e = -32]) native: "TestSmiSum"; // The native entry TestSmiSum implements sum natively. CODEGEN_TEST_GENERATE(NativeSumCodegen, test) { SequenceNode* node_seq = test->node_sequence(); const int num_fixed_params = 2; const int num_opt_params = 3; const int num_params = num_fixed_params + num_opt_params; LocalScope* local_scope = node_seq->scope(); local_scope->AddVariable(NewTestLocalVariable("a")); local_scope->AddVariable(NewTestLocalVariable("b")); local_scope->AddVariable(NewTestLocalVariable("c")); local_scope->AddVariable(NewTestLocalVariable("d")); local_scope->AddVariable(NewTestLocalVariable("e")); ASSERT(local_scope->num_variables() == num_params); const Array& default_values = Array::ZoneHandle(Array::New(num_opt_params)); default_values.SetAt(0, Smi::ZoneHandle(Smi::New(10))); default_values.SetAt(1, Smi::ZoneHandle(Smi::New(21))); default_values.SetAt(2, Smi::ZoneHandle(Smi::New(-32))); test->set_default_parameter_values(default_values); const Function& function = test->function(); function.set_num_fixed_parameters(num_fixed_params); function.set_num_optional_parameters(num_opt_params); function.set_parameter_types(Array::Handle(Array::New(num_params))); function.set_parameter_names(Array::Handle(Array::New(num_params))); const Type& param_type = Type::Handle(Type::DynamicType()); for (int i = 0; i < num_params - 1; i++) { function.SetParameterTypeAt(i, param_type); } const bool has_opt_params = true; const String& native_name = String::ZoneHandle(String::NewSymbol("TestSmiSum")); NativeFunction native_function = reinterpret_cast(NATIVE_ENTRY_FUNCTION(TestSmiSum)); node_seq->Add(new ReturnNode(kPos, new NativeBodyNode(kPos, native_name, native_function, num_params, has_opt_params))); } // Tested Dart code, calling function sum declared above: // return sum(1, 3); // Optional arguments are not passed and hence are set to their default values. CODEGEN_TEST2_GENERATE(StaticSumCallNoOptCodegen, function, test) { SequenceNode* node_seq = test->node_sequence(); ArgumentListNode* arguments = new ArgumentListNode(kPos); arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(1)))); arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(3)))); node_seq->Add(new ReturnNode(kPos, new StaticCallNode(kPos, function, arguments))); } CODEGEN_TEST2_RUN(StaticSumCallNoOptCodegen, NativeSumCodegen, Smi::New(1 + 3 + 10 + 21 - 32)) // Tested Dart code, calling function sum declared above: // return sum(1, 3, 5); // Only one out of three optional arguments is passed in; the second and third // arguments are hence set to their default values. CODEGEN_TEST2_GENERATE(StaticSumCallOneOptCodegen, function, test) { SequenceNode* node_seq = test->node_sequence(); ArgumentListNode* arguments = new ArgumentListNode(kPos); arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(1)))); arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(3)))); arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(5)))); node_seq->Add(new ReturnNode(kPos, new StaticCallNode(kPos, function, arguments))); } CODEGEN_TEST2_RUN(StaticSumCallOneOptCodegen, NativeSumCodegen, Smi::New(1 + 3 + 5 + 21 - 32)) // Tested Dart code, calling function sum declared above: // return sum(0, 1, 1, 2, 3); // Optional arguments are passed in. CODEGEN_TEST2_GENERATE(StaticSumCallTenFiboCodegen, function, test) { SequenceNode* node_seq = test->node_sequence(); ArgumentListNode* arguments = new ArgumentListNode(kPos); arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(0)))); arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(1)))); arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(1)))); arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(2)))); arguments->Add(new LiteralNode(kPos, Smi::ZoneHandle(Smi::New(3)))); node_seq->Add(new ReturnNode(kPos, new StaticCallNode(kPos, function, arguments))); } CODEGEN_TEST2_RUN( StaticSumCallTenFiboCodegen, NativeSumCodegen, Smi::New(0 + 1 + 1 + 2 + 3)) // Test allocation of dart objects. CODEGEN_TEST_GENERATE(AllocateNewObjectCodegen, test) { const char* kScriptChars = "class A {\n" " A() {}\n" " static bar() { return 42; }\n" "}\n"; String& url = String::Handle(String::New("dart-test:CompileScript")); String& source = String::Handle(String::New(kScriptChars)); Script& script = Script::Handle(Script::New(url, source, RawScript::kSource)); Library& lib = MakeTestLibrary("TestLib"); EXPECT(CompilerTest::TestCompileScript(lib, script)); EXPECT(ClassFinalizer::FinalizePendingClasses()); Class& cls = Class::ZoneHandle(LookupClass(lib, "A")); EXPECT(!cls.IsNull()); String& constructor_name = String::Handle(String::New("A.")); Function& constructor = Function::ZoneHandle(cls.LookupConstructor(constructor_name)); EXPECT(!constructor.IsNull()); const TypeArguments& no_type_arguments = TypeArguments::ZoneHandle(); ArgumentListNode* no_arguments = new ArgumentListNode(kPos); test->node_sequence()->Add(new ReturnNode(kPos, new ConstructorCallNode( kPos, no_type_arguments, constructor, no_arguments))); } CODEGEN_TEST_RAW_RUN(AllocateNewObjectCodegen, function) { GrowableArray arguments; const Array& kNoArgumentNames = Array::Handle(); Object& result = Object::Handle(); result = DartEntry::InvokeStatic(function, arguments, kNoArgumentNames); EXPECT(!result.IsError()); const Library& app_lib = Library::Handle( Isolate::Current()->object_store()->registered_libraries()); const Class& cls = Class::Handle( app_lib.LookupClass(String::Handle(String::NewSymbol("A")))); EXPECT_EQ(cls.raw(), result.clazz()); } } // namespace dart #endif // defined TARGET_ARCH_IA32 || defined(TARGET_ARCH_X64)