Files
sdk/runtime/vm/code_descriptors_test.cc
T
Tess Strickland e2df4d30a0 [vm] Remove non-private uses of TokenPosition::value().
Instead, split each old use into the following cases:

* If the TokenPosition value is expected to be a real token position,
  then use TokenPosition::Pos().

* If the TokenPosition is being serialized in some way, then use
  TokenPosition::Serialize() and change the place where the
  TokenPosition is recreated to use TokenPosition::Deserialize().

* If the value of the TokenPosition is being printed for debugging
  purposes, then just use TokenPosition::ToCString() instead.

That is, we try to pin down when token positions are expected to
be real vs. when other types of token positions can be found.

Another source of possible error when using token positions is to
convert between synthetic and real token positions. In the past,
synthetic token positions may have been based off real token positions,
but that is no longer the case. Thus, all methods that allow
that conversion have been removed, and instead there is a new static
method for constructing synthetic tokens from valid nonces.

This CL also makes it so that Pos() and relational operators on token
positions are only defined on real token positions, to avoid any
assumptions about what the value encoded in synthetic positions mean. To
help with cases where non-real token positions may occur, four helper
methods are added:

* TokenPosition::Min(a, b): A static method that returns the smallest
  real token position provided. If neither `a` or `b` are real,
  returns `a`.

* TokenPosition::Max(a, b): A static method that returns the largest
  real token position provided. If neither `a` or `b` are real,
  returns `a`.

* TokenPosition::IsWithin(start, end): Determines whether `this` falls
  between `start` and `end` (inclusive). If `this` is non-real, then it
  must be either `start` or `end` if synthetic, otherwise false.
  Otherwise, we mimic the old style of range checking, which means that
  non-real starts and ends are treated as less than every real token.

* TokenPosition::CompareForSorting(other): Unlike the relational
  operators, provides a comparison between any types of token positions
  for purposes such as sorting.  Currently only used in the profiler.

It also changes TokenPosition::ToCString() to tag synthetic token
positions, so they can be distinguished from real ones at a glance.

TEST=Existing test suite on trybots, especially the observatory tests
which make heavy use of the debugger and the unit tests for the
profiler/source report modules.

Bug: https://github.com/dart-lang/sdk/issues/44436

Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-nnbd-linux-debug-x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-nnbd-linux-debug-x64-try,vm-kernel-linux-debug-x64-try,vm-kernel-linux-release-x64-try,vm-kernel-nnbd-linux-release-x64-try,vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-nnbd-linux-release-x64-try,vm-kernel-linux-product-x64-try,vm-kernel-precomp-linux-product-x64-try
Change-Id: Ic06aa0bc7a1f0fbac7257ed22ca5e7e0ccd7f3f2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/174924
Commit-Queue: Tess Strickland <sstrickl@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Daco Harkes <dacoharkes@google.com>
2020-12-16 08:27:32 +00:00

172 lines
5.8 KiB
C++

// 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());
Isolate::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 =
"class A {"
" static void func(var i, var k) native 'NativeFunc';"
" 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';"
" }\n"
"}\n";
// 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,
PcDescriptorsLayout::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 != NULL);
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(PcDescriptorsLayout::kRuntimeCall, 0, 0, tp, 0,
1);
}
const PcDescriptors& finalized_descriptors =
PcDescriptors::Handle(descriptors->FinalizePcDescriptors(0));
ASSERT(!finalized_descriptors.IsNull());
PcDescriptors::Iterator it(finalized_descriptors,
PcDescriptorsLayout::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