Files
sdk/runtime/lib/errors.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

229 lines
9.0 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 "vm/bootstrap_natives.h"
#include "vm/exceptions.h"
#include "vm/object_store.h"
#include "vm/runtime_entry.h"
#include "vm/stack_frame.h"
#include "vm/symbols.h"
namespace dart {
// Scan the stack until we hit the first function in the _AssertionError
// class. We then return the next frame's script taking inlining into account.
static ScriptPtr FindScript(DartFrameIterator* iterator) {
#if defined(DART_PRECOMPILED_RUNTIME)
// The precompiled runtime faces two issues in recovering the correct
// assertion text. First, the precompiled runtime does not include
// the inlining meta-data so we cannot walk the inline-aware stack trace.
// Second, the script text itself is missing so whatever script is returned
// from here will be missing the assertion expression text.
iterator->NextFrame(); // Skip _AssertionError._evaluateAssertion frame
return Exceptions::GetCallerScript(iterator);
#else
StackFrame* stack_frame = iterator->NextFrame();
Code& code = Code::Handle();
Function& func = Function::Handle();
const Class& assert_error_class =
Class::Handle(Library::LookupCoreClass(Symbols::AssertionError()));
ASSERT(!assert_error_class.IsNull());
bool hit_assertion_error = false;
for (; stack_frame != NULL; stack_frame = iterator->NextFrame()) {
code = stack_frame->LookupDartCode();
if (code.is_optimized()) {
InlinedFunctionsIterator inlined_iterator(code, stack_frame->pc());
while (!inlined_iterator.Done()) {
func = inlined_iterator.function();
if (hit_assertion_error) {
return func.script();
}
ASSERT(!hit_assertion_error);
hit_assertion_error = (func.Owner() == assert_error_class.raw());
inlined_iterator.Advance();
}
continue;
} else {
func = code.function();
}
ASSERT(!func.IsNull());
if (hit_assertion_error) {
return func.script();
}
ASSERT(!hit_assertion_error);
hit_assertion_error = (func.Owner() == assert_error_class.raw());
}
UNREACHABLE();
return Script::null();
#endif // defined(DART_PRECOMPILED_RUNTIME)
}
// Allocate and throw a new AssertionError.
// Arg0: index of the first token of the failed assertion.
// Arg1: index of the first token after the failed assertion.
// Arg2: Message object or null.
// Return value: none, throws an exception.
DEFINE_NATIVE_ENTRY(AssertionError_throwNew, 0, 3) {
// No need to type check the arguments. This function can only be called
// internally from the VM.
const TokenPosition assertion_start = TokenPosition::Deserialize(
Smi::CheckedHandle(zone, arguments->NativeArgAt(0)).Value());
const TokenPosition assertion_end = TokenPosition::Deserialize(
Smi::CheckedHandle(zone, arguments->NativeArgAt(1)).Value());
const Instance& message =
Instance::CheckedHandle(zone, arguments->NativeArgAt(2));
const Array& args = Array::Handle(zone, Array::New(5));
DartFrameIterator iterator(thread,
StackFrameIterator::kNoCrossThreadIteration);
iterator.NextFrame(); // Skip native call.
const Script& script = Script::Handle(FindScript(&iterator));
// Initialize argument 'failed_assertion' with source snippet.
intptr_t from_line, from_column;
script.GetTokenLocation(assertion_start, &from_line, &from_column);
intptr_t to_line, to_column;
script.GetTokenLocation(assertion_end, &to_line, &to_column);
// Extract the assertion condition text (if source is available).
auto& condition_text = String::Handle(
script.GetSnippet(from_line, from_column, to_line, to_column));
if (condition_text.IsNull()) {
condition_text = Symbols::OptimizedOut().raw();
}
args.SetAt(0, condition_text);
// Initialize location arguments starting at position 1.
// Do not set a column if the source has been generated as it will be wrong.
args.SetAt(1, String::Handle(script.url()));
args.SetAt(2, Smi::Handle(Smi::New(from_line)));
args.SetAt(3, Smi::Handle(Smi::New(script.HasSource() ? from_column : -1)));
args.SetAt(4, message);
Exceptions::ThrowByType(Exceptions::kAssertion, args);
UNREACHABLE();
return Object::null();
}
// Allocate and throw a new AssertionError.
// Arg0: Source code snippet of failed assertion.
// Arg1: Line number.
// Arg2: Column number.
// Arg3: Message object or null.
// Return value: none, throws an exception.
DEFINE_NATIVE_ENTRY(AssertionError_throwNewSource, 0, 4) {
// No need to type check the arguments. This function can only be called
// internally from the VM.
const String& failed_assertion =
String::CheckedHandle(zone, arguments->NativeArgAt(0));
const intptr_t line =
Smi::CheckedHandle(zone, arguments->NativeArgAt(1)).Value();
const intptr_t column =
Smi::CheckedHandle(zone, arguments->NativeArgAt(2)).Value();
const Instance& message =
Instance::CheckedHandle(zone, arguments->NativeArgAt(3));
const Array& args = Array::Handle(zone, Array::New(5));
DartFrameIterator iterator(thread,
StackFrameIterator::kNoCrossThreadIteration);
iterator.NextFrame(); // Skip native call.
const Script& script = Script::Handle(zone, FindScript(&iterator));
args.SetAt(0, failed_assertion);
args.SetAt(1, String::Handle(zone, script.url()));
args.SetAt(2, Smi::Handle(zone, Smi::New(line)));
args.SetAt(3, Smi::Handle(zone, Smi::New(column)));
args.SetAt(4, message);
Exceptions::ThrowByType(Exceptions::kAssertion, args);
UNREACHABLE();
return Object::null();
}
// Allocate and throw a new TypeError or CastError.
// Arg0: index of the token of the failed type check.
// Arg1: src value.
// Arg2: dst type.
// Arg3: dst name.
// Return value: none, throws an exception.
DEFINE_NATIVE_ENTRY(TypeError_throwNew, 0, 4) {
// No need to type check the arguments. This function can only be called
// internally from the VM.
const TokenPosition location = TokenPosition::Deserialize(
Smi::CheckedHandle(zone, arguments->NativeArgAt(0)).Value());
const Instance& src_value =
Instance::CheckedHandle(zone, arguments->NativeArgAt(1));
const AbstractType& dst_type =
AbstractType::CheckedHandle(zone, arguments->NativeArgAt(2));
const String& dst_name =
String::CheckedHandle(zone, arguments->NativeArgAt(3));
const AbstractType& src_type =
AbstractType::Handle(src_value.GetType(Heap::kNew));
Exceptions::CreateAndThrowTypeError(location, src_type, dst_type, dst_name);
UNREACHABLE();
return Object::null();
}
// Allocate and throw a new FallThroughError.
// Arg0: index of the case clause token into which we fall through.
// Return value: none, throws an exception.
DEFINE_NATIVE_ENTRY(FallThroughError_throwNew, 0, 1) {
GET_NON_NULL_NATIVE_ARGUMENT(Smi, smi_pos, arguments->NativeArgAt(0));
TokenPosition fallthrough_pos = TokenPosition::Deserialize(smi_pos.Value());
const Array& args = Array::Handle(Array::New(2));
// Initialize 'url' and 'line' arguments.
DartFrameIterator iterator(thread,
StackFrameIterator::kNoCrossThreadIteration);
iterator.NextFrame(); // Skip native call.
const Script& script = Script::Handle(Exceptions::GetCallerScript(&iterator));
args.SetAt(0, String::Handle(script.url()));
intptr_t line;
script.GetTokenLocation(fallthrough_pos, &line, NULL);
args.SetAt(1, Smi::Handle(Smi::New(line)));
Exceptions::ThrowByType(Exceptions::kFallThrough, args);
UNREACHABLE();
return Object::null();
}
// Allocate and throw a new AbstractClassInstantiationError.
// Arg0: Token position of allocation statement.
// Arg1: class name of the abstract class that cannot be instantiated.
// Return value: none, throws an exception.
DEFINE_NATIVE_ENTRY(AbstractClassInstantiationError_throwNew, 0, 2) {
GET_NON_NULL_NATIVE_ARGUMENT(Smi, smi_pos, arguments->NativeArgAt(0));
GET_NON_NULL_NATIVE_ARGUMENT(String, class_name, arguments->NativeArgAt(1));
TokenPosition error_pos = TokenPosition::Deserialize(smi_pos.Value());
const Array& args = Array::Handle(Array::New(3));
// Initialize 'className', 'url' and 'line' arguments.
DartFrameIterator iterator(thread,
StackFrameIterator::kNoCrossThreadIteration);
iterator.NextFrame(); // Skip native call.
const Script& script = Script::Handle(Exceptions::GetCallerScript(&iterator));
args.SetAt(0, class_name);
args.SetAt(1, String::Handle(script.url()));
intptr_t line;
script.GetTokenLocation(error_pos, &line, NULL);
args.SetAt(2, Smi::Handle(Smi::New(line)));
Exceptions::ThrowByType(Exceptions::kAbstractClassInstantiation, args);
UNREACHABLE();
return Object::null();
}
// Rethrow an error with a stacktrace.
DEFINE_NATIVE_ENTRY(Async_rethrow, 0, 2) {
GET_NON_NULL_NATIVE_ARGUMENT(Instance, error, arguments->NativeArgAt(0));
GET_NON_NULL_NATIVE_ARGUMENT(Instance, stacktrace, arguments->NativeArgAt(1));
Exceptions::ReThrow(thread, error, stacktrace);
return Object::null();
}
} // namespace dart