[vm/kernel] Pass type variables through ActivationFrame for expression compilation.
Fixes https://github.com/dart-lang/sdk/issues/32376.
Change-Id: I37bbda2dbc6052925aa840865c62a170604c6cdd
Reviewed-on: https://dart-review.googlesource.com/44784
Reviewed-by: Alexander Aprelev <aam@google.com>
Commit-Queue: Samir Jindel <sjindel@google.com>
diff --git a/runtime/observatory/tests/service/evaluate_class_type_parameters_test.dart b/runtime/observatory/tests/service/evaluate_class_type_parameters_test.dart
new file mode 100644
index 0000000..0909420
--- /dev/null
+++ b/runtime/observatory/tests/service/evaluate_class_type_parameters_test.dart
@@ -0,0 +1,58 @@
+// 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.
+
+import 'dart:developer';
+import 'package:observatory/service_io.dart';
+import 'package:unittest/unittest.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+class A<T> {
+ void foo() {
+ debugger();
+ }
+}
+
+class B<S> extends A<int> {
+ void bar() {
+ debugger();
+ }
+}
+
+testFunction() {
+ var v = new B<String>();
+ v.bar();
+ v.foo();
+}
+
+var tests = <IsolateTest>[
+ hasStoppedAtBreakpoint,
+ (Isolate isolate) async {
+ // Make sure we are in the right place.
+ var stack = await isolate.getStack();
+ var topFrame = 0;
+ expect(stack.type, equals('Stack'));
+ expect(await stack['frames'][topFrame].location.getLine(), 20);
+
+ Instance result = await isolate.evalFrame(topFrame, '"\$S"');
+ print(result);
+ expect(result.valueAsString, equals("String"));
+ },
+ resumeIsolate,
+ hasStoppedAtBreakpoint,
+ (Isolate isolate) async {
+ // Make sure we are in the right place.
+ var stack = await isolate.getStack();
+ var topFrame = 0;
+ expect(stack.type, equals('Stack'));
+ expect(await stack['frames'][topFrame].location.getLine(), 14);
+
+ Instance result = await isolate.evalFrame(topFrame, '"\$T"');
+ print(result);
+ expect(result.valueAsString, equals("int"));
+ },
+ resumeIsolate,
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction);
diff --git a/runtime/observatory/tests/service/evaluate_function_type_parameters_test.dart b/runtime/observatory/tests/service/evaluate_function_type_parameters_test.dart
new file mode 100644
index 0000000..1ee01db
--- /dev/null
+++ b/runtime/observatory/tests/service/evaluate_function_type_parameters_test.dart
@@ -0,0 +1,96 @@
+// 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.
+
+import 'dart:developer';
+import 'package:observatory/service_io.dart';
+import 'package:unittest/unittest.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+topLevel<S>() {
+ debugger();
+
+ void inner1<T>() {
+ debugger();
+ }
+
+ inner1<int>();
+
+ void inner2() {
+ debugger();
+ }
+
+ inner2();
+}
+
+class A {
+ foo<T>() {
+ debugger();
+ }
+}
+
+void testMain() {
+ topLevel<String>();
+ (new A()).foo<int>();
+}
+
+var tests = <IsolateTest>[
+ hasStoppedAtBreakpoint,
+ (Isolate isolate) async {
+ // Make sure we are in the right place.
+ var stack = await isolate.getStack();
+ var topFrame = 0;
+ expect(stack.type, equals('Stack'));
+ expect(await stack['frames'][topFrame].location.getLine(), 14);
+
+ Instance result = await isolate.evalFrame(topFrame, "S.toString()");
+ print(result);
+ expect(result.valueAsString, equals("String"));
+ },
+ resumeIsolate,
+ hasStoppedAtBreakpoint,
+ (Isolate isolate) async {
+ // Make sure we are in the right place.
+ var stack = await isolate.getStack();
+ var topFrame = 0;
+ expect(stack.type, equals('Stack'));
+ expect(await stack['frames'][topFrame].location.getLine(), 16);
+
+ Instance result = await isolate.evalFrame(topFrame, "T.toString()");
+ print(result);
+ expect(result.valueAsString, equals("int"));
+
+ result = await isolate.evalFrame(topFrame, "S.toString()");
+ print(result);
+ expect(result.valueAsString, equals("String"));
+ },
+ resumeIsolate,
+ hasStoppedAtBreakpoint,
+ (Isolate isolate) async {
+ // Make sure we are in the right place.
+ var stack = await isolate.getStack();
+ var topFrame = 0;
+ expect(stack.type, equals('Stack'));
+ expect(await stack['frames'][topFrame].location.getLine(), 22);
+
+ Instance result = await isolate.evalFrame(topFrame, "S.toString()");
+ print(result);
+ expect(result.valueAsString, equals("String"));
+ },
+ resumeIsolate,
+ hasStoppedAtBreakpoint,
+ (Isolate isolate) async {
+ // Make sure we are in the right place.
+ var stack = await isolate.getStack();
+ var topFrame = 0;
+ expect(stack.type, equals('Stack'));
+ expect(await stack['frames'][topFrame].location.getLine(), 30);
+
+ Instance result = await isolate.evalFrame(topFrame, "T.toString()");
+ print(result);
+ expect(result.valueAsString, equals("int"));
+ },
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testMain);
diff --git a/runtime/observatory/tests/service/service.status b/runtime/observatory/tests/service/service.status
index 63f1252..8a40809 100644
--- a/runtime/observatory/tests/service/service.status
+++ b/runtime/observatory/tests/service/service.status
@@ -43,6 +43,7 @@
# Kernel version of tests
[ $compiler != dartk ]
add_breakpoint_rpc_kernel_test: SkipByDesign # kernel specific version of add_breakpoint_rpc_test
+evaluate_function_type_parameters_test: SkipByDesign # only supported in kernel
[ $compiler == precompiler ]
*: Skip # Issue 24651
diff --git a/runtime/observatory/tests/service/service_kernel.status b/runtime/observatory/tests/service/service_kernel.status
index 22ec1ae..4253ba1 100644
--- a/runtime/observatory/tests/service/service_kernel.status
+++ b/runtime/observatory/tests/service/service_kernel.status
@@ -121,6 +121,8 @@
eval_test: RuntimeError # Issue #33087
evaluate_activation_test/none: RuntimeError # Issue #33087
evaluate_async_closure_test: RuntimeError # Issue #33087
+evaluate_class_type_parameters_test: RuntimeError # Issue 33087
+evaluate_function_type_parameters_test: RuntimeError # Issue 33087
evaluate_in_async_activation_test: RuntimeError # Issue #33087
evaluate_in_async_star_activation_test: RuntimeError # Issue #33087
evaluate_in_frame_rpc_test: RuntimeError # Issue #33087
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index 85adc30..559b954 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -1266,6 +1266,8 @@
const GrowableObjectArray& param_names,
const GrowableObjectArray& param_values) {
GetDescIndices();
+ bool type_arguments_available = false;
+ TypeArguments& type_arguments = TypeArguments::Handle();
String& name = String::Handle();
String& existing_name = String::Handle();
Object& value = Instance::Handle();
@@ -1273,7 +1275,11 @@
for (intptr_t i = 0; i < num_variables; i++) {
TokenPosition ignore;
VariableAt(i, &name, &ignore, &ignore, &ignore, &value);
- if (!name.Equals(Symbols::This()) && !IsSyntheticVariableName(name)) {
+ if (name.Equals(Symbols::FunctionTypeArgumentsVar())) {
+ type_arguments_available = true;
+ type_arguments = TypeArguments::RawCast(value.raw());
+ } else if (!name.Equals(Symbols::This()) &&
+ !IsSyntheticVariableName(name)) {
if (IsPrivateVariableName(name)) {
name = String::ScrubName(name);
}
@@ -1295,11 +1301,44 @@
}
}
+ Array& type_param_names = Array::Handle();
+ if ((function().IsGeneric() || function().HasGenericParent()) &&
+ type_arguments_available) {
+ intptr_t num_vars =
+ function().NumTypeParameters() + function().NumParentTypeParameters();
+ type_param_names = Array::New(num_vars);
+ TypeArguments& type_params = TypeArguments::Handle();
+ TypeParameter& type_param = TypeParameter::Handle();
+ String& name = String::Handle();
+ Function& current = Function::Handle(function().raw());
+ for (intptr_t i = 0; !current.IsNull(); i += current.NumTypeParameters(),
+ current = current.parent_function()) {
+ type_params = current.type_parameters();
+ for (intptr_t j = 0; j < current.NumTypeParameters(); ++j) {
+ type_param = TypeParameter::RawCast(type_params.TypeAt(j));
+ name = type_param.Name();
+ // Write the names in backwards so they match up with the order of the
+ // types in 'type_arguments'.
+ type_param_names.SetAt(num_vars - (i + j) - 1, name);
+ }
+ }
+ if (type_arguments.IsNull()) {
+ type_arguments = TypeArguments::New(num_vars);
+ for (intptr_t i = 0; i < num_vars; ++i) {
+ type_arguments.SetTypeAt(i, Object::dynamic_type());
+ }
+ }
+ ASSERT(type_arguments.Length() == num_vars);
+ } else {
+ type_param_names = Object::empty_array().raw();
+ }
+
if (function().is_static()) {
const Class& cls = Class::Handle(function().Owner());
return cls.Evaluate(expr,
Array::Handle(Array::MakeFixedLength(param_names)),
- Array::Handle(Array::MakeFixedLength(param_values)));
+ Array::Handle(Array::MakeFixedLength(param_values)),
+ type_param_names, type_arguments);
} else {
const Object& receiver = Object::Handle(GetReceiver());
const Class& method_cls = Class::Handle(function().origin());
@@ -1310,7 +1349,8 @@
const Instance& inst = Instance::Cast(receiver);
return inst.Evaluate(method_cls, expr,
Array::Handle(Array::MakeFixedLength(param_names)),
- Array::Handle(Array::MakeFixedLength(param_values)));
+ Array::Handle(Array::MakeFixedLength(param_values)),
+ type_param_names, type_arguments);
}
UNREACHABLE();
return Object::null();
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index a52b4f6..c50f3ee 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -3205,7 +3205,8 @@
const String& library_url,
const String& klass,
bool is_static,
- const Array& arguments);
+ const Array& arguments,
+ const TypeArguments& type_arguments);
RawFunction* Function::EvaluateHelper(const Class& cls,
const String& expr,
@@ -3242,6 +3243,15 @@
RawObject* Class::Evaluate(const String& expr,
const Array& param_names,
const Array& param_values) const {
+ return Evaluate(expr, param_names, param_values, Object::empty_array(),
+ Object::null_type_arguments());
+}
+
+RawObject* Class::Evaluate(const String& expr,
+ const Array& param_names,
+ const Array& param_values,
+ const Array& type_param_names,
+ const TypeArguments& type_param_values) const {
ASSERT(Thread::Current()->IsMutatorThread());
if (id() < kInstanceCid) {
const Instance& exception = Instance::Handle(
@@ -3258,10 +3268,10 @@
}
return EvaluateWithDFEHelper(
- expr, param_names, Array::Handle(Array::New(0)),
+ expr, param_names, type_param_names,
String::Handle(Library::Handle(library()).url()),
IsTopLevel() ? String::Handle() : String::Handle(UserVisibleName()),
- !IsTopLevel(), param_values);
+ !IsTopLevel(), param_values, type_param_values);
}
// Ensure that top level parsing of the class has been done.
@@ -11404,6 +11414,15 @@
RawObject* Library::Evaluate(const String& expr,
const Array& param_names,
const Array& param_values) const {
+ return Evaluate(expr, param_names, param_values, Array::empty_array(),
+ TypeArguments::null_type_arguments());
+}
+
+RawObject* Library::Evaluate(const String& expr,
+ const Array& param_names,
+ const Array& param_values,
+ const Array& type_param_names,
+ const TypeArguments& type_param_values) const {
if (kernel_data() == TypedData::null() ||
!FLAG_enable_kernel_expression_compilation) {
// Evaluate the expression as a static function of the toplevel class.
@@ -11411,9 +11430,9 @@
ASSERT(top_level_class.is_finalized());
return top_level_class.Evaluate(expr, param_names, param_values);
}
- return EvaluateWithDFEHelper(expr, param_names, Array::Handle(Array::New(0)),
+ return EvaluateWithDFEHelper(expr, param_names, type_param_names,
String::Handle(url()), String::Handle(), false,
- param_values);
+ param_values, type_param_values);
}
void Library::InitNativeWrappersLibrary(Isolate* isolate, bool is_kernel) {
@@ -11476,7 +11495,8 @@
const String& library_url,
const String& klass,
bool is_static,
- const Array& arguments) {
+ const Array& arguments,
+ const TypeArguments& type_arguments) {
#if defined(DART_PRECOMPILED_RUNTIME)
const String& error_str = String::Handle(
String::New("Kernel service isolate not available in precompiled mode."));
@@ -11581,7 +11601,21 @@
ASSERT(removed);
I->object_store()->set_libraries_map(libraries_map.Release());
- return DartEntry::InvokeFunction(callee, arguments);
+ if (type_definitions.Length() == 0) {
+ return DartEntry::InvokeFunction(callee, arguments);
+ }
+
+ intptr_t num_type_args = type_arguments.Length();
+ Array& real_arguments = Array::Handle(Array::New(arguments.Length() + 1));
+ real_arguments.SetAt(0, type_arguments);
+ Object& arg = Object::Handle();
+ for (intptr_t i = 0; i < arguments.Length(); ++i) {
+ arg = arguments.At(i);
+ real_arguments.SetAt(i + 1, arg);
+ }
+ const Array& args_desc = Array::Handle(
+ ArgumentsDescriptor::New(num_type_args, real_arguments.Length()));
+ return DartEntry::InvokeFunction(callee, real_arguments, args_desc);
#endif
}
@@ -15801,6 +15835,16 @@
const String& expr,
const Array& param_names,
const Array& param_values) const {
+ return Evaluate(method_cls, expr, param_names, param_values,
+ Object::empty_array(), TypeArguments::null_type_arguments());
+}
+
+RawObject* Instance::Evaluate(const Class& method_cls,
+ const String& expr,
+ const Array& param_names,
+ const Array& param_values,
+ const Array& type_param_names,
+ const TypeArguments& type_param_values) const {
const Array& args = Array::Handle(Array::New(1 + param_values.Length()));
PassiveObject& param = PassiveObject::Handle();
args.SetAt(0, *this);
@@ -15817,9 +15861,10 @@
return DartEntry::InvokeFunction(eval_func, args);
}
return EvaluateWithDFEHelper(
- expr, param_names, Array::Handle(Array::New(0)),
+ expr, param_names, type_param_names,
String::Handle(Library::Handle(method_cls.library()).url()),
- String::Handle(method_cls.UserVisibleName()), false, args);
+ String::Handle(method_cls.UserVisibleName()), false, args,
+ type_param_values);
}
RawObject* Instance::HashCode() const {
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 49cbc89..c7749c5 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -1365,14 +1365,19 @@
// Return true on success, or false and error otherwise.
bool ApplyPatch(const Class& patch, Error* error) 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 parameters given in param_names, and is invoked with
- // the argument values given in param_values.
+ // 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
+ // (type_)param_names, and is invoked with the (type)argument values given in
+ // (type_)param_values.
RawObject* Evaluate(const String& expr,
const Array& param_names,
const Array& param_values) const;
+ RawObject* Evaluate(const String& expr,
+ const Array& param_names,
+ const Array& param_values,
+ const Array& type_param_names,
+ const TypeArguments& type_param_values) const;
RawError* EnsureIsFinalized(Thread* thread) const;
@@ -3713,15 +3718,21 @@
static RawLibrary* New(const String& url);
- // 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 parameters given in param_names, and is invoked with
- // the argument values given in param_values.
+ // 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)
+ // parameters given in (type_)param_names, and is invoked with the (type)
+ // argument values given in (type_)param_values.
RawObject* Evaluate(const String& expr,
const Array& param_names,
const Array& param_values) const;
+ RawObject* Evaluate(const String& expr,
+ const Array& param_names,
+ const Array& param_values,
+ const Array& type_param_names,
+ const TypeArguments& type_arguments) const;
+
// Library scope name dictionary.
//
// TODO(turnidge): The Lookup functions are not consistent in how
@@ -5579,16 +5590,23 @@
// (if not NULL) to call.
bool IsCallable(Function* function) 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 parameters given in param_names, and is invoked with
- // the argument values given in param_values.
+ // 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)
+ // parameters given in (type_)param_names, and is invoked with the (type)
+ // argument values given in (type_)param_values.
RawObject* Evaluate(const Class& method_cls,
const String& expr,
const Array& param_names,
const Array& param_values) const;
+ RawObject* Evaluate(const Class& method_cls,
+ const String& expr,
+ const Array& param_names,
+ const Array& param_values,
+ const Array& type_param_names,
+ const TypeArguments& type_param_values) const;
+
// Equivalent to invoking hashCode on this instance.
virtual RawObject* HashCode() const;
diff --git a/runtime/vm/scopes.cc b/runtime/vm/scopes.cc
index e07e393..e38d191 100644
--- a/runtime/vm/scopes.cc
+++ b/runtime/vm/scopes.cc
@@ -268,6 +268,10 @@
// Keep :async_stack_trace for asynchronous debugging.
return false;
}
+ if (str.raw() == Symbols::FunctionTypeArgumentsVar().raw()) {
+ // Keep :function_type_arguments for accessing type variables in debugging.
+ return false;
+ }
return str.CharAt(0) == ':';
}
@@ -644,7 +648,8 @@
(variable->name().raw() == Symbols::ExceptionVar().raw()) ||
(variable->name().raw() == Symbols::SavedTryContextVar().raw()) ||
(variable->name().raw() == Symbols::ArgDescVar().raw()) ||
- (variable->name().raw() == Symbols::FunctionTypeArgumentsVar().raw())) {
+ (variable->name().raw() ==
+ Symbols::FunctionTypeArgumentsVar().raw())) {
// Don't capture those variables because the VM expects them to be on
// the stack.
continue;