diff --git a/runtime/lib/invocation_mirror.cc b/runtime/lib/invocation_mirror.cc new file mode 100644 index 00000000000..2f5e3cc7bbe --- /dev/null +++ b/runtime/lib/invocation_mirror.cc @@ -0,0 +1,129 @@ +// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/bootstrap_natives.h" + +#include "vm/compiler.h" +#include "vm/dart_entry.h" +#include "vm/exceptions.h" +#include "vm/native_entry.h" +#include "vm/object_store.h" +#include "vm/resolver.h" +#include "vm/symbols.h" + +namespace dart { + +// TODO(regis): Factorize this static helper copied from code_generator.cc. +static RawObject* InvokeNoSuchMethod(const Instance& receiver, + const String& target_name, + const Array& arguments_descriptor, + const Array& arguments) { + // Allocate an InvocationMirror object. + const Library& core_lib = Library::Handle(Library::CoreLibrary()); + const String& invocation_mirror_name = + String::Handle(Symbols::InvocationMirror()); + Class& invocation_mirror_class = + Class::Handle(core_lib.LookupClassAllowPrivate(invocation_mirror_name)); + ASSERT(!invocation_mirror_class.IsNull()); + const String& allocation_function_name = + String::Handle(Symbols::AllocateInvocationMirror()); + const Function& allocation_function = Function::Handle( + Resolver::ResolveStaticByName(invocation_mirror_class, + allocation_function_name, + Resolver::kIsQualified)); + ASSERT(!allocation_function.IsNull()); + GrowableArray allocation_arguments(3); + allocation_arguments.Add(&target_name); + allocation_arguments.Add(&arguments_descriptor); + allocation_arguments.Add(&arguments); + const Array& kNoArgumentNames = Array::Handle(); + const Object& invocation_mirror = + Object::Handle(DartEntry::InvokeStatic(allocation_function, + allocation_arguments, + kNoArgumentNames)); + + const String& function_name = String::Handle(Symbols::NoSuchMethod()); + const int kNumArguments = 2; + const int kNumNamedArguments = 0; + const Function& function = Function::Handle( + Resolver::ResolveDynamic(receiver, + function_name, + kNumArguments, + kNumNamedArguments)); + ASSERT(!function.IsNull()); + GrowableArray invoke_arguments(1); + invoke_arguments.Add(&invocation_mirror); + const Object& result = + Object::Handle(DartEntry::InvokeDynamic(receiver, + function, + invoke_arguments, + kNoArgumentNames)); + if (result.IsError()) { + Exceptions::PropagateError(Error::Cast(result)); + } + return result.raw(); +} + + +DEFINE_NATIVE_ENTRY(InvocationMirror_invoke, 4) { + const Instance& receiver = Instance::CheckedHandle(arguments->NativeArgAt(0)); + const String& fun_name = String::CheckedHandle(arguments->NativeArgAt(1)); + const Array& fun_args_desc = Array::CheckedHandle(arguments->NativeArgAt(2)); + const Array& fun_arguments = Array::CheckedHandle(arguments->NativeArgAt(3)); + + // Allocate a fixed-length array duplicating the original function arguments + // and replace the receiver. + const int num_arguments = fun_arguments.Length(); + const Array& invoke_arguments = Array::Handle(Array::New(num_arguments)); + invoke_arguments.SetAt(0, receiver); + Object& arg = Object::Handle(); + for (int i = 1; i < num_arguments; i++) { + arg = fun_arguments.At(i); + invoke_arguments.SetAt(i, arg); + } + // Resolve dynamic function given by name. + const ArgumentsDescriptor args_desc(fun_args_desc); + const Function& function = Function::Handle( + Resolver::ResolveDynamic(receiver, + fun_name, + args_desc.Count(), + args_desc.NamedCount())); + if (function.IsNull()) { + return InvokeNoSuchMethod(receiver, + fun_name, + fun_args_desc, + invoke_arguments); + } + // TODO(regis): Simply call DartEntry::InvokeDynamic once it is modified to + // take an arguments descriptor array and an arguments array. + // Get the entrypoint corresponding to the instance function. This will + // result in a compilation of the function if it is not already compiled. + ASSERT(!function.IsNull()); + if (!function.HasCode()) { + const Error& error = Error::Handle(Compiler::CompileFunction(function)); + if (!error.IsNull()) { + Exceptions::PropagateError(error); + } + } + // Set up arguments as GrowableArray. + GrowableArray args(num_arguments); + for (int i = 0; i < num_arguments; i++) { + args.Add(&Object::ZoneHandle(invoke_arguments.At(i))); + } + // Now call the invoke stub which will invoke the function. + DartEntry::invokestub entrypoint = reinterpret_cast( + StubCode::InvokeDartCodeEntryPoint()); + const Code& code = Code::Handle(function.CurrentCode()); + ASSERT(!code.IsNull()); + const Context& context = Context::ZoneHandle( + Isolate::Current()->object_store()->empty_context()); + const Object& result = Object::Handle( + entrypoint(code.EntryPoint(), fun_args_desc, args.data(), context)); + if (result.IsError()) { + Exceptions::PropagateError(Error::Cast(result)); + } + return result.raw(); +} + +} // namespace dart diff --git a/runtime/lib/invocation_mirror_patch.dart b/runtime/lib/invocation_mirror_patch.dart index e81fe50293b..229a3dbf688 100644 --- a/runtime/lib/invocation_mirror_patch.dart +++ b/runtime/lib/invocation_mirror_patch.dart @@ -3,64 +3,112 @@ // BSD-style license that can be found in the LICENSE file. class _InvocationMirror implements InvocationMirror { - static final int METHOD = 0; - static final int GETTER = 1; - static final int SETTER = 2; + // Constants describing the invocation type. + static final int _METHOD = 0; + static final int _GETTER = 1; + static final int _SETTER = 2; - // TODO(regis): Compute lazily the value of these fields, and save the - // arguments passed into _allocateInvocationMirror. + // Internal representation of the invocation mirror. + final String _functionName; + final List _argumentsDescriptor; + final List _arguments; - final String memberName; - final List positionalArguments; - final Map namedArguments; + // External representation of the invocation mirror; populated on demand. + String _memberName; + int _type; + List _positionalArguments; + Map _namedArguments; - final int _type; - - _InvocationMirror(this.memberName, - this._type, - this.positionalArguments, - this.namedArguments); - - static _allocateInvocationMirror(String name, - List argumentsDescriptor, - List arguments) { - var memberName; - var type; - if (name.startsWith("get:")) { - type = GETTER; - memberName = name.substring(4); - } else if (name.startsWith("set:")) { - type = SETTER; - memberName = name.substring(4).concat("="); + void _setMemberNameAndType() { + if (_functionName.startsWith("get:")) { + _type = _GETTER; + _memberName = _functionName.substring(4); + } else if (_functionName.startsWith("set:")) { + _type = _SETTER; + _memberName = _functionName.substring(4).concat("="); } else { - type = METHOD; - memberName = name; + _type = _METHOD; + _memberName = _functionName; } - // Exclude receiver. - int numArguments = argumentsDescriptor[0] - 1; - int numPositionalArguments = argumentsDescriptor[1] - 1; - int numNamedArguments = numArguments - numPositionalArguments; - List positionalArguments = arguments.getRange(1, numPositionalArguments); - Map namedArguments; - if (numNamedArguments > 0) { - namedArguments = new Map(); - for (int i = 0; i < numNamedArguments; i++) { - String arg_name = argumentsDescriptor[2 + 2*i]; - var arg_value = arguments[argumentsDescriptor[3 + 2*i]]; - namedArguments[arg_name] = arg_value; - } - } - return new _InvocationMirror(memberName, type, - positionalArguments, namedArguments); } - bool get isMethod => _type == METHOD; - bool get isAccessor => _type != METHOD; - bool get isGetter => _type == GETTER; - bool get isSetter => _type == SETTER; + String get memberName { + if (_memberName == null) { + _setMemberNameAndType(); + } + return _memberName; + } + + List get positionalArguments { + if (_positionalArguments == null) { + // Exclude receiver. + int numPositionalArguments = _argumentsDescriptor[1] - 1; + _positionalArguments = _arguments.getRange(1, numPositionalArguments); + } + return _positionalArguments; + } + + Map get namedArguments { + if (_namedArguments == null) { + _namedArguments = new Map(); + int numArguments = _argumentsDescriptor[0] - 1; // Exclude receiver. + int numPositionalArguments = _argumentsDescriptor[1] - 1; + int numNamedArguments = numArguments - numPositionalArguments; + for (int i = 0; i < numNamedArguments; i++) { + String arg_name = _argumentsDescriptor[2 + 2*i]; + var arg_value = _arguments[_argumentsDescriptor[3 + 2*i]]; + _namedArguments[arg_name] = arg_value; + } + } + return _namedArguments; + } + + bool get isMethod { + if (_memberName == null) { + _setMemberNameAndType(); + } + return _type == _METHOD; + } + + bool get isAccessor { + if (_memberName == null) { + _setMemberNameAndType(); + } + return _type != _METHOD; + } + + bool get isGetter { + if (_memberName == null) { + _setMemberNameAndType(); + } + return _type == _GETTER; + } + + bool get isSetter { + if (_memberName == null) { + _setMemberNameAndType(); + } + return _type == _SETTER; + } + + _InvocationMirror(this._functionName, + this._argumentsDescriptor, + this._arguments); + + static _allocateInvocationMirror(String functionName, + List argumentsDescriptor, + List arguments) { + return new _InvocationMirror(functionName, argumentsDescriptor, arguments); + } + + static _invoke(Object receiver, + String functionName, + List argumentsDescriptor, + List arguments) + native "InvocationMirror_invoke"; invokeOn(Object receiver) { - throw new UnsupportedError("invokeOn not implemented yet"); + _invoke(receiver, _functionName, _argumentsDescriptor, _arguments); } } diff --git a/runtime/lib/lib_sources.gypi b/runtime/lib/lib_sources.gypi index edfd34fd6f4..415dd58f86e 100644 --- a/runtime/lib/lib_sources.gypi +++ b/runtime/lib/lib_sources.gypi @@ -29,6 +29,7 @@ 'integers.cc', 'integers.dart', 'integers_patch.dart', + 'invocation_mirror.cc', 'invocation_mirror_patch.dart', 'map_patch.dart', 'object.cc', diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h index 1967cf2d3c8..ea7e61c9726 100644 --- a/runtime/vm/bootstrap_natives.h +++ b/runtime/vm/bootstrap_natives.h @@ -17,6 +17,7 @@ namespace dart { V(Object_noSuchMethod, 5) \ V(Object_runtimeType, 1) \ V(Function_apply, 2) \ + V(InvocationMirror_invoke, 4) \ V(AbstractType_toString, 1) \ V(Integer_bitAndFromInteger, 2) \ V(Integer_bitOrFromInteger, 2) \ diff --git a/runtime/vm/code_generator.cc b/runtime/vm/code_generator.cc index 86f32d4b7ff..283392ed9cd 100644 --- a/runtime/vm/code_generator.cc +++ b/runtime/vm/code_generator.cc @@ -1239,7 +1239,7 @@ static bool ResolveCallThroughGetter(const Instance& receiver, const String& getter_name = String::Handle(Field::GetterName(target_name)); const int kNumArguments = 1; const int kNumNamedArguments = 0; - const Function& getter = Function::ZoneHandle( + const Function& getter = Function::Handle( Resolver::ResolveDynamicForReceiverClass(receiver_class, getter_name, kNumArguments, @@ -1295,7 +1295,7 @@ static RawObject* InvokeNoSuchMethod(const Instance& receiver, ASSERT(!invocation_mirror_class.IsNull()); const String& allocation_function_name = String::Handle(Symbols::AllocateInvocationMirror()); - const Function& allocation_function = Function::ZoneHandle( + const Function& allocation_function = Function::Handle( Resolver::ResolveStaticByName(invocation_mirror_class, allocation_function_name, Resolver::kIsQualified)); @@ -1313,11 +1313,11 @@ static RawObject* InvokeNoSuchMethod(const Instance& receiver, const String& function_name = String::Handle(Symbols::NoSuchMethod()); const int kNumArguments = 2; const int kNumNamedArguments = 0; - const Function& function = - Function::ZoneHandle(Resolver::ResolveDynamic(receiver, - function_name, - kNumArguments, - kNumNamedArguments)); + const Function& function = Function::Handle( + Resolver::ResolveDynamic(receiver, + function_name, + kNumArguments, + kNumNamedArguments)); ASSERT(!function.IsNull()); GrowableArray invoke_arguments(1); invoke_arguments.Add(&invocation_mirror); diff --git a/tests/language/invocation_mirror_invoke_on_test.dart b/tests/language/invocation_mirror_invoke_on_test.dart new file mode 100644 index 00000000000..f03726c5968 --- /dev/null +++ b/tests/language/invocation_mirror_invoke_on_test.dart @@ -0,0 +1,37 @@ +// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// Testing InvocationMirror.invokeOn method; test of issue 7227. + +var reachedSetX = 0; +var reachedGetX = 0; +var reachedM = 0; + +class A { + + set x(val) { + reachedSetX = val; + } + + get x { + reachedGetX = 1; + } + + m() { reachedM = 1; } +} + +class B { + final a = new A(); + noSuchMethod(mirror) => mirror.invokeOn(a); +} + +main () { + var b = new B(); + b.x = 10; + Expect.equals(10, reachedSetX); + b.x; + Expect.equals(1, reachedGetX); + b.m(); + Expect.equals(1, reachedM); +} diff --git a/tests/language/language_dart2js.status b/tests/language/language_dart2js.status index a4ca3529650..3bd28d28456 100644 --- a/tests/language/language_dart2js.status +++ b/tests/language/language_dart2js.status @@ -86,6 +86,7 @@ compile_time_constant_checked3_test/05: Fail, OK compile_time_constant_checked3_test/06: Fail, OK [ $compiler == dart2js ] +invocation_mirror_invoke_on_test: Fail # http://dartbug.com/7425 built_in_identifier_prefix_test: Fail # http://dartbug.com/6972 number_identity2_test: Fail # identity of NaN new_expression_type_args_test/00: Fail # Wrongly reports compile-time error.