| // 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" |
| |
| #include "vm/code_descriptors.h" |
| #include "vm/compiler/assembler/assembler.h" |
| #include "vm/compiler/jit/compiler.h" |
| #include "vm/dart_entry.h" |
| #include "vm/native_entry.h" |
| #include "vm/parser.h" |
| #include "vm/symbols.h" |
| #include "vm/thread.h" |
| #include "vm/unit_test.h" |
| |
| namespace dart { |
| |
| static void NativeFunc(Dart_NativeArguments args) { |
| Dart_Handle i = Dart_GetNativeArgument(args, 0); |
| Dart_Handle k = Dart_GetNativeArgument(args, 1); |
| int64_t value = -1; |
| EXPECT_VALID(Dart_IntegerToInt64(i, &value)); |
| EXPECT_EQ(10, value); |
| EXPECT_VALID(Dart_IntegerToInt64(k, &value)); |
| EXPECT_EQ(20, value); |
| { |
| TransitionNativeToVM transition(Thread::Current()); |
| IsolateGroup::Current()->heap()->CollectAllGarbage(); |
| } |
| } |
| |
| static Dart_NativeFunction native_resolver(Dart_Handle name, |
| int argument_count, |
| bool* auto_setup_scope) { |
| ASSERT(auto_setup_scope); |
| *auto_setup_scope = false; |
| return NativeFunc; |
| } |
| |
| TEST_CASE(StackMapGC) { |
| const char* kScriptChars = R"( |
| class A { |
| @pragma("vm:external-name", "NativeFunc") |
| external static void func(var i, var k); |
| static foo() { |
| var i; |
| var s1; |
| var k; |
| var s2; |
| var s3; |
| i = 10; s1 = 'abcd'; k = 20; s2 = 'B'; s3 = 'C'; |
| func(i, k); |
| return i + k; } |
| static void moo() { |
| var i = A.foo(); |
| if (i != 30) throw '$i != 30'; |
| } |
| })"; |
| // First setup the script and compile the script. |
| TestCase::LoadTestScript(kScriptChars, native_resolver); |
| TransitionNativeToVM transition(thread); |
| |
| EXPECT(ClassFinalizer::ProcessPendingClasses()); |
| const String& name = String::Handle(String::New(TestCase::url())); |
| const Library& lib = Library::Handle(Library::LookupLibrary(thread, name)); |
| EXPECT(!lib.IsNull()); |
| Class& cls = |
| Class::Handle(lib.LookupClass(String::Handle(Symbols::New(thread, "A")))); |
| EXPECT(!cls.IsNull()); |
| |
| // Now compile the two functions 'A.foo' and 'A.moo' |
| String& function_moo_name = String::Handle(String::New("moo")); |
| const auto& error = cls.EnsureIsFinalized(thread); |
| EXPECT(error == Error::null()); |
| Function& function_moo = |
| Function::Handle(cls.LookupStaticFunction(function_moo_name)); |
| EXPECT(CompilerTest::TestCompileFunction(function_moo)); |
| EXPECT(function_moo.HasCode()); |
| |
| String& function_foo_name = String::Handle(String::New("foo")); |
| Function& function_foo = |
| Function::Handle(cls.LookupStaticFunction(function_foo_name)); |
| EXPECT(CompilerTest::TestCompileFunction(function_foo)); |
| EXPECT(function_foo.HasCode()); |
| |
| // Build and setup a stackmap for the call to 'func' in 'A.foo' in order |
| // to test the traversal of stack maps when a GC happens. |
| BitmapBuilder* stack_bitmap = new BitmapBuilder(); |
| EXPECT(stack_bitmap != nullptr); |
| stack_bitmap->Set(0, false); // var i. |
| stack_bitmap->Set(1, true); // var s1. |
| stack_bitmap->Set(2, false); // var k. |
| stack_bitmap->Set(3, true); // var s2. |
| stack_bitmap->Set(4, true); // var s3. |
| const Code& code = Code::Handle(function_foo.unoptimized_code()); |
| // Search for the pc of the call to 'func'. |
| const PcDescriptors& descriptors = |
| PcDescriptors::Handle(code.pc_descriptors()); |
| int call_count = 0; |
| PcDescriptors::Iterator iter(descriptors, |
| UntaggedPcDescriptors::kUnoptStaticCall); |
| CompressedStackMapsBuilder compressed_maps_builder(thread->zone()); |
| while (iter.MoveNext()) { |
| compressed_maps_builder.AddEntry(iter.PcOffset(), stack_bitmap, 0); |
| ++call_count; |
| } |
| // We can't easily check that we put the stackmap at the correct pc, but |
| // we did if there was exactly one call seen. |
| EXPECT(call_count == 1); |
| const auto& compressed_maps = |
| CompressedStackMaps::Handle(compressed_maps_builder.Finalize()); |
| code.set_compressed_stackmaps(compressed_maps); |
| |
| // Now invoke 'A.moo' and it will trigger a GC when the native function |
| // is called, this should then cause the stack map of function 'A.foo' |
| // to be traversed and the appropriate objects visited. |
| const Object& result = Object::Handle( |
| DartEntry::InvokeFunction(function_foo, Object::empty_array())); |
| EXPECT(!result.IsError()); |
| } |
| |
| ISOLATE_UNIT_TEST_CASE(DescriptorList_TokenPositions) { |
| DescriptorList* descriptors = new DescriptorList(thread->zone()); |
| ASSERT(descriptors != nullptr); |
| const int32_t token_positions[] = { |
| kMinInt32, |
| 5, |
| 13, |
| 13, |
| 13, |
| 13, |
| 31, |
| 23, |
| 23, |
| 23, |
| 33, |
| 33, |
| 5, |
| 5, |
| TokenPosition::kMinSourcePos, |
| TokenPosition::kMaxSourcePos, |
| }; |
| const intptr_t num_token_positions = ARRAY_SIZE(token_positions); |
| |
| for (intptr_t i = 0; i < num_token_positions; i++) { |
| const TokenPosition& tp = TokenPosition::Deserialize(token_positions[i]); |
| descriptors->AddDescriptor(UntaggedPcDescriptors::kRuntimeCall, 0, 0, tp, 0, |
| 1); |
| } |
| |
| const PcDescriptors& finalized_descriptors = |
| PcDescriptors::Handle(descriptors->FinalizePcDescriptors(0)); |
| |
| ASSERT(!finalized_descriptors.IsNull()); |
| PcDescriptors::Iterator it(finalized_descriptors, |
| UntaggedPcDescriptors::kRuntimeCall); |
| |
| intptr_t i = 0; |
| while (it.MoveNext()) { |
| const TokenPosition& tp = TokenPosition::Deserialize(token_positions[i]); |
| if (tp != it.TokenPos()) { |
| OS::PrintErr("[%" Pd "]: Expected: %s != %s\n", i, tp.ToCString(), |
| it.TokenPos().ToCString()); |
| } |
| EXPECT(tp == it.TokenPos()); |
| i++; |
| } |
| } |
| |
| } // namespace dart |