|  | // Copyright (c) 2011, 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 "vm/globals.h" | 
|  | #if defined(TARGET_ARCH_IA32) | 
|  |  | 
|  | #include "vm/dart_entry.h" | 
|  | #include "vm/isolate.h" | 
|  | #include "vm/native_entry.h" | 
|  | #include "vm/native_entry_test.h" | 
|  | #include "vm/object.h" | 
|  | #include "vm/runtime_entry.h" | 
|  | #include "vm/stub_code.h" | 
|  | #include "vm/symbols.h" | 
|  | #include "vm/unit_test.h" | 
|  |  | 
|  | #define __ assembler-> | 
|  |  | 
|  | namespace dart { | 
|  |  | 
|  | static Function* CreateFunction(const char* name) { | 
|  | const String& class_name = | 
|  | String::Handle(Symbols::New(Thread::Current(), "ownerClass")); | 
|  | const Script& script = Script::Handle(); | 
|  | const Library& lib = Library::Handle(Library::New(class_name)); | 
|  | const Class& owner_class = Class::Handle( | 
|  | Class::New(lib, class_name, script, TokenPosition::kNoSource)); | 
|  | const String& function_name = | 
|  | String::ZoneHandle(Symbols::New(Thread::Current(), name)); | 
|  | const FunctionType& signature = FunctionType::ZoneHandle(FunctionType::New()); | 
|  | Function& function = Function::ZoneHandle(Function::New( | 
|  | signature, function_name, UntaggedFunction::kRegularFunction, true, false, | 
|  | false, false, false, owner_class, TokenPosition::kMinSource)); | 
|  | return &function; | 
|  | } | 
|  |  | 
|  | // Test calls to stub code which calls into the runtime. | 
|  | static void GenerateCallToCallRuntimeStub(compiler::Assembler* assembler, | 
|  | int length) { | 
|  | const int argc = 2; | 
|  | const Smi& smi_length = Smi::ZoneHandle(Smi::New(length)); | 
|  | __ enter(compiler::Immediate(0)); | 
|  | __ PushObject(Object::null_object());  // Push Null object for return value. | 
|  | __ PushObject(smi_length);             // Push argument 1: length. | 
|  | __ PushObject(Object::null_object());  // Push argument 2: type arguments. | 
|  | ASSERT(kAllocateArrayRuntimeEntry.argument_count() == argc); | 
|  | __ CallRuntime(kAllocateArrayRuntimeEntry, argc); | 
|  | __ AddImmediate(ESP, compiler::Immediate(argc * kWordSize)); | 
|  | __ popl(EAX);  // Pop return value from return slot. | 
|  | __ leave(); | 
|  | __ ret(); | 
|  | } | 
|  |  | 
|  | ISOLATE_UNIT_TEST_CASE(CallRuntimeStubCode) { | 
|  | extern const Function& RegisterFakeFunction(const char* name, | 
|  | const Code& code); | 
|  | const int length = 10; | 
|  | const char* kName = "Test_CallRuntimeStubCode"; | 
|  | compiler::Assembler assembler(nullptr); | 
|  | GenerateCallToCallRuntimeStub(&assembler, length); | 
|  | SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock()); | 
|  | const Code& code = Code::Handle(Code::FinalizeCodeAndNotify( | 
|  | *CreateFunction("Test_CallRuntimeStubCode"), nullptr, &assembler, | 
|  | Code::PoolAttachment::kAttachPool)); | 
|  | const Function& function = RegisterFakeFunction(kName, code); | 
|  | Array& result = Array::Handle(); | 
|  | result ^= DartEntry::InvokeFunction(function, Object::empty_array()); | 
|  | EXPECT_EQ(length, result.Length()); | 
|  | } | 
|  |  | 
|  | // Test calls to stub code which calls into a leaf runtime entry. | 
|  | static void GenerateCallToCallLeafRuntimeStub(compiler::Assembler* assembler, | 
|  | const char* str_value, | 
|  | intptr_t lhs_index_value, | 
|  | intptr_t rhs_index_value, | 
|  | intptr_t length_value) { | 
|  | const String& str = String::ZoneHandle(String::New(str_value, Heap::kOld)); | 
|  | const Smi& lhs_index = Smi::ZoneHandle(Smi::New(lhs_index_value)); | 
|  | const Smi& rhs_index = Smi::ZoneHandle(Smi::New(rhs_index_value)); | 
|  | const Smi& length = Smi::ZoneHandle(Smi::New(length_value)); | 
|  | __ enter(compiler::Immediate(0)); | 
|  | { | 
|  | compiler::LeafRuntimeScope rt(assembler, | 
|  | /*frame_size=*/4 * kWordSize, | 
|  | /*preserve_registers=*/false); | 
|  | __ LoadObject(EAX, str); | 
|  | __ movl(compiler::Address(ESP, 0), EAX);  // Push argument 1. | 
|  | __ LoadObject(EAX, lhs_index); | 
|  | __ movl(compiler::Address(ESP, kWordSize), EAX);  // Push argument 2. | 
|  | __ LoadObject(EAX, rhs_index); | 
|  | __ movl(compiler::Address(ESP, 2 * kWordSize), EAX);  // Push argument 3. | 
|  | __ LoadObject(EAX, length); | 
|  | __ movl(compiler::Address(ESP, 3 * kWordSize), EAX);  // Push argument 4. | 
|  | rt.Call(kCaseInsensitiveCompareUCS2RuntimeEntry, 4); | 
|  | } | 
|  | __ leave(); | 
|  | __ ret();  // Return value is in EAX. | 
|  | } | 
|  |  | 
|  | ISOLATE_UNIT_TEST_CASE(CallLeafRuntimeStubCode) { | 
|  | extern const Function& RegisterFakeFunction(const char* name, | 
|  | const Code& code); | 
|  | const char* str_value = "abAB"; | 
|  | intptr_t lhs_index_value = 0; | 
|  | intptr_t rhs_index_value = 2; | 
|  | intptr_t length_value = 2; | 
|  | const char* kName = "Test_CallLeafRuntimeStubCode"; | 
|  | compiler::Assembler assembler(nullptr); | 
|  | GenerateCallToCallLeafRuntimeStub(&assembler, str_value, lhs_index_value, | 
|  | rhs_index_value, length_value); | 
|  | SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock()); | 
|  | const Code& code = Code::Handle(Code::FinalizeCodeAndNotify( | 
|  | *CreateFunction("Test_CallLeafRuntimeStubCode"), nullptr, &assembler, | 
|  | Code::PoolAttachment::kAttachPool)); | 
|  | if (FLAG_disassemble) { | 
|  | OS::PrintErr("Disassemble:\n"); | 
|  | code.Disassemble(); | 
|  | } | 
|  | const Function& function = RegisterFakeFunction(kName, code); | 
|  | Instance& result = Instance::Handle(); | 
|  | result ^= DartEntry::InvokeFunction(function, Object::empty_array()); | 
|  | EXPECT_EQ(Bool::True().ptr(), result.ptr()); | 
|  | } | 
|  |  | 
|  | }  // namespace dart | 
|  |  | 
|  | #endif  // defined TARGET_ARCH_IA32 |