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