| // 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(i, 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 |