[VM/compiler] Emit a throw NoSuchMethodError for bad arity rather than aborting compilation.
The bad arity maybe detected after an isolate reload or when using strong mode with a mix of opted in and opted out libraries. The thrown NoSuchMethodError contains enough information to be useful, but does not mirror the complete invocation (e.g. arguments). This CL does not fix https://github.com/dart-lang/sdk/issues/37517 where the target is missing after a reload (not just bad arity). The VM implementation of NoSuchMethodError is cleaned up, but the deprecated constructor 'NoSuchMethodError(...)' is not yet removed, since it is still documented. Change-Id: I0306971c59cb510d21cb1b1acc3545c8817dfea7 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/146986 Reviewed-by: Alexander Markov <alexmarkov@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com> Commit-Queue: Régis Crelier <regis@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
f4f0ba4b36
commit
eae248cc41
@@ -54,14 +54,14 @@ static void ThrowNoSuchMethod(const Instance& receiver,
|
||||
const Smi& invocation_type =
|
||||
Smi::Handle(Smi::New(InvocationMirror::EncodeType(level, kind)));
|
||||
|
||||
const Array& args = Array::Handle(Array::New(6));
|
||||
const Array& args = Array::Handle(Array::New(7));
|
||||
args.SetAt(0, receiver);
|
||||
args.SetAt(1, function_name);
|
||||
args.SetAt(2, invocation_type);
|
||||
// TODO(regis): Support invocation of generic functions with type arguments.
|
||||
args.SetAt(3, Object::null_type_arguments());
|
||||
args.SetAt(4, arguments);
|
||||
args.SetAt(5, argument_names);
|
||||
args.SetAt(3, Object::smi_zero()); // Type arguments length.
|
||||
args.SetAt(4, Object::null_type_arguments());
|
||||
args.SetAt(5, arguments);
|
||||
args.SetAt(6, argument_names);
|
||||
|
||||
const Library& libcore = Library::Handle(Library::CoreLibrary());
|
||||
const Class& NoSuchMethodError =
|
||||
|
||||
@@ -1685,8 +1685,9 @@ Fragment StreamingFlowGraphBuilder::RethrowException(TokenPosition position,
|
||||
return flow_graph_builder_->RethrowException(position, catch_try_index);
|
||||
}
|
||||
|
||||
Fragment StreamingFlowGraphBuilder::ThrowNoSuchMethodError() {
|
||||
return flow_graph_builder_->ThrowNoSuchMethodError();
|
||||
Fragment StreamingFlowGraphBuilder::ThrowNoSuchMethodError(
|
||||
const Function& target) {
|
||||
return flow_graph_builder_->ThrowNoSuchMethodError(target);
|
||||
}
|
||||
|
||||
Fragment StreamingFlowGraphBuilder::Constant(const Object& value) {
|
||||
@@ -1715,32 +1716,12 @@ Fragment StreamingFlowGraphBuilder::CheckNull(
|
||||
clear_the_temp);
|
||||
}
|
||||
|
||||
static void BadArity(const Script& script,
|
||||
TokenPosition position,
|
||||
const String& error_message) {
|
||||
#ifndef PRODUCT
|
||||
// TODO(https://github.com/dart-lang/sdk/issues/37517): Should emit code to
|
||||
// throw a NoSuchMethodError.
|
||||
// Correct arity is checked at compile time by CFE. However, an isolate reload
|
||||
// after an arity change may lead here.
|
||||
// Using strong mode with a mix of opted in and opted out libraries may
|
||||
// also result in this error undetected by CFE.
|
||||
Isolate* isolate = Isolate::Current();
|
||||
ASSERT(isolate->HasAttemptedReload() || isolate->null_safety());
|
||||
Report::MessageF(Report::kError, script, position, Report::AtLocation,
|
||||
"Static call with invalid arguments: %s",
|
||||
error_message.ToCString());
|
||||
#endif
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
Fragment StreamingFlowGraphBuilder::StaticCall(TokenPosition position,
|
||||
const Function& target,
|
||||
intptr_t argument_count,
|
||||
ICData::RebindRule rebind_rule) {
|
||||
String& error_message = String::Handle();
|
||||
if (!target.AreValidArgumentCounts(0, argument_count, 0, &error_message)) {
|
||||
BadArity(script_, position, error_message);
|
||||
if (!target.AreValidArgumentCounts(0, argument_count, 0, nullptr)) {
|
||||
return flow_graph_builder_->ThrowNoSuchMethodError(target);
|
||||
}
|
||||
return flow_graph_builder_->StaticCall(position, target, argument_count,
|
||||
rebind_rule);
|
||||
@@ -1755,10 +1736,9 @@ Fragment StreamingFlowGraphBuilder::StaticCall(
|
||||
const InferredTypeMetadata* result_type,
|
||||
intptr_t type_args_count,
|
||||
bool use_unchecked_entry) {
|
||||
String& error_message = String::Handle();
|
||||
if (!target.AreValidArguments(type_args_count, argument_count, argument_names,
|
||||
&error_message)) {
|
||||
BadArity(script_, position, error_message);
|
||||
nullptr)) {
|
||||
return flow_graph_builder_->ThrowNoSuchMethodError(target);
|
||||
}
|
||||
return flow_graph_builder_->StaticCall(
|
||||
position, target, argument_count, argument_names, rebind_rule,
|
||||
|
||||
@@ -166,7 +166,7 @@ class StreamingFlowGraphBuilder : public KernelReaderHelper {
|
||||
intptr_t yield_index = PcDescriptorsLayout::kInvalidYieldIndex);
|
||||
Fragment EvaluateAssertion();
|
||||
Fragment RethrowException(TokenPosition position, int catch_try_index);
|
||||
Fragment ThrowNoSuchMethodError();
|
||||
Fragment ThrowNoSuchMethodError(const Function& target);
|
||||
Fragment Constant(const Object& value);
|
||||
Fragment IntConstant(int64_t value);
|
||||
Fragment LoadStaticField(const Field& field);
|
||||
|
||||
@@ -392,7 +392,7 @@ Fragment FlowGraphBuilder::ThrowException(TokenPosition position) {
|
||||
instructions +=
|
||||
Fragment(new (Z) ThrowInstr(position, GetNextDeoptId(), exception))
|
||||
.closed();
|
||||
// Use it's side effect of leaving a constant on the stack (does not change
|
||||
// Use its side effect of leaving a constant on the stack (does not change
|
||||
// the graph).
|
||||
NullConstant();
|
||||
|
||||
@@ -408,7 +408,7 @@ Fragment FlowGraphBuilder::RethrowException(TokenPosition position,
|
||||
Fragment(new (Z) ReThrowInstr(position, catch_try_index, GetNextDeoptId(),
|
||||
exception, stacktrace))
|
||||
.closed();
|
||||
// Use it's side effect of leaving a constant on the stack (does not change
|
||||
// Use its side effect of leaving a constant on the stack (does not change
|
||||
// the graph).
|
||||
NullConstant();
|
||||
|
||||
@@ -678,7 +678,7 @@ Fragment FlowGraphBuilder::ThrowTypeError() {
|
||||
return instructions;
|
||||
}
|
||||
|
||||
Fragment FlowGraphBuilder::ThrowNoSuchMethodError() {
|
||||
Fragment FlowGraphBuilder::ThrowNoSuchMethodError(const Function& target) {
|
||||
const Class& klass = Class::ZoneHandle(
|
||||
Z, Library::LookupCoreClass(Symbols::NoSuchMethodError()));
|
||||
ASSERT(!klass.IsNull());
|
||||
@@ -688,20 +688,36 @@ Fragment FlowGraphBuilder::ThrowNoSuchMethodError() {
|
||||
|
||||
Fragment instructions;
|
||||
|
||||
// Call NoSuchMethodError._throwNew static function.
|
||||
instructions += NullConstant(); // receiver
|
||||
const Class& owner = Class::Handle(Z, target.Owner());
|
||||
AbstractType& receiver = AbstractType::ZoneHandle();
|
||||
InvocationMirror::Kind kind = InvocationMirror::Kind::kMethod;
|
||||
InvocationMirror::Level level;
|
||||
if (owner.IsTopLevel()) {
|
||||
level = InvocationMirror::Level::kTopLevel;
|
||||
} else {
|
||||
receiver = owner.RareType();
|
||||
if (target.kind() == FunctionLayout::kConstructor) {
|
||||
level = InvocationMirror::Level::kConstructor;
|
||||
} else {
|
||||
level = InvocationMirror::Level::kStatic;
|
||||
}
|
||||
}
|
||||
|
||||
instructions +=
|
||||
Constant(H.DartString("<unknown>", Heap::kOld)); // memberName
|
||||
instructions += IntConstant(-1); // invocation_type
|
||||
instructions += NullConstant(); // type arguments
|
||||
instructions += NullConstant(); // arguments
|
||||
instructions += NullConstant(); // argumentNames
|
||||
// Call NoSuchMethodError._throwNew static function.
|
||||
instructions += Constant(receiver); // receiver
|
||||
instructions += Constant(String::ZoneHandle(Z, target.name())); // memberName
|
||||
instructions += IntConstant(InvocationMirror::EncodeType(level, kind));
|
||||
instructions += IntConstant(0); // type arguments length
|
||||
instructions += NullConstant(); // type arguments
|
||||
instructions += NullConstant(); // arguments
|
||||
instructions += NullConstant(); // argumentNames
|
||||
|
||||
instructions += StaticCall(TokenPosition::kNoSource, throw_function,
|
||||
/* argument_count = */ 6, ICData::kStatic);
|
||||
// Leave "result" on the stack since callers expect it to be there (even
|
||||
// though the function will result in an exception).
|
||||
/* argument_count = */ 7, ICData::kStatic);
|
||||
|
||||
// Properly close graph with a ThrowInstr, although it is not executed.
|
||||
instructions += ThrowException(TokenPosition::kNoSource);
|
||||
instructions += Drop();
|
||||
|
||||
return instructions;
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ class FlowGraphBuilder : public BaseFlowGraphBuilder {
|
||||
bool use_unchecked_entry = false);
|
||||
Fragment StringInterpolateSingle(TokenPosition position);
|
||||
Fragment ThrowTypeError();
|
||||
Fragment ThrowNoSuchMethodError();
|
||||
Fragment ThrowNoSuchMethodError(const Function& target);
|
||||
Fragment ThrowLateInitializationError(TokenPosition position,
|
||||
const String& name);
|
||||
Fragment BuildImplicitClosureCreation(const Function& target);
|
||||
|
||||
@@ -4516,7 +4516,9 @@ TEST_CASE(IsolateReload_StaticTargetArityChange) {
|
||||
lib = TestCase::ReloadTestScript(kReloadScript);
|
||||
EXPECT_VALID(lib);
|
||||
EXPECT_ERROR(SimpleInvokeError(lib, "main"),
|
||||
"Static call with invalid arguments");
|
||||
"Unhandled exception:\n"
|
||||
"NoSuchMethodError: No constructor 'A.' "
|
||||
"with matching arguments declared in class 'A'.");
|
||||
}
|
||||
|
||||
#endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
@@ -3907,14 +3907,14 @@ static ObjectPtr ThrowNoSuchMethod(const Instance& receiver,
|
||||
const Smi& invocation_type =
|
||||
Smi::Handle(Smi::New(InvocationMirror::EncodeType(level, kind)));
|
||||
|
||||
const Array& args = Array::Handle(Array::New(6));
|
||||
const Array& args = Array::Handle(Array::New(7));
|
||||
args.SetAt(0, receiver);
|
||||
args.SetAt(1, function_name);
|
||||
args.SetAt(2, invocation_type);
|
||||
// TODO(regis): Support invocation of generic functions with type arguments.
|
||||
args.SetAt(3, Object::null_type_arguments());
|
||||
args.SetAt(4, arguments);
|
||||
args.SetAt(5, argument_names);
|
||||
args.SetAt(3, Object::smi_zero()); // Type arguments length.
|
||||
args.SetAt(4, Object::null_type_arguments());
|
||||
args.SetAt(5, arguments);
|
||||
args.SetAt(6, argument_names);
|
||||
|
||||
const Library& libcore = Library::Handle(Library::CoreLibrary());
|
||||
const Class& NoSuchMethodError =
|
||||
|
||||
@@ -179,13 +179,14 @@ static void NullErrorHelper(Zone* zone, const String& selector) {
|
||||
zone,
|
||||
Smi::New(InvocationMirror::EncodeType(InvocationMirror::kDynamic, kind)));
|
||||
|
||||
const Array& args = Array::Handle(zone, Array::New(6));
|
||||
const Array& args = Array::Handle(zone, Array::New(7));
|
||||
args.SetAt(0, /* instance */ Object::null_object());
|
||||
args.SetAt(1, selector);
|
||||
args.SetAt(2, invocation_type);
|
||||
args.SetAt(3, /* func_type_args */ Object::null_object());
|
||||
args.SetAt(4, /* func_args */ Object::null_object());
|
||||
args.SetAt(5, /* func_arg_names */ Object::null_object());
|
||||
args.SetAt(3, /* func_type_args_length */ Object::smi_zero());
|
||||
args.SetAt(4, /* func_type_args */ Object::null_object());
|
||||
args.SetAt(5, /* func_args */ Object::null_object());
|
||||
args.SetAt(6, /* func_arg_names */ Object::null_object());
|
||||
Exceptions::ThrowByType(Exceptions::kNoSuchMethod, args);
|
||||
}
|
||||
|
||||
|
||||
@@ -180,12 +180,6 @@ class AbstractClassInstantiationError {
|
||||
|
||||
@patch
|
||||
class NoSuchMethodError {
|
||||
// Deprecated members to be removed.
|
||||
Symbol _memberName;
|
||||
List _arguments;
|
||||
Map<Symbol, dynamic> _namedArguments;
|
||||
List _existingArgumentNames;
|
||||
|
||||
final Object _receiver;
|
||||
final Invocation _invocation;
|
||||
|
||||
@@ -202,44 +196,29 @@ class NoSuchMethodError {
|
||||
// method at compile time. The receiver is actually the literal class of the
|
||||
// unresolved method.
|
||||
@pragma("vm:entry-point", "call")
|
||||
static void _throwNew(Object receiver, String memberName, int invocation_type,
|
||||
Object typeArguments, List arguments, List argumentNames) {
|
||||
throw new NoSuchMethodError._withType(receiver, memberName, invocation_type,
|
||||
typeArguments, arguments, argumentNames);
|
||||
}
|
||||
|
||||
static void _throwNewIfNotLoaded(
|
||||
_LibraryPrefix prefix,
|
||||
static void _throwNew(
|
||||
Object receiver,
|
||||
String memberName,
|
||||
int invocation_type,
|
||||
int invocationType,
|
||||
int typeArgumentsLength,
|
||||
Object typeArguments,
|
||||
List arguments,
|
||||
List argumentNames) {
|
||||
if (!prefix.isLoaded()) {
|
||||
_throwNew(receiver, memberName, invocation_type, typeArguments, arguments,
|
||||
argumentNames);
|
||||
}
|
||||
throw new NoSuchMethodError._withType(receiver, memberName, invocationType,
|
||||
typeArgumentsLength, typeArguments, arguments, argumentNames);
|
||||
}
|
||||
|
||||
// TODO(regis): Deprecated member still used by dart2js to be removed.
|
||||
// Remember the type from the invocation mirror or static compilation
|
||||
// analysis when thrown directly with _throwNew. A negative value means
|
||||
// that no information is available.
|
||||
int _invocation_type;
|
||||
|
||||
// TODO(regis): Deprecated constructor still used by dart2js to be removed.
|
||||
// Deprecated constructor.
|
||||
@patch
|
||||
NoSuchMethodError(Object receiver, Symbol memberName,
|
||||
List positionalArguments, Map<Symbol, dynamic> namedArguments,
|
||||
[List existingArgumentNames = null])
|
||||
: _receiver = receiver,
|
||||
_invocation = null,
|
||||
_memberName = memberName,
|
||||
_arguments = positionalArguments,
|
||||
_namedArguments = namedArguments,
|
||||
_existingArgumentNames = existingArgumentNames,
|
||||
_invocation_type = -1;
|
||||
NoSuchMethodError(this._receiver, Symbol memberName, List positionalArguments,
|
||||
Map<Symbol, dynamic> namedArguments,
|
||||
[List existingArgumentNames = null]) // existingArgumentNames ignored.
|
||||
: this._invocation = new _InvocationMirror._withType(
|
||||
memberName,
|
||||
_InvocationMirror._UNINITIALIZED,
|
||||
null, // Type arguments not supported in deprecated constructor.
|
||||
positionalArguments,
|
||||
namedArguments);
|
||||
|
||||
// Helper to build a map of named arguments.
|
||||
static Map<Symbol, dynamic> _NamedArgumentsMap(
|
||||
@@ -247,8 +226,8 @@ class NoSuchMethodError {
|
||||
Map<Symbol, dynamic> namedArguments = new Map<Symbol, dynamic>();
|
||||
int numPositionalArguments = arguments.length - argumentNames.length;
|
||||
for (int i = 0; i < argumentNames.length; i++) {
|
||||
var arg_value = arguments[numPositionalArguments + i];
|
||||
namedArguments[new Symbol(argumentNames[i])] = arg_value;
|
||||
final argValue = arguments[numPositionalArguments + i];
|
||||
namedArguments[new Symbol(argumentNames[i])] = argValue;
|
||||
}
|
||||
return namedArguments;
|
||||
}
|
||||
@@ -261,17 +240,16 @@ class NoSuchMethodError {
|
||||
NoSuchMethodError._withType(
|
||||
this._receiver,
|
||||
String memberName,
|
||||
int invocation_type,
|
||||
int invocationType,
|
||||
int typeArgumentsLength, // Needed with all-dynamic (null) typeArguments.
|
||||
Object typeArguments,
|
||||
List arguments,
|
||||
List argumentNames)
|
||||
: this._invocation = new _InvocationMirror._withType(
|
||||
new Symbol(memberName),
|
||||
invocation_type,
|
||||
typeArguments != null
|
||||
// TODO(33073): Use actual count of type arguments in place of 0.
|
||||
? _InvocationMirror._unpackTypeArguments(typeArguments, 0)
|
||||
: null,
|
||||
invocationType,
|
||||
_InvocationMirror._unpackTypeArguments(
|
||||
typeArguments, typeArgumentsLength),
|
||||
argumentNames != null
|
||||
? arguments.sublist(0, arguments.length - argumentNames.length)
|
||||
: arguments,
|
||||
@@ -284,12 +262,7 @@ class NoSuchMethodError {
|
||||
|
||||
@patch
|
||||
String toString() {
|
||||
// TODO(regis): Remove this null check once dart2js is updated.
|
||||
var invocation = _invocation;
|
||||
if (invocation == null) {
|
||||
// Use deprecated version of toString.
|
||||
return _toStringDeprecated();
|
||||
}
|
||||
if (invocation is _InvocationMirror) {
|
||||
String memberName =
|
||||
internal.Symbol.computeUnmangledName(invocation.memberName);
|
||||
@@ -341,7 +314,7 @@ class NoSuchMethodError {
|
||||
_existingMethodSignature(_receiver, memberName, invocation._type);
|
||||
String argsMsg = existingSig != null ? " with matching arguments" : "";
|
||||
|
||||
String kindBuf;
|
||||
String kindBuf = "function";
|
||||
if (kind >= 0 && kind < 5) {
|
||||
kindBuf = (const [
|
||||
"method",
|
||||
@@ -452,7 +425,8 @@ class NoSuchMethodError {
|
||||
var name = _symbolToString(invocation.memberName);
|
||||
var receiverType = "${receiver.runtimeType}";
|
||||
if (invocation.isAccessor) {
|
||||
return "NoSuchMethodError: $receiverType has no $name ${invocation.isGetter ? "getter" : "setter"}";
|
||||
return "NoSuchMethodError: $receiverType has no $name "
|
||||
"${invocation.isGetter ? "getter" : "setter"}";
|
||||
}
|
||||
var buffer = StringBuffer("NoSuchMethodError")..write(": ");
|
||||
buffer.write("$receiverType has no $name method accepting arguments ");
|
||||
@@ -490,145 +464,6 @@ class NoSuchMethodError {
|
||||
}
|
||||
return "$symbol";
|
||||
}
|
||||
|
||||
// TODO(regis): Remove this function once dart2js is updated.
|
||||
String _toStringDeprecated() {
|
||||
var level = (_invocation_type >> _InvocationMirror._LEVEL_SHIFT) &
|
||||
_InvocationMirror._LEVEL_MASK;
|
||||
var type = _invocation_type & _InvocationMirror._KIND_MASK;
|
||||
String memberName = (_memberName == null)
|
||||
? ""
|
||||
: internal.Symbol.computeUnmangledName(_memberName);
|
||||
|
||||
if (type == _InvocationMirror._LOCAL_VAR) {
|
||||
return "NoSuchMethodError: Cannot assign to final variable '$memberName'";
|
||||
}
|
||||
|
||||
StringBuffer arguments = new StringBuffer();
|
||||
int argumentCount = 0;
|
||||
if (_arguments != null) {
|
||||
for (; argumentCount < _arguments.length; argumentCount++) {
|
||||
if (argumentCount > 0) {
|
||||
arguments.write(", ");
|
||||
}
|
||||
arguments.write(Error.safeToString(_arguments[argumentCount]));
|
||||
}
|
||||
}
|
||||
if (_namedArguments != null) {
|
||||
_namedArguments.forEach((Symbol key, var value) {
|
||||
if (argumentCount > 0) {
|
||||
arguments.write(", ");
|
||||
}
|
||||
arguments.write(internal.Symbol.computeUnmangledName(key));
|
||||
arguments.write(": ");
|
||||
arguments.write(Error.safeToString(value));
|
||||
argumentCount++;
|
||||
});
|
||||
}
|
||||
bool argsMismatch = _existingArgumentNames != null;
|
||||
String argsMessage = argsMismatch ? " with matching arguments" : "";
|
||||
|
||||
String type_str;
|
||||
if (type >= 0 && type < 5) {
|
||||
type_str = (const [
|
||||
"method",
|
||||
"getter",
|
||||
"setter",
|
||||
"getter or setter",
|
||||
"variable"
|
||||
])[type];
|
||||
}
|
||||
|
||||
StringBuffer msg_buf = new StringBuffer("NoSuchMethodError: ");
|
||||
bool is_type_call = false;
|
||||
switch (level) {
|
||||
case _InvocationMirror._DYNAMIC:
|
||||
{
|
||||
if (_receiver == null) {
|
||||
if (argsMismatch) {
|
||||
msg_buf.writeln("The null object does not have a $type_str "
|
||||
"'$memberName'$argsMessage.");
|
||||
} else {
|
||||
msg_buf
|
||||
.writeln("The $type_str '$memberName' was called on null.");
|
||||
}
|
||||
} else {
|
||||
if (_receiver is _Closure) {
|
||||
msg_buf.writeln("Closure call with mismatched arguments: "
|
||||
"function '$memberName'");
|
||||
} else if (_receiver is _Type && memberName == "call") {
|
||||
is_type_call = true;
|
||||
String name = _receiver.toString();
|
||||
msg_buf.writeln("Attempted to use type '$name' as a function. "
|
||||
"Since types do not define a method 'call', this is not "
|
||||
"possible. Did you intend to call the $name constructor and "
|
||||
"forget the 'new' operator?");
|
||||
} else {
|
||||
msg_buf
|
||||
.writeln("Class '${_receiver.runtimeType}' has no instance "
|
||||
"$type_str '$memberName'$argsMessage.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case _InvocationMirror._SUPER:
|
||||
{
|
||||
msg_buf.writeln("Super class of class '${_receiver.runtimeType}' has "
|
||||
"no instance $type_str '$memberName'$argsMessage.");
|
||||
memberName = "super.$memberName";
|
||||
break;
|
||||
}
|
||||
case _InvocationMirror._STATIC:
|
||||
{
|
||||
msg_buf.writeln("No static $type_str '$memberName'$argsMessage "
|
||||
"declared in class '$_receiver'.");
|
||||
break;
|
||||
}
|
||||
case _InvocationMirror._CONSTRUCTOR:
|
||||
{
|
||||
msg_buf.writeln("No constructor '$memberName'$argsMessage declared "
|
||||
"in class '$_receiver'.");
|
||||
memberName = "new $memberName";
|
||||
break;
|
||||
}
|
||||
case _InvocationMirror._TOP_LEVEL:
|
||||
{
|
||||
msg_buf.writeln("No top-level $type_str '$memberName'$argsMessage "
|
||||
"declared.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (level == _InvocationMirror._TOP_LEVEL) {
|
||||
msg_buf.writeln("Receiver: top-level");
|
||||
} else {
|
||||
msg_buf.writeln("Receiver: ${Error.safeToString(_receiver)}");
|
||||
}
|
||||
|
||||
if (type == _InvocationMirror._METHOD) {
|
||||
String m = is_type_call ? "$_receiver" : "$memberName";
|
||||
msg_buf.write("Tried calling: $m($arguments)");
|
||||
} else if (argumentCount == 0) {
|
||||
msg_buf.write("Tried calling: $memberName");
|
||||
} else if (type == _InvocationMirror._SETTER) {
|
||||
msg_buf.write("Tried calling: $memberName$arguments");
|
||||
} else {
|
||||
msg_buf.write("Tried calling: $memberName = $arguments");
|
||||
}
|
||||
|
||||
if (argsMismatch) {
|
||||
StringBuffer formalParameters = new StringBuffer();
|
||||
for (int i = 0; i < _existingArgumentNames.length; i++) {
|
||||
if (i > 0) {
|
||||
formalParameters.write(", ");
|
||||
}
|
||||
formalParameters.write(_existingArgumentNames[i]);
|
||||
}
|
||||
msg_buf.write("\nFound: $memberName($formalParameters)");
|
||||
}
|
||||
|
||||
return msg_buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@pragma("vm:entry-point")
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
class _InvocationMirror implements Invocation {
|
||||
// Constants describing the invocation kind.
|
||||
// _FIELD cannot be generated by regular invocation mirrors.
|
||||
static const int _UNINITIALIZED = -1;
|
||||
static const int _METHOD = 0;
|
||||
static const int _GETTER = 1;
|
||||
static const int _SETTER = 2;
|
||||
@@ -48,7 +49,7 @@ class _InvocationMirror implements Invocation {
|
||||
|
||||
// External representation of the invocation mirror; populated on demand.
|
||||
Symbol _memberName;
|
||||
int _type;
|
||||
int _type = _UNINITIALIZED;
|
||||
List<Type> _typeArguments;
|
||||
List _positionalArguments;
|
||||
Map<Symbol, dynamic> _namedArguments;
|
||||
@@ -61,7 +62,9 @@ class _InvocationMirror implements Invocation {
|
||||
}
|
||||
|
||||
void _setMemberNameAndType() {
|
||||
_type ??= 0;
|
||||
if (_type == _UNINITIALIZED) {
|
||||
_type = 0;
|
||||
}
|
||||
if (_functionName.startsWith("get:")) {
|
||||
_type |= _GETTER;
|
||||
_memberName = new internal.Symbol.unvalidated(_functionName.substring(4));
|
||||
@@ -144,28 +147,28 @@ class _InvocationMirror implements Invocation {
|
||||
}
|
||||
|
||||
bool get isMethod {
|
||||
if (_type == null) {
|
||||
if (_type == _UNINITIALIZED) {
|
||||
_setMemberNameAndType();
|
||||
}
|
||||
return (_type & _KIND_MASK) == _METHOD;
|
||||
}
|
||||
|
||||
bool get isAccessor {
|
||||
if (_type == null) {
|
||||
if (_type == _UNINITIALIZED) {
|
||||
_setMemberNameAndType();
|
||||
}
|
||||
return (_type & _KIND_MASK) != _METHOD;
|
||||
}
|
||||
|
||||
bool get isGetter {
|
||||
if (_type == null) {
|
||||
if (_type == _UNINITIALIZED) {
|
||||
_setMemberNameAndType();
|
||||
}
|
||||
return (_type & _KIND_MASK) == _GETTER;
|
||||
}
|
||||
|
||||
bool get isSetter {
|
||||
if (_type == null) {
|
||||
if (_type == _UNINITIALIZED) {
|
||||
_setMemberNameAndType();
|
||||
}
|
||||
return (_type & _KIND_MASK) == _SETTER;
|
||||
@@ -182,7 +185,7 @@ class _InvocationMirror implements Invocation {
|
||||
@pragma("vm:entry-point", "call")
|
||||
static _allocateInvocationMirror(String functionName,
|
||||
List argumentsDescriptor, List arguments, bool isSuperInvocation,
|
||||
[int type = null]) {
|
||||
[int type = _UNINITIALIZED]) {
|
||||
return new _InvocationMirror(
|
||||
functionName, argumentsDescriptor, arguments, isSuperInvocation, type);
|
||||
}
|
||||
@@ -200,6 +203,6 @@ class _InvocationMirror implements Invocation {
|
||||
int type,
|
||||
int delayedTypeArgumentsLen) {
|
||||
return new _InvocationMirror(functionName, argumentsDescriptor, arguments,
|
||||
false, type, delayedTypeArgumentsLen);
|
||||
false, type ?? _UNINITIALIZED, delayedTypeArgumentsLen);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,22 +177,15 @@ class AbstractClassInstantiationError {
|
||||
|
||||
@patch
|
||||
class NoSuchMethodError {
|
||||
// Deprecated members to be removed.
|
||||
Symbol? _memberName;
|
||||
List? _arguments;
|
||||
Map<Symbol, dynamic>? _namedArguments;
|
||||
List? _existingArgumentNames;
|
||||
final Object? _receiver;
|
||||
final Invocation _invocation;
|
||||
|
||||
final Object _receiver;
|
||||
final Invocation? _invocation;
|
||||
|
||||
// Issue(dartbug.com/127160): Remove the cast to [_InvocationMirror].
|
||||
@patch
|
||||
NoSuchMethodError.withInvocation(Object receiver, Invocation invocation)
|
||||
NoSuchMethodError.withInvocation(Object? receiver, Invocation invocation)
|
||||
: _receiver = receiver,
|
||||
_invocation = invocation;
|
||||
|
||||
static void _throwNewInvocation(Object receiver, Invocation invocation) {
|
||||
static void _throwNewInvocation(Object? receiver, Invocation invocation) {
|
||||
throw new NoSuchMethodError.withInvocation(receiver, invocation);
|
||||
}
|
||||
|
||||
@@ -200,44 +193,29 @@ class NoSuchMethodError {
|
||||
// method at compile time. The receiver is actually the literal class of the
|
||||
// unresolved method.
|
||||
@pragma("vm:entry-point", "call")
|
||||
static void _throwNew(Object receiver, String memberName, int invocationType,
|
||||
Object? typeArguments, List? arguments, List? argumentNames) {
|
||||
throw new NoSuchMethodError._withType(receiver, memberName, invocationType,
|
||||
typeArguments, arguments, argumentNames);
|
||||
}
|
||||
|
||||
static void _throwNewIfNotLoaded(
|
||||
_LibraryPrefix prefix,
|
||||
static void _throwNew(
|
||||
Object receiver,
|
||||
String memberName,
|
||||
int invocationType,
|
||||
Object typeArguments,
|
||||
List arguments,
|
||||
List argumentNames) {
|
||||
if (!prefix.isLoaded()) {
|
||||
_throwNew(receiver, memberName, invocationType, typeArguments, arguments,
|
||||
argumentNames);
|
||||
}
|
||||
int typeArgumentsLength,
|
||||
Object? typeArguments,
|
||||
List? arguments,
|
||||
List? argumentNames) {
|
||||
throw new NoSuchMethodError._withType(receiver, memberName, invocationType,
|
||||
typeArgumentsLength, typeArguments, arguments, argumentNames);
|
||||
}
|
||||
|
||||
// TODO(regis): Deprecated member still used by dart2js to be removed.
|
||||
// Remember the type from the invocation mirror or static compilation
|
||||
// analysis when thrown directly with _throwNew. A negative value means
|
||||
// that no information is available.
|
||||
int _invocationType = -1;
|
||||
|
||||
// TODO(regis): Deprecated constructor still used by dart2js to be removed.
|
||||
// Deprecated constructor.
|
||||
@patch
|
||||
NoSuchMethodError(Object receiver, Symbol memberName,
|
||||
List positionalArguments, Map<Symbol, dynamic> namedArguments,
|
||||
[List? existingArgumentNames = null])
|
||||
: _receiver = receiver,
|
||||
_invocation = null,
|
||||
_memberName = memberName,
|
||||
_arguments = positionalArguments,
|
||||
_namedArguments = namedArguments,
|
||||
_existingArgumentNames = existingArgumentNames,
|
||||
_invocationType = -1;
|
||||
NoSuchMethodError(this._receiver, Symbol memberName,
|
||||
List? positionalArguments, Map<Symbol, dynamic>? namedArguments,
|
||||
[List? existingArgumentNames = null]) // existingArgumentNames ignored.
|
||||
: this._invocation = new _InvocationMirror._withType(
|
||||
memberName,
|
||||
_InvocationMirror._UNINITIALIZED,
|
||||
null, // Type arguments not supported in deprecated constructor.
|
||||
positionalArguments,
|
||||
namedArguments);
|
||||
|
||||
// Helper to build a map of named arguments.
|
||||
static Map<Symbol, dynamic> _NamedArgumentsMap(
|
||||
@@ -260,16 +238,15 @@ class NoSuchMethodError {
|
||||
this._receiver,
|
||||
String memberName,
|
||||
int invocationType,
|
||||
int typeArgumentsLength, // Needed with all-dynamic (null) typeArguments.
|
||||
Object? typeArguments,
|
||||
List? arguments,
|
||||
List? argumentNames)
|
||||
: this._invocation = new _InvocationMirror._withType(
|
||||
new Symbol(memberName),
|
||||
invocationType,
|
||||
typeArguments != null
|
||||
// TODO(33073): Use actual count of type arguments in place of 0.
|
||||
? _InvocationMirror._unpackTypeArguments(typeArguments, 0)
|
||||
: null,
|
||||
_InvocationMirror._unpackTypeArguments(
|
||||
typeArguments, typeArgumentsLength),
|
||||
argumentNames != null
|
||||
? arguments!.sublist(0, arguments.length - argumentNames.length)
|
||||
: arguments,
|
||||
@@ -277,17 +254,12 @@ class NoSuchMethodError {
|
||||
? _NamedArgumentsMap(arguments!, argumentNames)
|
||||
: null);
|
||||
|
||||
static String? _existingMethodSignature(Object receiver, String methodName,
|
||||
static String? _existingMethodSignature(Object? receiver, String methodName,
|
||||
int invocationType) native "NoSuchMethodError_existingMethodSignature";
|
||||
|
||||
@patch
|
||||
String toString() {
|
||||
// TODO(regis): Remove this null check once dart2js is updated.
|
||||
final localInvocation = _invocation;
|
||||
if (localInvocation == null) {
|
||||
// Use deprecated version of toString.
|
||||
return _toStringDeprecated();
|
||||
}
|
||||
if (localInvocation is _InvocationMirror) {
|
||||
var internalName = localInvocation.memberName as internal.Symbol;
|
||||
String memberName = internal.Symbol.computeUnmangledName(internalName);
|
||||
@@ -343,14 +315,16 @@ class NoSuchMethodError {
|
||||
_receiver, memberName, localInvocation._type);
|
||||
String argsMsg = existingSig != null ? " with matching arguments" : "";
|
||||
|
||||
assert(kind >= 0 && kind < 5);
|
||||
final String kindBuf = (const [
|
||||
"method",
|
||||
"getter",
|
||||
"setter",
|
||||
"getter or setter",
|
||||
"variable"
|
||||
])[kind];
|
||||
String kindBuf = "function";
|
||||
if (kind >= 0 && kind < 5) {
|
||||
kindBuf = (const [
|
||||
"method",
|
||||
"getter",
|
||||
"setter",
|
||||
"getter or setter",
|
||||
"variable"
|
||||
])[kind];
|
||||
}
|
||||
|
||||
StringBuffer msgBuf = new StringBuffer("NoSuchMethodError: ");
|
||||
bool isTypeCall = false;
|
||||
@@ -448,7 +422,7 @@ class NoSuchMethodError {
|
||||
/// Used for situations where there is no extra information available
|
||||
/// about the failed invocation than the [Invocation] object and receiver,
|
||||
/// which includes errors created using [NoSuchMethodError.withInvocation].
|
||||
static String _toStringPlain(Object receiver, Invocation invocation) {
|
||||
static String _toStringPlain(Object? receiver, Invocation invocation) {
|
||||
var name = _symbolToString(invocation.memberName);
|
||||
var receiverType = "${receiver.runtimeType}";
|
||||
if (invocation.isAccessor) {
|
||||
@@ -491,143 +465,6 @@ class NoSuchMethodError {
|
||||
}
|
||||
return "$symbol";
|
||||
}
|
||||
|
||||
// TODO(regis): Remove this function once dart2js is updated.
|
||||
String _toStringDeprecated() {
|
||||
var level = (_invocationType >> _InvocationMirror._LEVEL_SHIFT) &
|
||||
_InvocationMirror._LEVEL_MASK;
|
||||
var type = _invocationType & _InvocationMirror._KIND_MASK;
|
||||
String memberName = (_memberName == null)
|
||||
? ""
|
||||
: internal.Symbol.computeUnmangledName(_memberName as internal.Symbol);
|
||||
|
||||
if (type == _InvocationMirror._LOCAL_VAR) {
|
||||
return "NoSuchMethodError: Cannot assign to final variable '$memberName'";
|
||||
}
|
||||
|
||||
StringBuffer arguments = new StringBuffer();
|
||||
int argumentCount = 0;
|
||||
final args = _arguments;
|
||||
if (args != null) {
|
||||
for (; argumentCount < args.length; argumentCount++) {
|
||||
if (argumentCount > 0) {
|
||||
arguments.write(", ");
|
||||
}
|
||||
arguments.write(Error.safeToString(args[argumentCount]));
|
||||
}
|
||||
}
|
||||
_namedArguments?.forEach((Symbol key, var value) {
|
||||
if (argumentCount > 0) {
|
||||
arguments.write(", ");
|
||||
}
|
||||
var internalName = key as internal.Symbol;
|
||||
arguments.write(internal.Symbol.computeUnmangledName(internalName));
|
||||
arguments.write(": ");
|
||||
arguments.write(Error.safeToString(value));
|
||||
argumentCount++;
|
||||
});
|
||||
bool argsMismatch = _existingArgumentNames != null;
|
||||
String argsMessage = argsMismatch ? " with matching arguments" : "";
|
||||
|
||||
final String typeStr = (type >= 0 && type < 5)
|
||||
? (const [
|
||||
"method",
|
||||
"getter",
|
||||
"setter",
|
||||
"getter or setter",
|
||||
"variable"
|
||||
])[type]
|
||||
: "";
|
||||
|
||||
StringBuffer msgBuf = new StringBuffer("NoSuchMethodError: ");
|
||||
bool isTypeCall = false;
|
||||
switch (level) {
|
||||
case _InvocationMirror._DYNAMIC:
|
||||
{
|
||||
if (_receiver == null) {
|
||||
if (argsMismatch) {
|
||||
msgBuf.writeln("The null object does not have a $typeStr "
|
||||
"'$memberName'$argsMessage.");
|
||||
} else {
|
||||
msgBuf.writeln("The $typeStr '$memberName' was called on null.");
|
||||
}
|
||||
} else {
|
||||
if (_receiver is _Closure) {
|
||||
msgBuf.writeln("Closure call with mismatched arguments: "
|
||||
"function '$memberName'");
|
||||
} else if (_receiver is _Type && memberName == "call") {
|
||||
isTypeCall = true;
|
||||
String name = _receiver.toString();
|
||||
msgBuf.writeln("Attempted to use type '$name' as a function. "
|
||||
"Since types do not define a method 'call', this is not "
|
||||
"possible. Did you intend to call the $name constructor and "
|
||||
"forget the 'new' operator?");
|
||||
} else {
|
||||
msgBuf.writeln("Class '${_receiver.runtimeType}' has no instance "
|
||||
"$typeStr '$memberName'$argsMessage.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case _InvocationMirror._SUPER:
|
||||
{
|
||||
msgBuf.writeln("Super class of class '${_receiver.runtimeType}' has "
|
||||
"no instance $typeStr '$memberName'$argsMessage.");
|
||||
memberName = "super.$memberName";
|
||||
break;
|
||||
}
|
||||
case _InvocationMirror._STATIC:
|
||||
{
|
||||
msgBuf.writeln("No static $typeStr '$memberName'$argsMessage "
|
||||
"declared in class '$_receiver'.");
|
||||
break;
|
||||
}
|
||||
case _InvocationMirror._CONSTRUCTOR:
|
||||
{
|
||||
msgBuf.writeln("No constructor '$memberName'$argsMessage declared "
|
||||
"in class '$_receiver'.");
|
||||
memberName = "new $memberName";
|
||||
break;
|
||||
}
|
||||
case _InvocationMirror._TOP_LEVEL:
|
||||
{
|
||||
msgBuf.writeln("No top-level $typeStr '$memberName'$argsMessage "
|
||||
"declared.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (level == _InvocationMirror._TOP_LEVEL) {
|
||||
msgBuf.writeln("Receiver: top-level");
|
||||
} else {
|
||||
msgBuf.writeln("Receiver: ${Error.safeToString(_receiver)}");
|
||||
}
|
||||
|
||||
if (type == _InvocationMirror._METHOD) {
|
||||
String m = isTypeCall ? "$_receiver" : "$memberName";
|
||||
msgBuf.write("Tried calling: $m($arguments)");
|
||||
} else if (argumentCount == 0) {
|
||||
msgBuf.write("Tried calling: $memberName");
|
||||
} else if (type == _InvocationMirror._SETTER) {
|
||||
msgBuf.write("Tried calling: $memberName$arguments");
|
||||
} else {
|
||||
msgBuf.write("Tried calling: $memberName = $arguments");
|
||||
}
|
||||
|
||||
if (argsMismatch) {
|
||||
StringBuffer formalParameters = new StringBuffer();
|
||||
final argumentNames = _existingArgumentNames!;
|
||||
for (int i = 0; i < argumentNames.length; i++) {
|
||||
if (i > 0) {
|
||||
formalParameters.write(", ");
|
||||
}
|
||||
formalParameters.write(argumentNames[i]);
|
||||
}
|
||||
msgBuf.write("\nFound: $memberName($formalParameters)");
|
||||
}
|
||||
|
||||
return msgBuf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@pragma("vm:entry-point")
|
||||
|
||||
Reference in New Issue
Block a user