| 1 | // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 | // for details. All rights reserved. Use of this source code is governed by a |
| 3 | // BSD-style license that can be found in the LICENSE file. |
| 4 | |
| 5 | #include "platform/assert.h" |
| 6 | #include "vm/globals.h" |
| 7 | |
| 8 | #include "vm/code_descriptors.h" |
| 9 | #include "vm/compiler/assembler/assembler.h" |
| 10 | #include "vm/compiler/jit/compiler.h" |
| 11 | #include "vm/dart_entry.h" |
| 12 | #include "vm/native_entry.h" |
| 13 | #include "vm/parser.h" |
| 14 | #include "vm/symbols.h" |
| 15 | #include "vm/thread.h" |
| 16 | #include "vm/unit_test.h" |
| 17 | |
| 18 | namespace dart { |
| 19 | |
| 20 | static void NativeFunc(Dart_NativeArguments args) { |
| 21 | Dart_Handle i = Dart_GetNativeArgument(args, 0); |
| 22 | Dart_Handle k = Dart_GetNativeArgument(args, 1); |
| 23 | int64_t value = -1; |
| 24 | EXPECT_VALID(Dart_IntegerToInt64(i, &value)); |
| 25 | EXPECT_EQ(10, value); |
| 26 | EXPECT_VALID(Dart_IntegerToInt64(k, &value)); |
| 27 | EXPECT_EQ(20, value); |
| 28 | { |
| 29 | TransitionNativeToVM transition(Thread::Current()); |
| 30 | Isolate::Current()->heap()->CollectAllGarbage(); |
| 31 | } |
| 32 | } |
| 33 | |
| 34 | static Dart_NativeFunction native_resolver(Dart_Handle name, |
| 35 | int argument_count, |
| 36 | bool* auto_setup_scope) { |
| 37 | ASSERT(auto_setup_scope); |
| 38 | *auto_setup_scope = false; |
| 39 | return reinterpret_cast<Dart_NativeFunction>(&NativeFunc); |
| 40 | } |
| 41 | |
| 42 | TEST_CASE(StackMapGC) { |
| 43 | const char* kScriptChars = |
| 44 | "class A {" |
| 45 | " static void func(var i, var k) native 'NativeFunc';" |
| 46 | " static foo() {" |
| 47 | " var i;" |
| 48 | " var s1;" |
| 49 | " var k;" |
| 50 | " var s2;" |
| 51 | " var s3;" |
| 52 | " i = 10; s1 = 'abcd'; k = 20; s2 = 'B'; s3 = 'C';" |
| 53 | " func(i, k);" |
| 54 | " return i + k; }" |
| 55 | " static void moo() {" |
| 56 | " var i = A.foo();" |
| 57 | " if (i != 30) throw '$i != 30';" |
| 58 | " }\n" |
| 59 | "}\n" ; |
| 60 | // First setup the script and compile the script. |
| 61 | TestCase::LoadTestScript(kScriptChars, native_resolver); |
| 62 | TransitionNativeToVM transition(thread); |
| 63 | |
| 64 | EXPECT(ClassFinalizer::ProcessPendingClasses()); |
| 65 | const String& name = String::Handle(String::New(TestCase::url())); |
| 66 | const Library& lib = Library::Handle(Library::LookupLibrary(thread, name)); |
| 67 | EXPECT(!lib.IsNull()); |
| 68 | Class& cls = |
| 69 | Class::Handle(lib.LookupClass(String::Handle(Symbols::New(thread, "A" )))); |
| 70 | EXPECT(!cls.IsNull()); |
| 71 | |
| 72 | // Now compile the two functions 'A.foo' and 'A.moo' |
| 73 | String& function_moo_name = String::Handle(String::New("moo" )); |
| 74 | Function& function_moo = |
| 75 | Function::Handle(cls.LookupStaticFunction(function_moo_name)); |
| 76 | EXPECT(CompilerTest::TestCompileFunction(function_moo)); |
| 77 | EXPECT(function_moo.HasCode()); |
| 78 | |
| 79 | String& function_foo_name = String::Handle(String::New("foo" )); |
| 80 | Function& function_foo = |
| 81 | Function::Handle(cls.LookupStaticFunction(function_foo_name)); |
| 82 | EXPECT(CompilerTest::TestCompileFunction(function_foo)); |
| 83 | EXPECT(function_foo.HasCode()); |
| 84 | |
| 85 | // Build and setup a stackmap for the call to 'func' in 'A.foo' in order |
| 86 | // to test the traversal of stack maps when a GC happens. |
| 87 | BitmapBuilder* stack_bitmap = new BitmapBuilder(); |
| 88 | EXPECT(stack_bitmap != nullptr); |
| 89 | stack_bitmap->Set(0, false); // var i. |
| 90 | stack_bitmap->Set(1, true); // var s1. |
| 91 | stack_bitmap->Set(2, false); // var k. |
| 92 | stack_bitmap->Set(3, true); // var s2. |
| 93 | stack_bitmap->Set(4, true); // var s3. |
| 94 | const Code& code = Code::Handle(function_foo.unoptimized_code()); |
| 95 | // Search for the pc of the call to 'func'. |
| 96 | const PcDescriptors& descriptors = |
| 97 | PcDescriptors::Handle(code.pc_descriptors()); |
| 98 | int call_count = 0; |
| 99 | PcDescriptors::Iterator iter(descriptors, |
| 100 | PcDescriptorsLayout::kUnoptStaticCall); |
| 101 | CompressedStackMapsBuilder compressed_maps_builder; |
| 102 | while (iter.MoveNext()) { |
| 103 | compressed_maps_builder.AddEntry(iter.PcOffset(), stack_bitmap, 0); |
| 104 | ++call_count; |
| 105 | } |
| 106 | // We can't easily check that we put the stackmap at the correct pc, but |
| 107 | // we did if there was exactly one call seen. |
| 108 | EXPECT(call_count == 1); |
| 109 | const auto& compressed_maps = |
| 110 | CompressedStackMaps::Handle(compressed_maps_builder.Finalize()); |
| 111 | code.set_compressed_stackmaps(compressed_maps); |
| 112 | |
| 113 | // Now invoke 'A.moo' and it will trigger a GC when the native function |
| 114 | // is called, this should then cause the stack map of function 'A.foo' |
| 115 | // to be traversed and the appropriate objects visited. |
| 116 | const Object& result = Object::Handle( |
| 117 | DartEntry::InvokeFunction(function_foo, Object::empty_array())); |
| 118 | EXPECT(!result.IsError()); |
| 119 | } |
| 120 | |
| 121 | ISOLATE_UNIT_TEST_CASE(DescriptorList_TokenPositions) { |
| 122 | DescriptorList* descriptors = new DescriptorList(64); |
| 123 | ASSERT(descriptors != NULL); |
| 124 | const intptr_t token_positions[] = { |
| 125 | kMinInt32, |
| 126 | 5, |
| 127 | 13, |
| 128 | 13, |
| 129 | 13, |
| 130 | 13, |
| 131 | 31, |
| 132 | 23, |
| 133 | 23, |
| 134 | 23, |
| 135 | 33, |
| 136 | 33, |
| 137 | 5, |
| 138 | 5, |
| 139 | TokenPosition::kMinSourcePos, |
| 140 | TokenPosition::kMaxSourcePos, |
| 141 | }; |
| 142 | const intptr_t num_token_positions = |
| 143 | sizeof(token_positions) / sizeof(token_positions[0]); |
| 144 | |
| 145 | for (intptr_t i = 0; i < num_token_positions; i++) { |
| 146 | descriptors->AddDescriptor(PcDescriptorsLayout::kRuntimeCall, 0, 0, |
| 147 | TokenPosition(token_positions[i]), 0, 1); |
| 148 | } |
| 149 | |
| 150 | const PcDescriptors& finalized_descriptors = |
| 151 | PcDescriptors::Handle(descriptors->FinalizePcDescriptors(0)); |
| 152 | |
| 153 | ASSERT(!finalized_descriptors.IsNull()); |
| 154 | PcDescriptors::Iterator it(finalized_descriptors, |
| 155 | PcDescriptorsLayout::kRuntimeCall); |
| 156 | |
| 157 | intptr_t i = 0; |
| 158 | while (it.MoveNext()) { |
| 159 | if (token_positions[i] != it.TokenPos().value()) { |
| 160 | OS::PrintErr("[%" Pd "]: Expected: %" Pd " != %" Pd "\n" , i, |
| 161 | token_positions[i], it.TokenPos().value()); |
| 162 | } |
| 163 | EXPECT(token_positions[i] == it.TokenPos().value()); |
| 164 | i++; |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | } // namespace dart |
| 169 | |