[vm] Add a service method for 'invoke' as the dual to 'eval'.

Refactor the implementation of 'invoke' for sharing between the embedding API, mirrors, and the VM service. The fixes various issues in the embedding API:
 - Dart_Invoke, Dart_GetField and Dart_SetField now generate the same NoSuchMethodErrors as dart:mirrors and ordinary Dart code.
 - Dart_GetField and Dart_SetField now accept a null receiver.
 - Dart_GetField now handles closurization.
 - Dart_Invoke now handles calls through function-typed getters.
In mirrors, it also changes:
 - LibraryMirror.invoke/getField/setField now see members in the export namespace, not just local declarations. I.e., invocation with a LibraryMirror behaves the same ways as invocation against a library prefix in ordinary Dart code.

Bug: https://github.com/dart-lang/sdk/issues/11870
Bug: https://github.com/dart-lang/sdk/issues/13643
Bug: https://github.com/dart-lang/sdk/issues/29893
Bug: https://github.com/dart-lang/sdk/issues/33417
Bug: https://github.com/dart-lang/sdk/issues/34006
Change-Id: I913e8eef16943a3e86eda55c8d122819e41ad7cc
Reviewed-on: https://dart-review.googlesource.com/67302
Reviewed-by: Siva Annamalai <asiva@google.com>
This commit is contained in:
Ryan Macnak 2018-08-09 18:16:03 +00:00
parent 099f1504fa
commit 7b9fb893d2
13 changed files with 929 additions and 783 deletions

View file

@ -1036,7 +1036,7 @@ bool RunMainIsolate(const char* script_name, CommandLineOptions* dart_options) {
// namespace of the root library or invoke a getter of the same name
// in the exported namespace and return the resulting closure.
Dart_Handle main_closure =
Dart_GetClosure(root_lib, Dart_NewStringFromCString("main"));
Dart_GetField(root_lib, Dart_NewStringFromCString("main"));
CHECK_RESULT(main_closure);
if (!Dart_IsClosure(main_closure)) {
ErrorExit(kErrorExitCode,

View file

@ -9,10 +9,8 @@
/** \mainpage Dart Embedding API Reference
*
* Dart is a class-based programming language for creating structured
* web applications. This reference describes the Dart embedding api,
* which is used to embed the Dart Virtual Machine within an
* application.
* This reference describes the Dart Embedding API, which is used to embed the
* Dart Virtual Machine within C/C++ applications.
*
* This reference is generated from the header include/dart_api.h.
*/
@ -1694,20 +1692,6 @@ DART_EXPORT Dart_Handle Dart_NewDouble(double value);
*/
DART_EXPORT Dart_Handle Dart_DoubleValue(Dart_Handle double_obj, double* value);
/**
* Returns a closure of top level function 'function_name' in the exported
* namespace of specified 'library'. If a top level function 'function_name'
* does not exist, looks for a top level getter 'function_name' and invokes
* it and returns the object returned by the getter.
*
* \param library Library object
* \param function_name Name of the top level function
*
* \return A valid Dart instance if no error occurs during the operation.
*/
DART_EXPORT Dart_Handle Dart_GetClosure(Dart_Handle library,
Dart_Handle function_name);
/**
* Returns a closure of static function 'function_name' in the class 'class_name'
* in the exported namespace of specified 'library'.

View file

@ -28,6 +28,13 @@ namespace dart {
Exceptions::PropagateError(Error::Handle(type.error())); \
}
#define RETURN_OR_PROPAGATE(expr) \
RawObject* result = expr; \
if (RawObject::IsErrorClassId(result->GetClassIdMayBeSmi())) { \
Exceptions::PropagateError(Error::Handle(Error::RawCast(result))); \
} \
return result;
static RawInstance* CreateMirror(const String& mirror_class_name,
const Array& constructor_arguments) {
const Library& mirrors_lib = Library::Handle(Library::MirrorsLibrary());
@ -575,136 +582,6 @@ static void VerifyMethodKindShifts() {
#endif
}
static RawInstance* ReturnResult(const Object& result) {
if (result.IsError()) {
Exceptions::PropagateError(Error::Cast(result));
UNREACHABLE();
}
if (result.IsInstance()) {
return Instance::Cast(result).raw();
}
ASSERT(result.IsNull());
return Instance::null();
}
// Invoke the function, or noSuchMethod if it is null. Propagate any unhandled
// exceptions. Wrap and propagate any compilation errors.
static RawInstance* InvokeDynamicFunction(const Instance& receiver,
const Function& function,
const String& target_name,
const Array& args,
const Array& args_descriptor_array) {
// Note "args" is already the internal arguments with the receiver as the
// first element.
Object& result = Object::Handle();
ArgumentsDescriptor args_descriptor(args_descriptor_array);
if (function.IsNull() || !function.is_reflectable() ||
!function.AreValidArguments(args_descriptor, NULL)) {
result = DartEntry::InvokeNoSuchMethod(receiver, target_name, args,
args_descriptor_array);
} else {
result = DartEntry::InvokeFunction(function, args, args_descriptor_array);
}
return ReturnResult(result);
}
static RawInstance* InvokeLibraryGetter(const Library& library,
const String& getter_name,
const bool throw_nsm_if_absent) {
// To access a top-level we may need to use the Field or the getter Function.
// The getter function may either be in the library or in the field's owner
// class, depending on whether it was an actual getter, or an uninitialized
// field.
const Field& field = Field::Handle(library.LookupLocalField(getter_name));
Function& getter = Function::Handle();
if (field.IsNull()) {
// No field found. Check for a getter in the lib.
const String& internal_getter_name =
String::Handle(Field::GetterName(getter_name));
getter = library.LookupLocalFunction(internal_getter_name);
if (getter.IsNull()) {
getter = library.LookupLocalFunction(getter_name);
if (!getter.IsNull()) {
// Looking for a getter but found a regular method: closurize it.
const Function& closure_function =
Function::Handle(getter.ImplicitClosureFunction());
return closure_function.ImplicitStaticClosure();
}
}
} else {
if (!field.IsUninitialized()) {
return field.StaticValue();
}
// An uninitialized field was found. Check for a getter in the field's
// owner class.
const Class& klass = Class::Handle(field.Owner());
const String& internal_getter_name =
String::Handle(Field::GetterName(getter_name));
getter = klass.LookupStaticFunction(internal_getter_name);
}
if (!getter.IsNull() && getter.is_reflectable()) {
// Invoke the getter and return the result.
const Object& result = Object::Handle(
DartEntry::InvokeFunction(getter, Object::empty_array()));
return ReturnResult(result);
}
if (throw_nsm_if_absent) {
ThrowNoSuchMethod(AbstractType::Handle(
Class::Handle(library.toplevel_class()).RareType()),
getter_name, Object::null_array(), Object::null_array(),
InvocationMirror::kTopLevel, InvocationMirror::kGetter);
UNREACHABLE();
}
// Fall through case: Indicate that we didn't find any function or field using
// a special null instance. This is different from a field being null. Callers
// make sure that this null does not leak into Dartland.
return Object::sentinel().raw();
}
static RawInstance* InvokeClassGetter(const Class& klass,
const String& getter_name,
const bool throw_nsm_if_absent) {
// Note static fields do not have implicit getters.
const Field& field = Field::Handle(klass.LookupStaticField(getter_name));
if (field.IsNull() || field.IsUninitialized()) {
const String& internal_getter_name =
String::Handle(Field::GetterName(getter_name));
Function& getter =
Function::Handle(klass.LookupStaticFunction(internal_getter_name));
if (getter.IsNull() || !getter.is_reflectable()) {
if (getter.IsNull()) {
getter = klass.LookupStaticFunction(getter_name);
if (!getter.IsNull()) {
// Looking for a getter but found a regular method: closurize it.
const Function& closure_function =
Function::Handle(getter.ImplicitClosureFunction());
return closure_function.ImplicitStaticClosure();
}
}
if (throw_nsm_if_absent) {
ThrowNoSuchMethod(AbstractType::Handle(klass.RareType()), getter_name,
Object::null_array(), Object::null_array(),
InvocationMirror::kStatic, InvocationMirror::kGetter);
UNREACHABLE();
}
// Fall through case: Indicate that we didn't find any function or field
// using a special null instance. This is different from a field being
// null. Callers make sure that this null does not leak into Dartland.
return Object::sentinel().raw();
}
// Invoke the getter and return the result.
const Object& result = Object::Handle(
DartEntry::InvokeFunction(getter, Object::empty_array()));
return ReturnResult(result);
}
return field.StaticValue();
}
static RawAbstractType* InstantiateType(const AbstractType& type,
const AbstractType& instantiator) {
// Generic function type parameters are not reified, but mapped to dynamic,
@ -1341,48 +1218,7 @@ DEFINE_NATIVE_ENTRY(InstanceMirror_invoke, 5) {
arguments->NativeArgAt(2));
GET_NON_NULL_NATIVE_ARGUMENT(Array, args, arguments->NativeArgAt(3));
GET_NON_NULL_NATIVE_ARGUMENT(Array, arg_names, arguments->NativeArgAt(4));
Class& klass = Class::Handle(reflectee.clazz());
Function& function = Function::Handle(
zone, Resolver::ResolveDynamicAnyArgs(zone, klass, function_name));
// TODO(regis): Support invocation of generic functions with type arguments.
const int kTypeArgsLen = 0;
const Array& args_descriptor = Array::Handle(
zone, ArgumentsDescriptor::New(kTypeArgsLen, args.Length(), arg_names));
if (function.IsNull()) {
// Didn't find a method: try to find a getter and invoke call on its result.
const String& getter_name =
String::Handle(zone, Field::GetterName(function_name));
function = Resolver::ResolveDynamicAnyArgs(zone, klass, getter_name);
if (!function.IsNull()) {
ASSERT(function.kind() != RawFunction::kMethodExtractor);
// Invoke the getter.
const int kNumArgs = 1;
const Array& getter_args = Array::Handle(zone, Array::New(kNumArgs));
getter_args.SetAt(0, reflectee);
const Array& getter_args_descriptor = Array::Handle(
zone, ArgumentsDescriptor::New(kTypeArgsLen, getter_args.Length()));
const Instance& getter_result = Instance::Handle(
zone, InvokeDynamicFunction(reflectee, function, getter_name,
getter_args, getter_args_descriptor));
// Replace the closure as the receiver in the arguments list.
args.SetAt(0, getter_result);
// Call the closure.
const Object& call_result =
Object::Handle(zone, DartEntry::InvokeClosure(args, args_descriptor));
if (call_result.IsError()) {
Exceptions::PropagateError(Error::Cast(call_result));
UNREACHABLE();
}
return call_result.raw();
}
}
// Found an ordinary method.
return InvokeDynamicFunction(reflectee, function, function_name, args,
args_descriptor);
RETURN_OR_PROPAGATE(reflectee.Invoke(function_name, args, arg_names));
}
DEFINE_NATIVE_ENTRY(InstanceMirror_invokeGetter, 3) {
@ -1391,33 +1227,7 @@ DEFINE_NATIVE_ENTRY(InstanceMirror_invokeGetter, 3) {
// with its cousins.
GET_NATIVE_ARGUMENT(Instance, reflectee, arguments->NativeArgAt(1));
GET_NON_NULL_NATIVE_ARGUMENT(String, getter_name, arguments->NativeArgAt(2));
Class& klass = Class::Handle(reflectee.clazz());
const String& internal_getter_name =
String::Handle(Field::GetterName(getter_name));
Function& function = Function::Handle(
zone, Resolver::ResolveDynamicAnyArgs(zone, klass, internal_getter_name));
// Check for method extraction when method extractors are not created.
if (function.IsNull() && !FLAG_lazy_dispatchers) {
function = Resolver::ResolveDynamicAnyArgs(zone, klass, getter_name);
if (!function.IsNull()) {
const Function& closure_function =
Function::Handle(zone, function.ImplicitClosureFunction());
return closure_function.ImplicitInstanceClosure(reflectee);
}
}
const int kTypeArgsLen = 0;
const int kNumArgs = 1;
const Array& args = Array::Handle(zone, Array::New(kNumArgs));
args.SetAt(0, reflectee);
const Array& args_descriptor = Array::Handle(
zone, ArgumentsDescriptor::New(kTypeArgsLen, args.Length()));
// InvokeDynamic invokes NoSuchMethod if the provided function is null.
return InvokeDynamicFunction(reflectee, function, internal_getter_name, args,
args_descriptor);
RETURN_OR_PROPAGATE(reflectee.InvokeGetter(getter_name));
}
DEFINE_NATIVE_ENTRY(InstanceMirror_invokeSetter, 4) {
@ -1427,23 +1237,7 @@ DEFINE_NATIVE_ENTRY(InstanceMirror_invokeSetter, 4) {
GET_NATIVE_ARGUMENT(Instance, reflectee, arguments->NativeArgAt(1));
GET_NON_NULL_NATIVE_ARGUMENT(String, setter_name, arguments->NativeArgAt(2));
GET_NATIVE_ARGUMENT(Instance, value, arguments->NativeArgAt(3));
const Class& klass = Class::Handle(zone, reflectee.clazz());
const String& internal_setter_name =
String::Handle(zone, Field::SetterName(setter_name));
const Function& setter = Function::Handle(
zone, Resolver::ResolveDynamicAnyArgs(zone, klass, internal_setter_name));
const int kTypeArgsLen = 0;
const int kNumArgs = 2;
const Array& args = Array::Handle(zone, Array::New(kNumArgs));
args.SetAt(0, reflectee);
args.SetAt(1, value);
const Array& args_descriptor = Array::Handle(
zone, ArgumentsDescriptor::New(kTypeArgsLen, args.Length()));
return InvokeDynamicFunction(reflectee, setter, internal_setter_name, args,
args_descriptor);
RETURN_OR_PROPAGATE(reflectee.InvokeSetter(setter_name, value));
}
DEFINE_NATIVE_ENTRY(InstanceMirror_computeType, 1) {
@ -1498,74 +1292,7 @@ DEFINE_NATIVE_ENTRY(ClassMirror_invoke, 5) {
arguments->NativeArgAt(2));
GET_NON_NULL_NATIVE_ARGUMENT(Array, args, arguments->NativeArgAt(3));
GET_NON_NULL_NATIVE_ARGUMENT(Array, arg_names, arguments->NativeArgAt(4));
// TODO(regis): Support invocation of generic functions with type arguments.
const int kTypeArgsLen = 0;
const Error& error = Error::Handle(zone, klass.EnsureIsFinalized(thread));
if (!error.IsNull()) {
Exceptions::PropagateError(error);
UNREACHABLE();
}
Function& function =
Function::Handle(klass.LookupStaticFunction(function_name));
if (function.IsNull()) {
// Didn't find a method: try to find a getter and invoke call on its result.
const String& getter_name =
String::Handle(Field::GetterName(function_name));
function = klass.LookupStaticFunction(getter_name);
if (!function.IsNull()) {
// Invoke the getter.
const Object& getter_result = Object::Handle(
DartEntry::InvokeFunction(function, Object::empty_array()));
if (getter_result.IsError()) {
Exceptions::PropagateError(Error::Cast(getter_result));
UNREACHABLE();
}
// Make room for the closure (receiver) in the argument list.
const intptr_t num_args = args.Length();
const Array& call_args = Array::Handle(Array::New(num_args + 1));
Object& temp = Object::Handle();
for (int i = 0; i < num_args; i++) {
temp = args.At(i);
call_args.SetAt(i + 1, temp);
}
call_args.SetAt(0, getter_result);
const Array& call_args_descriptor_array =
Array::Handle(ArgumentsDescriptor::New(
kTypeArgsLen, call_args.Length(), arg_names));
// Call the closure.
const Object& call_result = Object::Handle(
DartEntry::InvokeClosure(call_args, call_args_descriptor_array));
if (call_result.IsError()) {
Exceptions::PropagateError(Error::Cast(call_result));
UNREACHABLE();
}
return call_result.raw();
}
}
const Array& args_descriptor_array = Array::Handle(
ArgumentsDescriptor::New(kTypeArgsLen, args.Length(), arg_names));
ArgumentsDescriptor args_descriptor(args_descriptor_array);
if (function.IsNull() || !function.AreValidArguments(args_descriptor, NULL) ||
!function.is_reflectable()) {
ThrowNoSuchMethod(AbstractType::Handle(klass.RareType()), function_name,
args, arg_names, InvocationMirror::kStatic,
InvocationMirror::kMethod);
UNREACHABLE();
}
Object& result = Object::Handle(
DartEntry::InvokeFunction(function, args, args_descriptor_array));
if (result.IsError()) {
Exceptions::PropagateError(Error::Cast(result));
UNREACHABLE();
}
return result.raw();
RETURN_OR_PROPAGATE(klass.Invoke(function_name, args, arg_names));
}
DEFINE_NATIVE_ENTRY(ClassMirror_invokeGetter, 3) {
@ -1580,7 +1307,7 @@ DEFINE_NATIVE_ENTRY(ClassMirror_invokeGetter, 3) {
UNREACHABLE();
}
GET_NON_NULL_NATIVE_ARGUMENT(String, getter_name, arguments->NativeArgAt(2));
return InvokeClassGetter(klass, getter_name, true);
RETURN_OR_PROPAGATE(klass.InvokeGetter(getter_name, true));
}
DEFINE_NATIVE_ENTRY(ClassMirror_invokeSetter, 4) {
@ -1591,55 +1318,7 @@ DEFINE_NATIVE_ENTRY(ClassMirror_invokeSetter, 4) {
const Class& klass = Class::Handle(ref.GetClassReferent());
GET_NON_NULL_NATIVE_ARGUMENT(String, setter_name, arguments->NativeArgAt(2));
GET_NATIVE_ARGUMENT(Instance, value, arguments->NativeArgAt(3));
const Error& error = Error::Handle(zone, klass.EnsureIsFinalized(thread));
if (!error.IsNull()) {
Exceptions::PropagateError(error);
UNREACHABLE();
}
// Check for real fields and user-defined setters.
const Field& field = Field::Handle(klass.LookupStaticField(setter_name));
Function& setter = Function::Handle();
const String& internal_setter_name =
String::Handle(Field::SetterName(setter_name));
if (field.IsNull()) {
setter = klass.LookupStaticFunction(internal_setter_name);
const int kNumArgs = 1;
const Array& args = Array::Handle(Array::New(kNumArgs));
args.SetAt(0, value);
if (setter.IsNull() || !setter.is_reflectable()) {
ThrowNoSuchMethod(AbstractType::Handle(klass.RareType()),
internal_setter_name, args, Object::null_array(),
InvocationMirror::kStatic, InvocationMirror::kSetter);
UNREACHABLE();
}
// Invoke the setter and return the result.
Object& result = Object::Handle(DartEntry::InvokeFunction(setter, args));
if (result.IsError()) {
Exceptions::PropagateError(Error::Cast(result));
UNREACHABLE();
}
return result.raw();
}
if (field.is_final() || !field.is_reflectable()) {
const int kNumArgs = 1;
const Array& args = Array::Handle(Array::New(kNumArgs));
args.SetAt(0, value);
ThrowNoSuchMethod(AbstractType::Handle(klass.RareType()),
internal_setter_name, args, Object::null_array(),
InvocationMirror::kStatic, InvocationMirror::kSetter);
UNREACHABLE();
}
field.SetStaticValue(value);
return value.raw();
RETURN_OR_PROPAGATE(klass.InvokeSetter(setter_name, value));
}
DEFINE_NATIVE_ENTRY(ClassMirror_invokeConstructor, 5) {
@ -1804,60 +1483,7 @@ DEFINE_NATIVE_ENTRY(LibraryMirror_invoke, 5) {
arguments->NativeArgAt(2));
GET_NON_NULL_NATIVE_ARGUMENT(Array, args, arguments->NativeArgAt(3));
GET_NON_NULL_NATIVE_ARGUMENT(Array, arg_names, arguments->NativeArgAt(4));
// TODO(regis): Support invocation of generic functions with type arguments.
const int kTypeArgsLen = 0;
Function& function =
Function::Handle(library.LookupLocalFunction(function_name));
if (function.IsNull()) {
// Didn't find a method: try to find a getter and invoke call on its result.
const Instance& getter_result =
Instance::Handle(InvokeLibraryGetter(library, function_name, false));
if (getter_result.raw() != Object::sentinel().raw()) {
// Make room for the closure (receiver) in arguments.
intptr_t numArgs = args.Length();
const Array& call_args = Array::Handle(Array::New(numArgs + 1));
Object& temp = Object::Handle();
for (int i = 0; i < numArgs; i++) {
temp = args.At(i);
call_args.SetAt(i + 1, temp);
}
call_args.SetAt(0, getter_result);
const Array& call_args_descriptor_array =
Array::Handle(ArgumentsDescriptor::New(
kTypeArgsLen, call_args.Length(), arg_names));
// Call closure.
const Object& call_result = Object::Handle(
DartEntry::InvokeClosure(call_args, call_args_descriptor_array));
if (call_result.IsError()) {
Exceptions::PropagateError(Error::Cast(call_result));
UNREACHABLE();
}
return call_result.raw();
}
}
const Array& args_descriptor_array = Array::Handle(
ArgumentsDescriptor::New(kTypeArgsLen, args.Length(), arg_names));
ArgumentsDescriptor args_descriptor(args_descriptor_array);
if (function.IsNull() || !function.AreValidArguments(args_descriptor, NULL) ||
!function.is_reflectable()) {
ThrowNoSuchMethod(AbstractType::Handle(
Class::Handle(library.toplevel_class()).RareType()),
function_name, args, arg_names,
InvocationMirror::kTopLevel, InvocationMirror::kMethod);
UNREACHABLE();
}
const Object& result = Object::Handle(
DartEntry::InvokeFunction(function, args, args_descriptor_array));
if (result.IsError()) {
Exceptions::PropagateError(Error::Cast(result));
UNREACHABLE();
}
return result.raw();
RETURN_OR_PROPAGATE(library.Invoke(function_name, args, arg_names));
}
DEFINE_NATIVE_ENTRY(LibraryMirror_invokeGetter, 3) {
@ -1867,7 +1493,7 @@ DEFINE_NATIVE_ENTRY(LibraryMirror_invokeGetter, 3) {
GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(1));
const Library& library = Library::Handle(ref.GetLibraryReferent());
GET_NON_NULL_NATIVE_ARGUMENT(String, getter_name, arguments->NativeArgAt(2));
return InvokeLibraryGetter(library, getter_name, true);
RETURN_OR_PROPAGATE(library.InvokeGetter(getter_name, true));
}
DEFINE_NATIVE_ENTRY(LibraryMirror_invokeSetter, 4) {
@ -1878,54 +1504,7 @@ DEFINE_NATIVE_ENTRY(LibraryMirror_invokeSetter, 4) {
const Library& library = Library::Handle(ref.GetLibraryReferent());
GET_NON_NULL_NATIVE_ARGUMENT(String, setter_name, arguments->NativeArgAt(2));
GET_NATIVE_ARGUMENT(Instance, value, arguments->NativeArgAt(3));
// To access a top-level we may need to use the Field or the
// setter Function. The setter function may either be in the
// library or in the field's owner class, depending.
const Field& field = Field::Handle(library.LookupLocalField(setter_name));
Function& setter = Function::Handle();
const String& internal_setter_name =
String::Handle(Field::SetterName(setter_name));
if (field.IsNull()) {
setter = library.LookupLocalFunction(internal_setter_name);
const int kNumArgs = 1;
const Array& args = Array::Handle(Array::New(kNumArgs));
args.SetAt(0, value);
if (setter.IsNull() || !setter.is_reflectable()) {
ThrowNoSuchMethod(AbstractType::Handle(
Class::Handle(library.toplevel_class()).RareType()),
internal_setter_name, args, Object::null_array(),
InvocationMirror::kTopLevel, InvocationMirror::kSetter);
UNREACHABLE();
}
// Invoke the setter and return the result.
const Object& result =
Object::Handle(DartEntry::InvokeFunction(setter, args));
if (result.IsError()) {
Exceptions::PropagateError(Error::Cast(result));
UNREACHABLE();
}
return result.raw();
}
if (field.is_final() || !field.is_reflectable()) {
const int kNumArgs = 1;
const Array& args = Array::Handle(Array::New(kNumArgs));
args.SetAt(0, value);
ThrowNoSuchMethod(AbstractType::Handle(
Class::Handle(library.toplevel_class()).RareType()),
internal_setter_name, args, Object::null_array(),
InvocationMirror::kTopLevel, InvocationMirror::kSetter);
UNREACHABLE();
}
field.SetStaticValue(value);
return value.raw();
RETURN_OR_PROPAGATE(library.InvokeSetter(setter_name, value));
}
DEFINE_NATIVE_ENTRY(MethodMirror_owner, 2) {

View file

@ -13,7 +13,7 @@ var tests = <VMTest>[
var result = await vm.invokeRpcNoUpgrade('getVersion', {});
expect(result['type'], equals('Version'));
expect(result['major'], equals(3));
expect(result['minor'], equals(9));
expect(result['minor'], equals(10));
expect(result['_privateMajor'], equals(0));
expect(result['_privateMinor'], equals(0));
},

View file

@ -0,0 +1,105 @@
// Copyright (c) 2018, 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.
// VMOptions=--error_on_bad_type --error_on_bad_override
import 'dart:async';
import 'dart:developer';
import 'package:observatory/service_io.dart';
import 'package:unittest/unittest.dart';
import 'service_test_common.dart';
import 'test_helper.dart';
libraryFunction() => "foobar1";
class Klass {
static classFunction(x) => "foobar2" + x;
instanceFunction(x, y) => "foobar3" + x + y;
}
var instance;
var apple;
var banana;
void testFunction() {
instance = new Klass();
apple = "apple";
banana = "banana";
debugger();
}
var tests = <IsolateTest>[
hasStoppedAtBreakpoint,
(Isolate isolate) async {
Library lib = isolate.rootLibrary;
await lib.load();
Class cls = lib.classes.singleWhere((cls) => cls.name == "Klass");
Field field =
lib.variables.singleWhere((field) => field.name == "instance");
await field.load();
Instance instance = field.staticValue;
field = lib.variables.singleWhere((field) => field.name == "apple");
await field.load();
Instance apple = field.staticValue;
field = lib.variables.singleWhere((field) => field.name == "banana");
await field.load();
Instance banana = field.staticValue;
dynamic result = await isolate.invokeRpc("invoke", {
"receiverId": lib.id,
"selector": "libraryFunction",
"argumentIds": []
});
print(result);
expect(result.valueAsString, equals('foobar1'));
result = await isolate.invokeRpc("invoke", {
"receiverId": cls.id,
"selector": "classFunction",
"argumentIds": [apple.id]
});
print(result);
expect(result.valueAsString, equals('foobar2apple'));
result = await isolate.invokeRpc("invoke", {
"receiverId": instance.id,
"selector": "instanceFunction",
"argumentIds": [apple.id, banana.id]
});
print(result);
expect(result.valueAsString, equals('foobar3applebanana'));
// Wrong arity.
await expectError(() => isolate.invokeRpc("invoke", {
"receiverId": instance.id,
"selector": "instanceFunction",
"argumentIds": [apple.id]
}));
// No such target.
await expectError(() => isolate.invokeRpc("invoke", {
"receiverId": instance.id,
"selector": "functionDoesNotExist",
"argumentIds": [apple.id]
}));
},
resumeIsolate,
];
expectError(func) async {
bool gotException = false;
dynamic result;
try {
result = await func();
expect(result.type, equals('Error')); // dart1 semantics
} on ServerRpcException catch (e) {
expect(e.code, equals(ServerRpcException.kExpressionCompilationError));
gotException = true;
}
if (result?.type != 'Error') {
expect(gotException, true); // dart2 semantics
}
}
main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction);

View file

@ -2314,20 +2314,6 @@ DART_EXPORT Dart_Handle Dart_DoubleValue(Dart_Handle double_obj,
return Api::Success();
}
DART_EXPORT Dart_Handle Dart_GetClosure(Dart_Handle library,
Dart_Handle function_name) {
DARTSCOPE(Thread::Current());
const Library& lib = Api::UnwrapLibraryHandle(Z, library);
if (lib.IsNull()) {
RETURN_TYPE_ERROR(Z, library, Library);
}
const String& name = Api::UnwrapStringHandle(Z, function_name);
if (name.IsNull()) {
RETURN_TYPE_ERROR(Z, function_name, String);
}
return Api::NewHandle(T, lib.GetFunctionClosure(name));
}
DART_EXPORT Dart_Handle Dart_GetStaticMethodClosure(Dart_Handle library,
Dart_Handle cls_type,
Dart_Handle function_name) {
@ -4148,7 +4134,8 @@ DART_EXPORT Dart_Handle Dart_Invoke(Dart_Handle target,
API_TIMELINE_DURATION(T);
CHECK_CALLBACK_STATE(T);
const String& function_name = Api::UnwrapStringHandle(Z, name);
String& function_name =
String::Handle(Z, Api::UnwrapStringHandle(Z, name).raw());
if (function_name.IsNull()) {
RETURN_TYPE_ERROR(Z, name, String);
}
@ -4163,7 +4150,9 @@ DART_EXPORT Dart_Handle Dart_Invoke(Dart_Handle target,
}
Dart_Handle result;
Array& args = Array::Handle(Z);
const intptr_t kTypeArgsLen = 0;
// This API does not provide a way to pass named parameters.
const Array& arg_names = Object::empty_array();
const bool respect_reflectable = false;
if (obj.IsType()) {
if (!Type::Cast(obj).IsFinalized()) {
return Api::NewError(
@ -4172,70 +4161,33 @@ DART_EXPORT Dart_Handle Dart_Invoke(Dart_Handle target,
}
const Class& cls = Class::Handle(Z, Type::Cast(obj).type_class());
const Function& function =
Function::Handle(Z, Resolver::ResolveStaticAllowPrivate(
cls, function_name, kTypeArgsLen,
number_of_arguments, Object::empty_array()));
if (function.IsNull()) {
const String& cls_name = String::Handle(Z, cls.Name());
return Api::NewError("%s: did not find static method '%s.%s'.",
CURRENT_FUNC, cls_name.ToCString(),
function_name.ToCString());
if (Library::IsPrivate(function_name)) {
const Library& lib = Library::Handle(Z, cls.library());
function_name = lib.PrivateName(function_name);
}
#if !defined(PRODUCT)
if (tds.enabled()) {
const String& cls_name = String::Handle(Z, cls.Name());
tds.SetNumArguments(1);
tds.FormatArgument(0, "name", "%s.%s", cls_name.ToCString(),
function_name.ToCString());
}
#endif // !defined(PRODUCT)
// Setup args and check for malformed arguments in the arguments list.
result = SetupArguments(T, number_of_arguments, arguments, 0, &args);
if (!::Dart_IsError(result)) {
result = Api::NewHandle(T, DartEntry::InvokeFunction(function, args));
if (::Dart_IsError(result)) {
return result;
}
return result;
return Api::NewHandle(
T, cls.Invoke(function_name, args, arg_names, respect_reflectable));
} else if (obj.IsNull() || obj.IsInstance()) {
// Since we have allocated an object it would mean that the type of the
// receiver is already resolved and finalized, hence it is not necessary
// to check here.
Instance& instance = Instance::Handle(Z);
instance ^= obj.raw();
ArgumentsDescriptor args_desc(Array::Handle(
Z, ArgumentsDescriptor::New(kTypeArgsLen, number_of_arguments + 1)));
const Function& function = Function::Handle(
Z, Resolver::ResolveDynamic(instance, function_name, args_desc));
if (function.IsNull()) {
// Setup args and check for malformed arguments in the arguments list.
result = SetupArguments(T, number_of_arguments, arguments, 1, &args);
if (!::Dart_IsError(result)) {
args.SetAt(0, instance);
const Array& args_descriptor = Array::Handle(
Z, ArgumentsDescriptor::New(kTypeArgsLen, args.Length()));
result = Api::NewHandle(
T, DartEntry::InvokeNoSuchMethod(instance, function_name, args,
args_descriptor));
}
return result;
}
#if !defined(PRODUCT)
if (tds.enabled()) {
const Class& cls = Class::Handle(Z, instance.clazz());
ASSERT(!cls.IsNull());
const String& cls_name = String::Handle(Z, cls.Name());
tds.SetNumArguments(1);
tds.FormatArgument(0, "name", "%s.%s", cls_name.ToCString(),
function_name.ToCString());
}
#endif // !defined(PRODUCT)
// Setup args and check for malformed arguments in the arguments list.
result = SetupArguments(T, number_of_arguments, arguments, 1, &args);
if (!::Dart_IsError(result)) {
args.SetAt(0, instance);
result = Api::NewHandle(T, DartEntry::InvokeFunction(function, args));
if (::Dart_IsError(result)) {
return result;
}
return result;
args.SetAt(0, instance);
return Api::NewHandle(T, instance.Invoke(function_name, args, arg_names,
respect_reflectable));
} else if (obj.IsLibrary()) {
// Check whether class finalization is needed.
const Library& lib = Library::Cast(obj);
@ -4246,37 +4198,18 @@ DART_EXPORT Dart_Handle Dart_Invoke(Dart_Handle target,
CURRENT_FUNC);
}
const Function& function =
Function::Handle(Z, lib.LookupFunctionAllowPrivate(function_name));
if (function.IsNull()) {
return Api::NewError("%s: did not find top-level function '%s'.",
CURRENT_FUNC, function_name.ToCString());
if (Library::IsPrivate(function_name)) {
function_name = lib.PrivateName(function_name);
}
#if !defined(PRODUCT)
if (tds.enabled()) {
const String& lib_name = String::Handle(Z, lib.url());
tds.SetNumArguments(1);
tds.FormatArgument(0, "name", "%s.%s", lib_name.ToCString(),
function_name.ToCString());
}
#endif // !defined(PRODUCT)
// LookupFunctionAllowPrivate does not check argument arity, so we
// do it here.
String& error_message = String::Handle(Z);
if (!function.AreValidArgumentCounts(kTypeArgsLen, number_of_arguments, 0,
&error_message)) {
return Api::NewError("%s: wrong argument count for function '%s': %s.",
CURRENT_FUNC, function_name.ToCString(),
error_message.ToCString());
}
// Setup args and check for malformed arguments in the arguments list.
result = SetupArguments(T, number_of_arguments, arguments, 0, &args);
if (!::Dart_IsError(result)) {
result = Api::NewHandle(T, DartEntry::InvokeFunction(function, args));
if (::Dart_IsError(result)) {
return result;
}
return result;
return Api::NewHandle(
T, lib.Invoke(function_name, args, arg_names, respect_reflectable));
} else {
return Api::NewError(
"%s expects argument 'target' to be an object, type, or library.",
@ -4320,96 +4253,39 @@ DART_EXPORT Dart_Handle Dart_GetField(Dart_Handle container, Dart_Handle name) {
API_TIMELINE_DURATION(T);
CHECK_CALLBACK_STATE(T);
const String& field_name = Api::UnwrapStringHandle(Z, name);
String& field_name =
String::Handle(Z, Api::UnwrapStringHandle(Z, name).raw());
if (field_name.IsNull()) {
RETURN_TYPE_ERROR(Z, name, String);
}
Field& field = Field::Handle(Z);
Function& getter = Function::Handle(Z);
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(container));
if (obj.IsNull()) {
return Api::NewError("%s expects argument 'container' to be non-null.",
CURRENT_FUNC);
} else if (obj.IsType()) {
const bool throw_nsm_if_absent = true;
const bool respect_reflectable = false;
if (obj.IsType()) {
if (!Type::Cast(obj).IsFinalized()) {
return Api::NewError(
"%s expects argument 'container' to be a fully resolved type.",
CURRENT_FUNC);
}
// To access a static field we may need to use the Field or the
// getter Function.
Class& cls = Class::Handle(Z, Type::Cast(obj).type_class());
field = cls.LookupStaticFieldAllowPrivate(field_name);
if (field.IsNull() || field.IsUninitialized()) {
const String& getter_name =
String::Handle(Z, Field::GetterName(field_name));
getter = cls.LookupStaticFunctionAllowPrivate(getter_name);
if (Library::IsPrivate(field_name)) {
const Library& lib = Library::Handle(Z, cls.library());
field_name = lib.PrivateName(field_name);
}
#if !defined(PRODUCT)
if (tds.enabled()) {
const String& cls_name = String::Handle(cls.Name());
tds.SetNumArguments(1);
tds.FormatArgument(0, "name", "%s.%s", cls_name.ToCString(),
field_name.ToCString());
return Api::NewHandle(T, cls.InvokeGetter(field_name, throw_nsm_if_absent,
respect_reflectable));
} else if (obj.IsNull() || obj.IsInstance()) {
Instance& instance = Instance::Handle(Z);
instance ^= obj.raw();
if (Library::IsPrivate(field_name)) {
const Class& cls = Class::Handle(Z, instance.clazz());
const Library& lib = Library::Handle(Z, cls.library());
field_name = lib.PrivateName(field_name);
}
#endif // !defined(PRODUCT)
if (!getter.IsNull()) {
// Invoke the getter and return the result.
return Api::NewHandle(
T, DartEntry::InvokeFunction(getter, Object::empty_array()));
} else if (!field.IsNull()) {
return Api::NewHandle(T, field.StaticValue());
} else {
return Api::NewError("%s: did not find static field '%s'.", CURRENT_FUNC,
field_name.ToCString());
}
} else if (obj.IsInstance()) {
// Every instance field has a getter Function. Try to find the
// getter in any superclass and use that function to access the
// field.
const Instance& instance = Instance::Cast(obj);
Class& cls = Class::Handle(Z, instance.clazz());
String& getter_name = String::Handle(Z, Field::GetterName(field_name));
while (!cls.IsNull()) {
getter = cls.LookupDynamicFunctionAllowPrivate(getter_name);
if (!getter.IsNull()) {
break;
}
cls = cls.SuperClass();
}
#if !defined(PRODUCT)
if (tds.enabled()) {
const String& cls_name = String::Handle(cls.Name());
tds.SetNumArguments(1);
tds.FormatArgument(0, "name", "%s.%s", cls_name.ToCString(),
field_name.ToCString());
}
#endif // !defined(PRODUCT)
// Invoke the getter and return the result.
const int kTypeArgsLen = 0;
const int kNumArgs = 1;
const Array& args = Array::Handle(Z, Array::New(kNumArgs));
args.SetAt(0, instance);
if (getter.IsNull()) {
const Array& args_descriptor = Array::Handle(
Z, ArgumentsDescriptor::New(kTypeArgsLen, args.Length()));
return Api::NewHandle(
T, DartEntry::InvokeNoSuchMethod(instance, getter_name, args,
args_descriptor));
}
return Api::NewHandle(T, DartEntry::InvokeFunction(getter, args));
return Api::NewHandle(
T, instance.InvokeGetter(field_name, respect_reflectable));
} else if (obj.IsLibrary()) {
// To access a top-level we may need to use the Field or the
// getter Function. The getter function may either be in the
// library or in the field's owner class, depending.
const Library& lib = Library::Cast(obj);
// Check that the library is loaded.
if (!lib.Loaded()) {
@ -4417,40 +4293,11 @@ DART_EXPORT Dart_Handle Dart_GetField(Dart_Handle container, Dart_Handle name) {
"%s expects library argument 'container' to be loaded.",
CURRENT_FUNC);
}
field = lib.LookupFieldAllowPrivate(field_name);
if (field.IsNull()) {
// No field found and no ambiguity error. Check for a getter in the lib.
const String& getter_name =
String::Handle(Z, Field::GetterName(field_name));
getter = lib.LookupFunctionAllowPrivate(getter_name);
} else if (!field.IsNull() && field.IsUninitialized()) {
// A field was found. Check for a getter in the field's owner class.
const Class& cls = Class::Handle(Z, field.Owner());
const String& getter_name =
String::Handle(Z, Field::GetterName(field_name));
getter = cls.LookupStaticFunctionAllowPrivate(getter_name);
if (Library::IsPrivate(field_name)) {
field_name = lib.PrivateName(field_name);
}
#if !defined(PRODUCT)
if (tds.enabled()) {
const String& lib_name = String::Handle(lib.url());
tds.SetNumArguments(1);
tds.FormatArgument(0, "name", "%s.%s", lib_name.ToCString(),
field_name.ToCString());
}
#endif // !defined(PRODUCT)
if (!getter.IsNull()) {
// Invoke the getter and return the result.
return Api::NewHandle(
T, DartEntry::InvokeFunction(getter, Object::empty_array()));
}
if (!field.IsNull()) {
return Api::NewHandle(T, field.StaticValue());
}
return Api::NewError("%s: did not find top-level variable '%s'.",
CURRENT_FUNC, field_name.ToCString());
return Api::NewHandle(T, lib.InvokeGetter(field_name, throw_nsm_if_absent,
respect_reflectable));
} else if (obj.IsError()) {
return container;
} else {
@ -4467,7 +4314,8 @@ DART_EXPORT Dart_Handle Dart_SetField(Dart_Handle container,
API_TIMELINE_DURATION(T);
CHECK_CALLBACK_STATE(T);
const String& field_name = Api::UnwrapStringHandle(Z, name);
String& field_name =
String::Handle(Z, Api::UnwrapStringHandle(Z, name).raw());
if (field_name.IsNull()) {
RETURN_TYPE_ERROR(Z, name, String);
}
@ -4480,13 +4328,10 @@ DART_EXPORT Dart_Handle Dart_SetField(Dart_Handle container,
Instance& value_instance = Instance::Handle(Z);
value_instance ^= value_obj.raw();
Field& field = Field::Handle(Z);
Function& setter = Function::Handle(Z);
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(container));
if (obj.IsNull()) {
return Api::NewError("%s expects argument 'container' to be non-null.",
CURRENT_FUNC);
} else if (obj.IsType()) {
const bool respect_reflectable = false;
if (obj.IsType()) {
if (!Type::Cast(obj).IsFinalized()) {
return Api::NewError(
"%s expects argument 'container' to be a fully resolved type.",
@ -4496,73 +4341,22 @@ DART_EXPORT Dart_Handle Dart_SetField(Dart_Handle container,
// To access a static field we may need to use the Field or the
// setter Function.
Class& cls = Class::Handle(Z, Type::Cast(obj).type_class());
field = cls.LookupStaticFieldAllowPrivate(field_name);
if (field.IsNull()) {
String& setter_name = String::Handle(Z, Field::SetterName(field_name));
setter = cls.LookupStaticFunctionAllowPrivate(setter_name);
if (Library::IsPrivate(field_name)) {
const Library& lib = Library::Handle(Z, cls.library());
field_name = lib.PrivateName(field_name);
}
if (!setter.IsNull()) {
// Invoke the setter and return the result.
const int kNumArgs = 1;
const Array& args = Array::Handle(Z, Array::New(kNumArgs));
args.SetAt(0, value_instance);
const Object& result =
Object::Handle(Z, DartEntry::InvokeFunction(setter, args));
if (result.IsError()) {
return Api::NewHandle(T, result.raw());
} else {
return Api::Success();
}
} else if (!field.IsNull()) {
if (field.is_final()) {
return Api::NewError("%s: cannot set final field '%s'.", CURRENT_FUNC,
field_name.ToCString());
} else {
field.SetStaticValue(value_instance);
return Api::Success();
}
} else {
return Api::NewError("%s: did not find static field '%s'.", CURRENT_FUNC,
field_name.ToCString());
return Api::NewHandle(
T, cls.InvokeSetter(field_name, value_instance, respect_reflectable));
} else if (obj.IsNull() || obj.IsInstance()) {
Instance& instance = Instance::Handle(Z);
instance ^= obj.raw();
if (Library::IsPrivate(field_name)) {
const Class& cls = Class::Handle(Z, instance.clazz());
const Library& lib = Library::Handle(Z, cls.library());
field_name = lib.PrivateName(field_name);
}
} else if (obj.IsInstance()) {
// Every instance field has a setter Function. Try to find the
// setter in any superclass and use that function to access the
// field.
const Instance& instance = Instance::Cast(obj);
Class& cls = Class::Handle(Z, instance.clazz());
String& setter_name = String::Handle(Z, Field::SetterName(field_name));
while (!cls.IsNull()) {
field = cls.LookupInstanceFieldAllowPrivate(field_name);
if (!field.IsNull() && field.is_final()) {
return Api::NewError("%s: cannot set final field '%s'.", CURRENT_FUNC,
field_name.ToCString());
}
setter = cls.LookupDynamicFunctionAllowPrivate(setter_name);
if (!setter.IsNull()) {
break;
}
cls = cls.SuperClass();
}
// Invoke the setter and return the result.
const int kTypeArgsLen = 0;
const int kNumArgs = 2;
const Array& args = Array::Handle(Z, Array::New(kNumArgs));
args.SetAt(0, instance);
args.SetAt(1, value_instance);
if (setter.IsNull()) {
const Array& args_descriptor = Array::Handle(
Z, ArgumentsDescriptor::New(kTypeArgsLen, args.Length()));
return Api::NewHandle(
T, DartEntry::InvokeNoSuchMethod(instance, setter_name, args,
args_descriptor));
}
return Api::NewHandle(T, DartEntry::InvokeFunction(setter, args));
return Api::NewHandle(T, instance.InvokeSetter(field_name, value_instance,
respect_reflectable));
} else if (obj.IsLibrary()) {
// To access a top-level we may need to use the Field or the
// setter Function. The setter function may either be in the
@ -4574,36 +4368,12 @@ DART_EXPORT Dart_Handle Dart_SetField(Dart_Handle container,
"%s expects library argument 'container' to be loaded.",
CURRENT_FUNC);
}
field = lib.LookupFieldAllowPrivate(field_name);
if (field.IsNull()) {
const String& setter_name =
String::Handle(Z, Field::SetterName(field_name));
setter ^= lib.LookupFunctionAllowPrivate(setter_name);
}
if (!setter.IsNull()) {
// Invoke the setter and return the result.
const int kNumArgs = 1;
const Array& args = Array::Handle(Z, Array::New(kNumArgs));
args.SetAt(0, value_instance);
const Object& result =
Object::Handle(Z, DartEntry::InvokeFunction(setter, args));
if (result.IsError()) {
return Api::NewHandle(T, result.raw());
}
return Api::Success();
if (Library::IsPrivate(field_name)) {
field_name = lib.PrivateName(field_name);
}
if (!field.IsNull()) {
if (field.is_final()) {
return Api::NewError("%s: cannot set final top-level variable '%s'.",
CURRENT_FUNC, field_name.ToCString());
}
field.SetStaticValue(value_instance);
return Api::Success();
}
return Api::NewError("%s: did not find top-level variable '%s'.",
CURRENT_FUNC, field_name.ToCString());
return Api::NewHandle(
T, lib.InvokeSetter(field_name, value_instance, respect_reflectable));
} else if (obj.IsError()) {
return container;
}

View file

@ -794,7 +794,7 @@ TEST_CASE(DartAPI_FunctionName) {
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
EXPECT_VALID(lib);
Dart_Handle closure = Dart_GetClosure(lib, NewString("getInt"));
Dart_Handle closure = Dart_GetField(lib, NewString("getInt"));
EXPECT_VALID(closure);
if (Dart_IsClosure(closure)) {
closure = Dart_ClosureFunction(closure);
@ -814,7 +814,7 @@ TEST_CASE(DartAPI_FunctionOwner) {
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
EXPECT_VALID(lib);
Dart_Handle closure = Dart_GetClosure(lib, NewString("getInt"));
Dart_Handle closure = Dart_GetField(lib, NewString("getInt"));
EXPECT_VALID(closure);
if (Dart_IsClosure(closure)) {
closure = Dart_ClosureFunction(closure);
@ -844,7 +844,7 @@ TEST_CASE(DartAPI_FunctionIsStatic) {
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
EXPECT_VALID(lib);
Dart_Handle closure = Dart_GetClosure(lib, NewString("getInt"));
Dart_Handle closure = Dart_GetField(lib, NewString("getInt"));
EXPECT_VALID(closure);
if (Dart_IsClosure(closure)) {
closure = Dart_ClosureFunction(closure);
@ -882,7 +882,7 @@ TEST_CASE(DartAPI_ClosureFunction) {
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
EXPECT_VALID(lib);
Dart_Handle closure = Dart_GetClosure(lib, NewString("getInt"));
Dart_Handle closure = Dart_GetField(lib, NewString("getInt"));
EXPECT_VALID(closure);
EXPECT(Dart_IsClosure(closure));
Dart_Handle closure_str = Dart_ToString(closure);
@ -3850,8 +3850,9 @@ static void TestFieldOk(Dart_Handle container,
}
static void TestFieldNotFound(Dart_Handle container, Dart_Handle name) {
EXPECT(Dart_IsError(Dart_GetField(container, name)));
EXPECT(Dart_IsError(Dart_SetField(container, name, Dart_Null())));
EXPECT_ERROR(Dart_GetField(container, name), "NoSuchMethodError");
EXPECT_ERROR(Dart_SetField(container, name, Dart_Null()),
"NoSuchMethodError");
}
TEST_CASE(DartAPI_FieldAccess) {
@ -4083,7 +4084,7 @@ TEST_CASE(DartAPI_FieldAccess) {
name = NewString("imported_fld");
TestFieldNotFound(type, name);
TestFieldNotFound(instance, name);
TestFieldOk(lib, name, false, "imported");
TestFieldNotFound(lib, name);
// Hidden imported top-level field. Not found at any level.
name = NewString("_imported_fld");
@ -4095,7 +4096,7 @@ TEST_CASE(DartAPI_FieldAccess) {
name = NewString("imported_getset_fld");
TestFieldNotFound(type, name);
TestFieldNotFound(instance, name);
TestFieldOk(lib, name, false, "imported getset");
TestFieldNotFound(lib, name);
// Hidden imported top-level get/set field. Not found at any level.
name = NewString("_imported_getset_fld");
@ -5006,7 +5007,8 @@ TEST_CASE(DartAPI_Invoke) {
// Static method, wrong arg count.
EXPECT_ERROR(Dart_Invoke(type, name, 2, bad_args),
"did not find static method 'Methods.staticMethod'");
"NoSuchMethodError: No static method 'staticMethod' with "
"matching arguments");
// Hidden static method.
name = NewString("_staticMethod");
@ -5034,8 +5036,8 @@ TEST_CASE(DartAPI_Invoke) {
// Top-level method, wrong arg count.
EXPECT_ERROR(Dart_Invoke(lib, name, 2, bad_args),
"Dart_Invoke: wrong argument count for function 'topMethod': "
"2 passed, 1 expected.");
"NoSuchMethodError: No top-level method 'topMethod' with "
"matching arguments");
// Hidden top-level method.
name = NewString("_topMethod");
@ -5131,6 +5133,17 @@ TEST_CASE(DartAPI_Invoke_Null) {
result = Dart_Invoke(Dart_Null(), function_name, 0, NULL);
EXPECT(Dart_IsError(result));
EXPECT(Dart_ErrorHasException(result));
result = Dart_GetField(Dart_Null(), NewString("toString"));
EXPECT_VALID(result);
EXPECT(Dart_IsClosure(result));
result =
Dart_SetField(Dart_Null(), NewString("nullHasNoSetters"), Dart_Null());
// Not that Dart_SetField expects a non-null receiver.
EXPECT_ERROR(
result,
"NoSuchMethodError: The setter 'nullHasNoSetters=' was called on null");
}
TEST_CASE(DartAPI_InvokeNoSuchMethod) {
@ -5228,10 +5241,11 @@ TEST_CASE(DartAPI_Invoke_CrossLibrary) {
EXPECT_VALID(Dart_Invoke(lib1, NewString("local"), 0, NULL));
EXPECT_VALID(Dart_Invoke(lib1, NewString("_local"), 0, NULL));
// We can only invoke non-private imported functions.
EXPECT_VALID(Dart_Invoke(lib1, NewString("imported"), 0, NULL));
// We cannot invoke imported functions (the library is an explicit receiver).
EXPECT_ERROR(Dart_Invoke(lib1, NewString("imported"), 0, NULL),
"NoSuchMethodError: No top-level method 'imported' declared");
EXPECT_ERROR(Dart_Invoke(lib1, NewString("_imported"), 0, NULL),
"did not find top-level function '_imported'");
"NoSuchMethodError: No top-level method '_imported' declared");
}
TEST_CASE(DartAPI_InvokeClosure) {
@ -6029,7 +6043,7 @@ TEST_CASE(DartAPI_ImportLibraryWithPrefix) {
// not be found directly in lib2.
Dart_Handle method_name = NewString("foo");
result = Dart_Invoke(lib2, method_name, 0, NULL);
EXPECT_ERROR(result, "Dart_Invoke: did not find top-level function 'foo'");
EXPECT_ERROR(result, "NoSuchMethodError: No top-level method 'foo' declared");
// Check that lib1 is available under the prefix in lib2.
method_name = NewString("foobar");
@ -9107,12 +9121,8 @@ TEST_CASE(DartAPI_InvokeImportedFunction) {
Dart_Handle args[2] = {Dart_NewInteger(123), Dart_NewInteger(321)};
Dart_Handle result = Dart_Invoke(lib, max, 2, args);
EXPECT_VALID(result);
EXPECT(Dart_IsNumber(result));
int64_t result_value;
EXPECT_VALID(Dart_IntegerToInt64(result, &result_value));
EXPECT(result_value == 321);
EXPECT_ERROR(result,
"NoSuchMethodError: No top-level method 'max' declared.");
// The function 'getCurrentTag' is actually defined in the library
// dart:developer. However, the library dart:profiler exports dart:developer
@ -9121,7 +9131,9 @@ TEST_CASE(DartAPI_InvokeImportedFunction) {
// an interim solution until we fix DartAPI_Invoke_CrossLibrary.
Dart_Handle getCurrentTag = Dart_NewStringFromCString("getCurrentTag");
result = Dart_Invoke(lib, getCurrentTag, 0, NULL);
EXPECT_VALID(result);
EXPECT_ERROR(
result,
"NoSuchMethodError: No top-level method 'getCurrentTag' declared.");
}
#endif // !PRODUCT

View file

@ -3348,6 +3348,196 @@ RawFunction* Function::EvaluateHelper(const Class& cls,
return func.raw();
}
// Conventions:
// * For throwing a NSM in a class klass we use its runtime type as receiver,
// i.e., klass.RareType().
// * For throwing a NSM in a library, we just pass the null instance as
// receiver.
static RawObject* ThrowNoSuchMethod(const Instance& receiver,
const String& function_name,
const Array& arguments,
const Array& argument_names,
const InvocationMirror::Level level,
const InvocationMirror::Kind kind) {
const Smi& invocation_type =
Smi::Handle(Smi::New(InvocationMirror::EncodeType(level, kind)));
const Array& args = Array::Handle(Array::New(6));
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);
const Library& libcore = Library::Handle(Library::CoreLibrary());
const Class& NoSuchMethodError =
Class::Handle(libcore.LookupClass(Symbols::NoSuchMethodError()));
const Function& throwNew = Function::Handle(
NoSuchMethodError.LookupFunctionAllowPrivate(Symbols::ThrowNew()));
return DartEntry::InvokeFunction(throwNew, args);
}
RawObject* Class::InvokeGetter(const String& getter_name,
bool throw_nsm_if_absent,
bool respect_reflectable) const {
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
const Error& error = Error::Handle(zone, EnsureIsFinalized(thread));
if (!error.IsNull()) {
return error.raw();
}
// Note static fields do not have implicit getters.
const Field& field = Field::Handle(zone, LookupStaticField(getter_name));
if (field.IsNull() || field.IsUninitialized()) {
const String& internal_getter_name =
String::Handle(zone, Field::GetterName(getter_name));
Function& getter =
Function::Handle(zone, LookupStaticFunction(internal_getter_name));
if (getter.IsNull() || (respect_reflectable && !getter.is_reflectable())) {
if (getter.IsNull()) {
getter = LookupStaticFunction(getter_name);
if (!getter.IsNull()) {
// Looking for a getter but found a regular method: closurize it.
const Function& closure_function =
Function::Handle(zone, getter.ImplicitClosureFunction());
return closure_function.ImplicitStaticClosure();
}
}
if (throw_nsm_if_absent) {
return ThrowNoSuchMethod(
AbstractType::Handle(zone, RareType()), getter_name,
Object::null_array(), Object::null_array(),
InvocationMirror::kStatic, InvocationMirror::kGetter);
}
// Fall through case: Indicate that we didn't find any function or field
// using a special null instance. This is different from a field being
// null. Callers make sure that this null does not leak into Dartland.
return Object::sentinel().raw();
}
// Invoke the getter and return the result.
return DartEntry::InvokeFunction(getter, Object::empty_array());
}
return field.StaticValue();
}
RawObject* Class::InvokeSetter(const String& setter_name,
const Instance& value,
bool respect_reflectable) const {
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
const Error& error = Error::Handle(zone, EnsureIsFinalized(thread));
if (!error.IsNull()) {
return error.raw();
}
// Check for real fields and user-defined setters.
const Field& field = Field::Handle(zone, LookupStaticField(setter_name));
const String& internal_setter_name =
String::Handle(zone, Field::SetterName(setter_name));
if (field.IsNull()) {
const Function& setter =
Function::Handle(zone, LookupStaticFunction(internal_setter_name));
const int kNumArgs = 1;
const Array& args = Array::Handle(zone, Array::New(kNumArgs));
args.SetAt(0, value);
if (setter.IsNull() || (respect_reflectable && !setter.is_reflectable())) {
return ThrowNoSuchMethod(AbstractType::Handle(zone, RareType()),
internal_setter_name, args, Object::null_array(),
InvocationMirror::kStatic,
InvocationMirror::kSetter);
}
// Invoke the setter and return the result.
return DartEntry::InvokeFunction(setter, args);
}
if (field.is_final() || (respect_reflectable && !field.is_reflectable())) {
const int kNumArgs = 1;
const Array& args = Array::Handle(zone, Array::New(kNumArgs));
args.SetAt(0, value);
return ThrowNoSuchMethod(AbstractType::Handle(zone, RareType()),
internal_setter_name, args, Object::null_array(),
InvocationMirror::kStatic,
InvocationMirror::kSetter);
}
field.SetStaticValue(value);
return value.raw();
}
RawObject* Class::Invoke(const String& function_name,
const Array& args,
const Array& arg_names,
bool respect_reflectable) const {
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
// TODO(regis): Support invocation of generic functions with type arguments.
const int kTypeArgsLen = 0;
const Error& error = Error::Handle(zone, EnsureIsFinalized(thread));
if (!error.IsNull()) {
return error.raw();
}
Function& function =
Function::Handle(zone, LookupStaticFunction(function_name));
if (function.IsNull()) {
// Didn't find a method: try to find a getter and invoke call on its result.
const String& getter_name =
String::Handle(zone, Field::GetterName(function_name));
function = LookupStaticFunction(getter_name);
if (!function.IsNull()) {
// Invoke the getter.
const Object& getter_result = Object::Handle(
zone, DartEntry::InvokeFunction(function, Object::empty_array()));
if (getter_result.IsError()) {
return getter_result.raw();
}
// Make room for the closure (receiver) in the argument list.
const intptr_t num_args = args.Length();
const Array& call_args = Array::Handle(zone, Array::New(num_args + 1));
Object& temp = Object::Handle(zone);
for (int i = 0; i < num_args; i++) {
temp = args.At(i);
call_args.SetAt(i + 1, temp);
}
call_args.SetAt(0, getter_result);
const Array& call_args_descriptor_array =
Array::Handle(zone, ArgumentsDescriptor::New(
kTypeArgsLen, call_args.Length(), arg_names));
// Call the closure.
return DartEntry::InvokeClosure(call_args, call_args_descriptor_array);
}
}
const Array& args_descriptor_array = Array::Handle(
zone, ArgumentsDescriptor::New(kTypeArgsLen, args.Length(), arg_names));
ArgumentsDescriptor args_descriptor(args_descriptor_array);
if (function.IsNull() || !function.AreValidArguments(args_descriptor, NULL) ||
(respect_reflectable && !function.is_reflectable())) {
return ThrowNoSuchMethod(
AbstractType::Handle(zone, RareType()), function_name, args, arg_names,
InvocationMirror::kStatic, InvocationMirror::kMethod);
}
return DartEntry::InvokeFunction(function, args, args_descriptor_array);
}
RawObject* Class::Evaluate(const String& expr,
const Array& param_names,
const Array& param_values) const {
@ -11209,6 +11399,15 @@ RawObject* Library::LookupLocalObject(const String& name) const {
return LookupEntry(name, &index);
}
RawObject* Library::LookupLocalOrReExportObject(const String& name) const {
intptr_t index;
const Object& result = Object::Handle(LookupEntry(name, &index));
if (!result.IsNull() && !result.IsLibraryPrefix()) {
return result.raw();
}
return LookupReExport(name);
}
RawField* Library::LookupFieldAllowPrivate(const String& name) const {
Object& obj = Object::Handle(LookupObjectAllowPrivate(name));
if (obj.IsField()) {
@ -11612,6 +11811,168 @@ void Library::InitCoreLibrary(Isolate* isolate) {
core_lib.AddObject(cls, String::Handle(zone, cls.Name()));
}
// Invoke the function, or noSuchMethod if it is null.
static RawObject* InvokeInstanceFunction(const Instance& receiver,
const Function& function,
const String& target_name,
const Array& args,
const Array& args_descriptor_array,
bool respect_reflectable) {
// Note "args" is already the internal arguments with the receiver as the
// first element.
ArgumentsDescriptor args_descriptor(args_descriptor_array);
if (function.IsNull() || !function.AreValidArguments(args_descriptor, NULL) ||
(respect_reflectable && !function.is_reflectable())) {
return DartEntry::InvokeNoSuchMethod(receiver, target_name, args,
args_descriptor_array);
}
return DartEntry::InvokeFunction(function, args, args_descriptor_array);
}
RawObject* Library::InvokeGetter(const String& getter_name,
bool throw_nsm_if_absent,
bool respect_reflectable) const {
Object& obj = Object::Handle(LookupLocalOrReExportObject(getter_name));
Function& getter = Function::Handle();
if (obj.IsField()) {
const Field& field = Field::Cast(obj);
if (!field.IsUninitialized()) {
return field.StaticValue();
}
// An uninitialized field was found. Check for a getter in the field's
// owner class.
const Class& klass = Class::Handle(field.Owner());
const String& internal_getter_name =
String::Handle(Field::GetterName(getter_name));
getter = klass.LookupStaticFunction(internal_getter_name);
} else {
// No field found. Check for a getter in the lib.
const String& internal_getter_name =
String::Handle(Field::GetterName(getter_name));
obj = LookupLocalOrReExportObject(internal_getter_name);
if (obj.IsFunction()) {
getter = Function::Cast(obj).raw();
} else {
obj = LookupLocalOrReExportObject(getter_name);
if (obj.IsFunction()) {
// Looking for a getter but found a regular method: closurize it.
const Function& closure_function =
Function::Handle(Function::Cast(obj).ImplicitClosureFunction());
return closure_function.ImplicitStaticClosure();
}
}
}
if (getter.IsNull() || (respect_reflectable && !getter.is_reflectable())) {
if (throw_nsm_if_absent) {
return ThrowNoSuchMethod(
AbstractType::Handle(Class::Handle(toplevel_class()).RareType()),
getter_name, Object::null_array(), Object::null_array(),
InvocationMirror::kTopLevel, InvocationMirror::kGetter);
}
// Fall through case: Indicate that we didn't find any function or field
// using a special null instance. This is different from a field being null.
// Callers make sure that this null does not leak into Dartland.
return Object::sentinel().raw();
}
// Invoke the getter and return the result.
return DartEntry::InvokeFunction(getter, Object::empty_array());
}
RawObject* Library::InvokeSetter(const String& setter_name,
const Instance& value,
bool respect_reflectable) const {
Object& obj = Object::Handle(LookupLocalOrReExportObject(setter_name));
const String& internal_setter_name =
String::Handle(Field::SetterName(setter_name));
if (obj.IsField()) {
const Field& field = Field::Cast(obj);
if (field.is_final() || (respect_reflectable && !field.is_reflectable())) {
const int kNumArgs = 1;
const Array& args = Array::Handle(Array::New(kNumArgs));
args.SetAt(0, value);
return ThrowNoSuchMethod(
AbstractType::Handle(Class::Handle(toplevel_class()).RareType()),
internal_setter_name, args, Object::null_array(),
InvocationMirror::kTopLevel, InvocationMirror::kSetter);
}
field.SetStaticValue(value);
return value.raw();
}
Function& setter = Function::Handle();
obj = LookupLocalOrReExportObject(internal_setter_name);
if (obj.IsFunction()) {
setter ^= obj.raw();
}
const int kNumArgs = 1;
const Array& args = Array::Handle(Array::New(kNumArgs));
args.SetAt(0, value);
if (setter.IsNull() || (respect_reflectable && !setter.is_reflectable())) {
return ThrowNoSuchMethod(
AbstractType::Handle(Class::Handle(toplevel_class()).RareType()),
internal_setter_name, args, Object::null_array(),
InvocationMirror::kTopLevel, InvocationMirror::kSetter);
}
return DartEntry::InvokeFunction(setter, args);
}
RawObject* Library::Invoke(const String& function_name,
const Array& args,
const Array& arg_names,
bool respect_reflectable) const {
// TODO(regis): Support invocation of generic functions with type arguments.
const int kTypeArgsLen = 0;
Function& function = Function::Handle();
Object& obj = Object::Handle(LookupLocalOrReExportObject(function_name));
if (obj.IsFunction()) {
function ^= obj.raw();
}
if (function.IsNull()) {
// Didn't find a method: try to find a getter and invoke call on its result.
const Object& getter_result =
Object::Handle(InvokeGetter(function_name, false));
if (getter_result.raw() != Object::sentinel().raw()) {
// Make room for the closure (receiver) in arguments.
intptr_t numArgs = args.Length();
const Array& call_args = Array::Handle(Array::New(numArgs + 1));
Object& temp = Object::Handle();
for (int i = 0; i < numArgs; i++) {
temp = args.At(i);
call_args.SetAt(i + 1, temp);
}
call_args.SetAt(0, getter_result);
const Array& call_args_descriptor_array =
Array::Handle(ArgumentsDescriptor::New(
kTypeArgsLen, call_args.Length(), arg_names));
// Call closure.
return DartEntry::InvokeClosure(call_args, call_args_descriptor_array);
}
}
const Array& args_descriptor_array = Array::Handle(
ArgumentsDescriptor::New(kTypeArgsLen, args.Length(), arg_names));
ArgumentsDescriptor args_descriptor(args_descriptor_array);
if (function.IsNull() || !function.AreValidArguments(args_descriptor, NULL) ||
(respect_reflectable && !function.is_reflectable())) {
return ThrowNoSuchMethod(
AbstractType::Handle(Class::Handle(toplevel_class()).RareType()),
function_name, args, arg_names, InvocationMirror::kTopLevel,
InvocationMirror::kMethod);
}
return DartEntry::InvokeFunction(function, args, args_descriptor_array);
}
RawObject* Library::Evaluate(const String& expr,
const Array& param_names,
const Array& param_values) const {
@ -16028,6 +16389,107 @@ const char* UnwindError::ToCString() const {
return "UnwindError";
}
RawObject* Instance::InvokeGetter(const String& getter_name,
bool respect_reflectable) const {
Zone* zone = Thread::Current()->zone();
Class& klass = Class::Handle(zone, clazz());
const String& internal_getter_name =
String::Handle(zone, Field::GetterName(getter_name));
Function& function = Function::Handle(
zone, Resolver::ResolveDynamicAnyArgs(zone, klass, internal_getter_name));
// Check for method extraction when method extractors are not created.
if (function.IsNull() && !FLAG_lazy_dispatchers) {
function = Resolver::ResolveDynamicAnyArgs(zone, klass, getter_name);
if (!function.IsNull()) {
const Function& closure_function =
Function::Handle(zone, function.ImplicitClosureFunction());
return closure_function.ImplicitInstanceClosure(*this);
}
}
const int kTypeArgsLen = 0;
const int kNumArgs = 1;
const Array& args = Array::Handle(zone, Array::New(kNumArgs));
args.SetAt(0, *this);
const Array& args_descriptor = Array::Handle(
zone, ArgumentsDescriptor::New(kTypeArgsLen, args.Length()));
return InvokeInstanceFunction(*this, function, internal_getter_name, args,
args_descriptor, respect_reflectable);
}
RawObject* Instance::InvokeSetter(const String& setter_name,
const Instance& value,
bool respect_reflectable) const {
Zone* zone = Thread::Current()->zone();
const Class& klass = Class::Handle(zone, clazz());
const String& internal_setter_name =
String::Handle(zone, Field::SetterName(setter_name));
const Function& setter = Function::Handle(
zone, Resolver::ResolveDynamicAnyArgs(zone, klass, internal_setter_name));
const int kTypeArgsLen = 0;
const int kNumArgs = 2;
const Array& args = Array::Handle(zone, Array::New(kNumArgs));
args.SetAt(0, *this);
args.SetAt(1, value);
const Array& args_descriptor = Array::Handle(
zone, ArgumentsDescriptor::New(kTypeArgsLen, args.Length()));
return InvokeInstanceFunction(*this, setter, internal_setter_name, args,
args_descriptor, respect_reflectable);
}
RawObject* Instance::Invoke(const String& function_name,
const Array& args,
const Array& arg_names,
bool respect_reflectable) const {
Zone* zone = Thread::Current()->zone();
Class& klass = Class::Handle(zone, clazz());
Function& function = Function::Handle(
zone, Resolver::ResolveDynamicAnyArgs(zone, klass, function_name));
// TODO(regis): Support invocation of generic functions with type arguments.
const int kTypeArgsLen = 0;
const Array& args_descriptor = Array::Handle(
zone, ArgumentsDescriptor::New(kTypeArgsLen, args.Length(), arg_names));
if (function.IsNull()) {
// Didn't find a method: try to find a getter and invoke call on its result.
const String& getter_name =
String::Handle(zone, Field::GetterName(function_name));
function = Resolver::ResolveDynamicAnyArgs(zone, klass, getter_name);
if (!function.IsNull()) {
ASSERT(function.kind() != RawFunction::kMethodExtractor);
// Invoke the getter.
const int kNumArgs = 1;
const Array& getter_args = Array::Handle(zone, Array::New(kNumArgs));
getter_args.SetAt(0, *this);
const Array& getter_args_descriptor = Array::Handle(
zone, ArgumentsDescriptor::New(kTypeArgsLen, getter_args.Length()));
const Object& getter_result = Object::Handle(
zone,
InvokeInstanceFunction(*this, function, getter_name, getter_args,
getter_args_descriptor, respect_reflectable));
if (getter_result.IsError()) {
return getter_result.raw();
}
// Replace the closure as the receiver in the arguments list.
args.SetAt(0, getter_result);
// Call the closure.
return DartEntry::InvokeClosure(args, args_descriptor);
}
}
// Found an ordinary method.
return InvokeInstanceFunction(*this, function, function_name, args,
args_descriptor, respect_reflectable);
}
RawObject* Instance::Evaluate(const Class& method_cls,
const String& expr,
const Array& param_names,

View file

@ -1374,6 +1374,17 @@ class Class : public Object {
// Return true on success, or false and error otherwise.
bool ApplyPatch(const Class& patch, Error* error) const;
RawObject* Invoke(const String& selector,
const Array& arguments,
const Array& argument_names,
bool respect_reflectable = true) const;
RawObject* InvokeGetter(const String& selector,
bool throw_nsm_if_absent,
bool respect_reflectable = true) const;
RawObject* InvokeSetter(const String& selector,
const Instance& argument,
bool respect_reflectable = true) const;
// Evaluate the given expression as if it appeared in a static method of this
// class and return the resulting value, or an error object if evaluating the
// expression fails. The method has the formal (type) parameters given in
@ -3808,6 +3819,17 @@ class Library : public Object {
static RawLibrary* New(const String& url);
RawObject* Invoke(const String& selector,
const Array& arguments,
const Array& argument_names,
bool respect_reflectable = true) const;
RawObject* InvokeGetter(const String& selector,
bool throw_nsm_if_absent,
bool respect_reflectable = true) const;
RawObject* InvokeSetter(const String& selector,
const Instance& argument,
bool respect_reflectable = true) const;
// Evaluate the given expression as if it appeared in an top-level method of
// this library and return the resulting value, or an error object if
// evaluating the expression fails. The method has the formal (type)
@ -3843,6 +3865,7 @@ class Library : public Object {
RawObject* LookupObjectAllowPrivate(const String& name) const;
RawObject* LookupLocalObjectAllowPrivate(const String& name) const;
RawObject* LookupLocalObject(const String& name) const;
RawObject* LookupLocalOrReExportObject(const String& name) const;
RawObject* LookupImportedObject(const String& name) const;
RawClass* LookupClass(const String& name) const;
RawClass* LookupClassAllowPrivate(const String& name) const;
@ -5703,6 +5726,16 @@ class Instance : public Object {
// (if not NULL) to call.
bool IsCallable(Function* function) const;
RawObject* Invoke(const String& selector,
const Array& arguments,
const Array& argument_names,
bool respect_reflectable = true) const;
RawObject* InvokeGetter(const String& selector,
bool respect_reflectable = true) const;
RawObject* InvokeSetter(const String& selector,
const Instance& argument,
bool respect_reflectable = true) const;
// Evaluate the given expression as if it appeared in an instance method of
// this instance and return the resulting value, or an error object if
// evaluating the expression fails. The method has the formal (type)

View file

@ -2249,6 +2249,132 @@ static bool GetReachableSize(Thread* thread, JSONStream* js) {
return true;
}
static const MethodParameter* invoke_params[] = {
RUNNABLE_ISOLATE_PARAMETER,
NULL,
};
static bool Invoke(Thread* thread, JSONStream* js) {
if (CheckDebuggerDisabled(thread, js)) {
return true;
}
const char* receiver_id = js->LookupParam("receiverId");
if (receiver_id == NULL) {
PrintMissingParamError(js, "receiverId");
return true;
}
const char* selector_cstr = js->LookupParam("selector");
if (selector_cstr == NULL) {
PrintMissingParamError(js, "selector");
return true;
}
const char* argument_ids = js->LookupParam("argumentIds");
if (argument_ids == NULL) {
PrintMissingParamError(js, "argumentIds");
return true;
}
Zone* zone = thread->zone();
ObjectIdRing::LookupResult lookup_result;
Object& receiver = Object::Handle(
zone, LookupHeapObject(thread, receiver_id, &lookup_result));
if (receiver.raw() == Object::sentinel().raw()) {
if (lookup_result == ObjectIdRing::kCollected) {
PrintSentinel(js, kCollectedSentinel);
} else if (lookup_result == ObjectIdRing::kExpired) {
PrintSentinel(js, kExpiredSentinel);
} else {
PrintInvalidParamError(js, "receiverId");
}
return true;
}
const GrowableObjectArray& growable_args =
GrowableObjectArray::Handle(zone, GrowableObjectArray::New());
bool is_instance = (receiver.IsInstance() || receiver.IsNull()) &&
!ContainsNonInstance(receiver);
if (is_instance) {
growable_args.Add(receiver);
}
intptr_t n = strlen(argument_ids);
if ((n < 2) || (argument_ids[0] != '[') || (argument_ids[n - 1] != ']')) {
PrintInvalidParamError(js, "argumentIds");
return true;
}
if (n > 2) {
intptr_t start = 1;
while (start < n) {
intptr_t end = start;
while ((argument_ids[end + 1] != ',') && (argument_ids[end + 1] != ']')) {
end++;
}
if (end == start) {
// Empty element.
PrintInvalidParamError(js, "argumentIds");
return true;
}
const char* argument_id =
zone->MakeCopyOfStringN(&argument_ids[start], end - start + 1);
ObjectIdRing::LookupResult lookup_result;
Object& argument = Object::Handle(
zone, LookupHeapObject(thread, argument_id, &lookup_result));
if (argument.raw() == Object::sentinel().raw()) {
if (lookup_result == ObjectIdRing::kCollected) {
PrintSentinel(js, kCollectedSentinel);
} else if (lookup_result == ObjectIdRing::kExpired) {
PrintSentinel(js, kExpiredSentinel);
} else {
PrintInvalidParamError(js, "argumentIds");
}
return true;
}
growable_args.Add(argument);
start = end + 3;
}
}
const String& selector = String::Handle(zone, String::New(selector_cstr));
const Array& args =
Array::Handle(zone, Array::MakeFixedLength(growable_args));
const Array& arg_names = Object::empty_array();
if (receiver.IsLibrary()) {
const Library& lib = Library::Cast(receiver);
const Object& result =
Object::Handle(zone, lib.Invoke(selector, args, arg_names));
result.PrintJSON(js, true);
return true;
}
if (receiver.IsClass()) {
const Class& cls = Class::Cast(receiver);
const Object& result =
Object::Handle(zone, cls.Invoke(selector, args, arg_names));
result.PrintJSON(js, true);
return true;
}
if (is_instance) {
// We don't use Instance::Cast here because it doesn't allow null.
Instance& instance = Instance::Handle(zone);
instance ^= receiver.raw();
const Object& result =
Object::Handle(zone, instance.Invoke(selector, args, arg_names));
result.PrintJSON(js, true);
return true;
}
js->PrintError(kInvalidParams,
"%s: invalid 'receiverId' parameter: "
"Cannot invoke against a VM-internal object",
js->method());
return true;
}
static const MethodParameter* evaluate_params[] = {
RUNNABLE_ISOLATE_PARAMETER, NULL,
};
@ -4729,6 +4855,7 @@ static const ServiceMethodDescriptor service_methods_[] = {
get_vm_timeline_params },
{ "_getVMTimelineFlags", GetVMTimelineFlags,
get_vm_timeline_flags_params },
{ "invoke", Invoke, invoke_params },
{ "kill", Kill, kill_params },
{ "pause", Pause,
pause_params },

View file

@ -15,7 +15,7 @@
namespace dart {
#define SERVICE_PROTOCOL_MAJOR_VERSION 3
#define SERVICE_PROTOCOL_MINOR_VERSION 9
#define SERVICE_PROTOCOL_MINOR_VERSION 10
class Array;
class EmbedderServiceHandler;

View file

@ -470,6 +470,40 @@ See [Breakpoint](#breakpoint).
Note that breakpoints are added and removed on a per-isolate basis.
### invoke
```
@Instance|@Error|Sentinel invoke(string isolateId,
string targetId,
string selector,
string[] argumentIds)
```
The _invoke_ RPC is used to perform regular method invocation on some receiver,
as if by dart:mirror's ObjectMirror.invoke. Note this does not provide a way to
perform getter, setter or constructor invocation.
_targetId_ may refer to a [Library](#library), [Class](#class), or
[Instance](#instance).
Each elements of _argumentId_ may refer to an [Instance](#instance).
If _targetId_ or any element of _argumentIds_ is a temporary id which has
expired, then the _Expired_ [Sentinel](#sentinel) is returned.
If _targetId_ or any element of _argumentIds_ refers to an object which has been
collected by the VM's garbage collector, then the _Collected_
[Sentinel](#sentinel) is returned.
If invocation triggers a failed compilation then [rpc error](#rpc-error) 113
"Expression compilation error" is returned.
If an runtime error occurs while evaluating the invocation, an [@Error](#error)
reference will be returned.
If the invocation is evaluated successfully, an [@Instance](#instance)
reference will be returned.
### evaluate
```
@ -2670,5 +2704,8 @@ version | comments
3.5 | Add the error field to SourceReportRange. Clarify definition of token position. Add "Isolate must be paused" error code.
3.6 | Add 'scopeStartTokenPos', 'scopeEndTokenPos', and 'declarationTokenPos' to BoundVariable. Add 'PausePostRequest' event kind. Add 'Rewind' StepOption. Add error code 107 (isolate cannot resume). Add 'reloadSources' RPC and related error codes. Add optional parameter 'scope' to 'evaluate' and 'evaluateInFrame'.
3.7 | Add 'setFlag'.
3.8 | Add 'kill'.
3.9 | Changed numbers for errors related to service extensions.
3.10 | Add 'invoke'.
[discuss-list]: https://groups.google.com/a/dartlang.org/forum/#!forum/observatory-discuss

View file

@ -470,6 +470,40 @@ See [Breakpoint](#breakpoint).
Note that breakpoints are added and removed on a per-isolate basis.
### invoke
```
@Instance|@Error|Sentinel invoke(string isolateId,
string targetId,
string selector,
string[] argumentIds)
```
The _invoke_ RPC is used to perform regular method invocation on some receiver,
as if by dart:mirror's ObjectMirror.invoke. Note this does not provide a way to
perform getter, setter or constructor invocation.
_targetId_ may refer to a [Library](#library), [Class](#class), or
[Instance](#instance).
Each elements of _argumentId_ may refer to an [Instance](#instance).
If _targetId_ or any element of _argumentIds_ is a temporary id which has
expired, then the _Expired_ [Sentinel](#sentinel) is returned.
If _targetId_ or any element of _argumentIds_ refers to an object which has been
collected by the VM's garbage collector, then the _Collected_
[Sentinel](#sentinel) is returned.
If invocation triggers a failed compilation then [rpc error](#rpc-error) 113
"Expression compilation error" is returned.
If an runtime error occurs while evaluating the invocation, an [@Error](#error)
reference will be returned.
If the invocation is evaluated successfully, an [@Instance](#instance)
reference will be returned.
### evaluate
```
@ -2670,5 +2704,8 @@ version | comments
3.5 | Add the error field to SourceReportRange. Clarify definition of token position. Add "Isolate must be paused" error code.
3.6 | Add 'scopeStartTokenPos', 'scopeEndTokenPos', and 'declarationTokenPos' to BoundVariable. Add 'PausePostRequest' event kind. Add 'Rewind' StepOption. Add error code 107 (isolate cannot resume). Add 'reloadSources' RPC and related error codes. Add optional parameter 'scope' to 'evaluate' and 'evaluateInFrame'.
3.7 | Add 'setFlag'.
3.8 | Add 'kill'.
3.9 | Changed numbers for errors related to service extensions.
3.10 | Add 'invoke'.
[discuss-list]: https://groups.google.com/a/dartlang.org/forum/#!forum/observatory-discuss