|  | // 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 |