[VM] [CFE]: Expression compilation inside extension method

This CL handles expression compilation inside extension methods better.
It is now possible to evaluate "this" and other methods defined in the
extension.

https://github.com/dart-lang/sdk/issues/46757

TEST=service tests added.

Change-Id: I3c71eb23117e26b01961f32103f4046f0b537976
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/212286
Commit-Queue: Jens Johansen <jensj@google.com>
Reviewed-by: Ben Konyi <bkonyi@google.com>
Reviewed-by: Anna Gringauze <annagrin@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
Jens Johansen 2021-09-10 12:02:57 +00:00 committed by commit-bot@chromium.org
parent d80cff6b2b
commit 48d8225b17
27 changed files with 321 additions and 58 deletions

View file

@ -11,7 +11,7 @@
"constraint, update this by running tools/generate_package_config.dart."
],
"configVersion": 2,
"generated": "2021-09-08T14:55:24.803714",
"generated": "2021-09-10T10:06:41.987732",
"generator": "tools/generate_package_config.dart",
"packages": [
{
@ -494,7 +494,7 @@
"name": "observatory_2",
"rootUri": "../runtime/observatory_2",
"packageUri": "lib/",
"languageVersion": "2.2"
"languageVersion": "2.6"
},
{
"name": "observatory_test_package",

View file

@ -440,8 +440,8 @@ class ExpressionCompiler {
scope.typeParameters,
debugProcedureName,
scope.library.importUri,
scope.cls?.name,
scope.isStatic);
className: scope.cls?.name,
isStatic: scope.isStatic);
_log('Compiled expression to kernel');

View file

@ -136,8 +136,9 @@ abstract class IncrementalKernelGenerator {
List<TypeParameter> typeDefinitions,
String syntheticProcedureName,
Uri libraryUri,
[String? className,
bool isStatic = false]);
{String? className,
String? methodName,
bool isStatic = false});
/// Sets experimental features.
///

View file

@ -660,8 +660,8 @@ String extractLocalNameFromLateLoweredSetter(String name) {
///
/// where '#this' is the synthetic "extension this" parameter.
bool isExtensionThis(VariableDeclaration node) {
assert(
node.isLowered || node.name == null || !isExtensionThisName(node.name));
assert(node.isLowered || node.name == null || !isExtensionThisName(node.name),
"$node has name ${node.name} and node.isLowered = ${node.isLowered}");
return node.isLowered && isExtensionThisName(node.name);
}

View file

@ -29,6 +29,8 @@ export '../api_prototype/incremental_kernel_generator.dart'
export '../api_prototype/kernel_generator.dart'
show kernelForModule, kernelForProgram;
export '../api_prototype/lowering_predicates.dart' show isExtensionThisName;
export '../api_prototype/memory_file_system.dart' show MemoryFileSystem;
export '../api_prototype/standard_file_system.dart' show StandardFileSystem;

View file

@ -11,7 +11,15 @@ import 'package:_fe_analyzer_shared/src/scanner/abstract_scanner.dart'
import 'package:front_end/src/api_prototype/experimental_flags.dart';
import 'package:front_end/src/api_prototype/front_end.dart';
import 'package:front_end/src/api_prototype/lowering_predicates.dart'
show isExtensionThisName;
import 'package:front_end/src/base/nnbd_mode.dart';
import 'package:front_end/src/fasta/builder/member_builder.dart'
show MemberBuilder;
import 'package:front_end/src/fasta/fasta_codes.dart';
import 'package:front_end/src/fasta/source/source_loader.dart';
import 'package:kernel/binary/ast_from_binary.dart'
@ -34,6 +42,7 @@ import 'package:kernel/kernel.dart'
Component,
DartType,
Expression,
Extension,
FunctionNode,
Library,
LibraryDependency,
@ -48,7 +57,8 @@ import 'package:kernel/kernel.dart'
Source,
Supertype,
TreeNode,
TypeParameter;
TypeParameter,
VariableDeclaration;
import 'package:kernel/canonical_name.dart'
show CanonicalNameError, CanonicalNameSdkError;
@ -71,6 +81,8 @@ import 'builder/builder.dart' show Builder;
import 'builder/class_builder.dart' show ClassBuilder;
import 'builder/extension_builder.dart' show ExtensionBuilder;
import 'builder/field_builder.dart' show FieldBuilder;
import 'builder/library_builder.dart' show LibraryBuilder;
@ -1907,8 +1919,9 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
List<TypeParameter> typeDefinitions,
String syntheticProcedureName,
Uri libraryUri,
[String? className,
bool isStatic = false]) async {
{String? className,
String? methodName,
bool isStatic = false}) async {
assert(dillLoadedData != null && userCode != null);
return await context.runInContext((_) async {
@ -1923,6 +1936,26 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
cls = classBuilder?.cls;
if (cls == null) return null;
}
Extension? extension;
String? extensionName;
if (methodName != null) {
int indexOfDot = methodName.indexOf(".");
if (indexOfDot >= 0) {
String beforeDot = methodName.substring(0, indexOfDot);
String afterDot = methodName.substring(indexOfDot + 1);
Builder? builder = libraryBuilder.scopeBuilder[beforeDot];
extensionName = beforeDot;
if (builder is ExtensionBuilder) {
extension = builder.extension;
Builder? subBuilder = builder.scopeBuilder[afterDot];
if (subBuilder is MemberBuilder) {
if (subBuilder.isExtensionInstanceMember) {
isStatic = false;
}
}
}
}
}
userCode!.loader.resetSeenMessages();
@ -1937,8 +1970,14 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
return null;
}
}
int index = 0;
for (String name in definitions.keys) {
if (!isLegalIdentifier(name)) {
index++;
if (!(isLegalIdentifier(name) ||
(extension != null &&
!isStatic &&
index == 1 &&
isExtensionThisName(name)))) {
userCode!.loader.addProblem(
templateIncrementalCompilerIllegalParameter.withArguments(name),
// TODO: pass variable declarations instead of
@ -2009,13 +2048,29 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
positionalParameters: definitions.keys
.map((name) =>
new VariableDeclarationImpl(name, 0, type: definitions[name])
..fileOffset =
cls?.fileOffset ?? libraryBuilder.library.fileOffset)
..fileOffset = cls?.fileOffset ??
extension?.fileOffset ??
libraryBuilder.library.fileOffset)
.toList());
VariableDeclaration? extensionThis;
if (extension != null &&
!isStatic &&
parameters.positionalParameters.isNotEmpty) {
// We expect the first parameter to be called #this and be special.
if (isExtensionThisName(parameters.positionalParameters.first.name)) {
extensionThis = parameters.positionalParameters.first;
extensionThis.isLowered = true;
}
}
debugLibrary.build(userCode!.loader.coreLibrary, modifyTarget: false);
Expression compiledExpression = await userCode!.loader.buildExpression(
debugLibrary, className, className != null && !isStatic, parameters);
debugLibrary,
className ?? extensionName,
(className != null && !isStatic) || extensionThis != null,
parameters,
extensionThis);
Procedure procedure = new Procedure(
new Name(syntheticProcedureName), ProcedureKind.Method, parameters,
@ -2026,7 +2081,7 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
..parent = parameters;
procedure.fileUri = debugLibrary.fileUri;
procedure.parent = className != null ? cls : libraryBuilder.library;
procedure.parent = cls ?? libraryBuilder.library;
userCode!.uriToSource.remove(debugExprUri);
userCode!.loader.sourceBytes.remove(debugExprUri);

View file

@ -45,6 +45,7 @@ import 'package:kernel/ast.dart'
Reference,
Supertype,
TreeNode,
VariableDeclaration,
Version;
import 'package:kernel/class_hierarchy.dart'
@ -513,26 +514,32 @@ class SourceLoader extends Loader {
}
}
// TODO(johnniwinther,jensj): Handle expression in extensions?
Future<Expression> buildExpression(
SourceLibraryBuilder libraryBuilder,
String? enclosingClass,
String? enclosingClassOrExtension,
bool isClassInstanceMember,
FunctionNode parameters) async {
FunctionNode parameters,
VariableDeclaration? extensionThis) async {
Token token = await tokenize(libraryBuilder, suppressLexicalErrors: false);
DietListener dietListener = createDietListener(libraryBuilder);
Builder parent = libraryBuilder;
if (enclosingClass != null) {
if (enclosingClassOrExtension != null) {
Builder? cls = dietListener.memberScope
.lookup(enclosingClass, -1, libraryBuilder.fileUri);
.lookup(enclosingClassOrExtension, -1, libraryBuilder.fileUri);
if (cls is ClassBuilder) {
parent = cls;
dietListener
..currentDeclaration = cls
..memberScope = cls.scope.copyWithParent(
dietListener.memberScope.withTypeVariables(cls.typeVariables),
"debugExpression in $enclosingClass");
"debugExpression in class $enclosingClassOrExtension");
} else if (cls is ExtensionBuilder) {
parent = cls;
dietListener
..currentDeclaration = cls
..memberScope = cls.scope.copyWithParent(dietListener.memberScope,
"debugExpression in extension $enclosingClassOrExtension");
}
}
ProcedureBuilder builder = new SourceProcedureBuilder(
@ -562,7 +569,8 @@ class SourceLoader extends Loader {
..parent = parent;
BodyBuilder listener = dietListener.createListener(
builder, dietListener.memberScope,
isDeclarationInstanceMember: isClassInstanceMember);
isDeclarationInstanceMember: isClassInstanceMember,
extensionThis: extensionThis);
return listener.parseSingleExpression(
new Parser(listener,

View file

@ -140,6 +140,8 @@ class TestCase {
final String className;
final String methodName;
String expression;
List<CompilationResult> results = [];
@ -153,6 +155,7 @@ class TestCase {
this.isStaticMethod,
this.library,
this.className,
this.methodName,
this.expression);
@override
@ -260,6 +263,7 @@ class ReadTest extends Step<TestDescription, List<TestCase>, Context> {
bool isStaticMethod = false;
Uri library;
String className;
String methodName;
String expression;
dynamic maps = loadYamlNode(contents, sourceUrl: uri);
@ -281,6 +285,8 @@ class ReadTest extends Step<TestDescription, List<TestCase>, Context> {
if (uri.fragment != null && uri.fragment != '') {
className = uri.fragment;
}
} else if (key == "method") {
methodName = value as String;
} else if (key == "definitions") {
definitions = (value as YamlList).map((x) => x as String).toList();
} else if (key == "type_definitions") {
@ -292,8 +298,17 @@ class ReadTest extends Step<TestDescription, List<TestCase>, Context> {
expression = value;
}
}
var test = new TestCase(description, entryPoint, import, definitions,
typeDefinitions, isStaticMethod, library, className, expression);
var test = new TestCase(
description,
entryPoint,
import,
definitions,
typeDefinitions,
isStaticMethod,
library,
className,
methodName,
expression);
var result = test.validate();
if (result != null) {
return new Result.fail(tests, result);
@ -325,13 +340,15 @@ class CompileExpression extends Step<List<TestCase>, List<TestCase>, Context> {
}
Procedure compiledProcedure = await compiler.compileExpression(
test.expression,
definitions,
typeParams,
"debugExpr",
test.library,
test.className,
test.isStaticMethod);
test.expression,
definitions,
typeParams,
"debugExpr",
test.library,
className: test.className,
methodName: test.methodName,
isStatic: test.isStaticMethod,
);
List<DiagnosticMessage> errors = context.takeErrors();
test.results.add(new CompilationResult(compiledProcedure, errors));
if (compiledProcedure != null) {

View file

@ -0,0 +1,10 @@
# Copyright (c) 2021, 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.
entry_point: "main.dart"
definitions: ["#this"]
position: "main.dart"
method: "Foo.parseAsInt"
expression: |
() { print(getFortyTwo()); return this; }

View file

@ -0,0 +1,7 @@
Errors: {
}
method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr(lowered dynamic #this) → dynamic
return () → dynamic {
dart.core::print(main::Foo|getFortyTwo(#this as{TypeError,ForDynamic} dart.core::String*));
return #this;
};

View file

@ -79,3 +79,16 @@ class MiddlewareApi<State extends Built<State, StateBuilder>,
main() {
exit(0);
}
extension Foo on String {
int parseAsInt() {
int result = int.parse(this);
print("Parsed $this to $result");
print(getFortyTwo());
return result;
}
int getFortyTwo() {
return 42;
}
}

View file

@ -274,6 +274,7 @@ abstract class CompilerInterface {
List<String> typeDefinitions,
String libraryUri,
String klass,
String method,
bool isStatic);
/// Compiles [expression] in [libraryUri] at [line]:[column] to JavaScript
@ -806,11 +807,12 @@ class FrontendCompiler implements CompilerInterface {
List<String> typeDefinitions,
String libraryUri,
String klass,
String method,
bool isStatic) async {
final String boundaryKey = Uuid().generateV4();
_outputStream.writeln('result $boundaryKey');
Procedure procedure = await _generator.compileExpression(
expression, definitions, typeDefinitions, libraryUri, klass, isStatic);
Procedure procedure = await _generator.compileExpression(expression,
definitions, typeDefinitions, libraryUri, klass, method, isStatic);
if (procedure != null) {
Component component = createExpressionEvaluationComponent(procedure);
final IOSink sink = File(_kernelBinaryFilename).openWrite();
@ -1091,6 +1093,7 @@ class _CompileExpressionRequest {
List<String> typeDefs = <String>[];
String library;
String klass;
String method;
bool isStatic;
}
@ -1231,6 +1234,7 @@ StreamSubscription<String> listenAndCompile(CompilerInterface compiler,
compileExpressionRequest.typeDefs,
compileExpressionRequest.library,
compileExpressionRequest.klass,
compileExpressionRequest.method,
compileExpressionRequest.isStatic);
} else {
compiler

View file

@ -501,12 +501,13 @@ Future _processExpressionCompilationRequest(request) async {
final List<String> typeDefinitions = request[6].cast<String>();
final String libraryUri = request[7];
final String? klass = request[8];
final bool isStatic = request[9];
final List<List<int>> dillData = request[10].cast<List<int>>();
final int blobLoadCount = request[11];
final bool enableAsserts = request[12];
final String? method = request[9];
final bool isStatic = request[10];
final List<List<int>> dillData = request[11].cast<List<int>>();
final int blobLoadCount = request[12];
final bool enableAsserts = request[13];
final List<String>? experimentalFlags =
request[13] != null ? request[13].cast<String>() : null;
request[14] != null ? request[14].cast<String>() : null;
IncrementalCompilerWrapper? compiler = isolateCompilers[isolateGroupId];
@ -618,7 +619,13 @@ Future _processExpressionCompilationRequest(request) async {
CompilationResult result;
try {
Procedure? procedure = await compiler.generator!.compileExpression(
expression, definitions, typeDefinitions, libraryUri, klass, isStatic);
expression,
definitions,
typeDefinitions,
libraryUri,
klass,
method,
isStatic);
if (procedure == null) {
port.send(

View file

@ -188,11 +188,14 @@ class IncrementalCompiler {
List<String> typeDefinitions,
String libraryUri,
String? klass,
String? method,
bool isStatic) {
Map<String, DartType> completeDefinitions = {};
for (String name in definitions) {
if (!isLegalIdentifier(name)) continue;
completeDefinitions[name] = new DynamicType();
for (int i = 0; i < definitions.length; i++) {
String name = definitions[i];
if (isLegalIdentifier(name) || (i == 0 && isExtensionThisName(name))) {
completeDefinitions[name] = new DynamicType();
}
}
List<TypeParameter> typeParameters = [];
@ -204,6 +207,7 @@ class IncrementalCompiler {
Uri library = Uri.parse(libraryUri);
return _generator.compileExpression(expression, completeDefinitions,
typeParameters, kDebugProcedureName, library, klass, isStatic);
typeParameters, kDebugProcedureName, library,
className: klass, methodName: method, isStatic: isStatic);
}
}

View file

@ -112,14 +112,14 @@ main() {
await compiler.compile();
compiler.accept();
{
Procedure? procedure = await compiler.compileExpression(
'main', <String>[], <String>[], main.uri.toString(), null, true);
Procedure? procedure = await compiler.compileExpression('main',
<String>[], <String>[], main.uri.toString(), null, null, true);
expect(procedure, isNotNull);
expect(errorsReported, equals(0));
}
{
Procedure? procedure = await compiler.compileExpression(
'main1', <String>[], <String>[], main.uri.toString(), null, true);
Procedure? procedure = await compiler.compileExpression('main1',
<String>[], <String>[], main.uri.toString(), null, null, true);
expect(procedure, isNotNull);
expect(errorsReported, equals(1));
errorsReported = 0;
@ -1024,8 +1024,8 @@ main() {
}
compiler.accept();
{
Procedure? procedure = await compiler.compileExpression(
'a', <String>[], <String>[], 'package:foo/bar.dart', 'A', true);
Procedure? procedure = await compiler.compileExpression('a', <String>[],
<String>[], 'package:foo/bar.dart', 'A', null, true);
expect(procedure, isNotNull);
}
@ -1039,8 +1039,8 @@ main() {
}
await compiler.reject();
{
Procedure? procedure = await compiler.compileExpression(
'a', <String>[], <String>[], 'package:foo/bar.dart', 'A', true);
Procedure? procedure = await compiler.compileExpression('a', <String>[],
<String>[], 'package:foo/bar.dart', 'A', null, true);
expect(procedure, isNotNull);
}
});
@ -1088,7 +1088,7 @@ main() {
compiler.accept();
{
final Procedure procedure = (await compiler.compileExpression(
'a', <String>[], <String>[], barUri.toString(), 'A', true))!;
'a', <String>[], <String>[], barUri.toString(), 'A', null, true))!;
// Verify that the expression only has links to the only bar we know
// about.
final LibraryReferenceCollector lrc = new LibraryReferenceCollector();
@ -1133,7 +1133,7 @@ main() {
}
{
final Procedure procedure = (await compiler.compileExpression(
'a', <String>[], <String>[], barUri.toString(), 'A', true))!;
'a', <String>[], <String>[], barUri.toString(), 'A', null, true))!;
// Verify that the expression only has links to the original bar.
final LibraryReferenceCollector lrc = new LibraryReferenceCollector();
procedure.accept(lrc);

View file

@ -0,0 +1,53 @@
// Copyright (c) 2021, 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/models.dart' show InstanceKind;
import 'package:observatory/service_io.dart';
import 'package:test/test.dart';
import 'service_test_common.dart';
import 'test_helper.dart';
extension Foo on String {
int parseInt(int x) {
debugger();
return foo();
}
int foo() => 42;
}
void testFunction() {
print("10".parseInt(21));
}
var tests = <IsolateTest>[
hasStoppedAtBreakpoint,
(Isolate isolate) async {
Instance result;
result = await isolate.evalFrame(0, 'x') as Instance;
expect(result.valueAsString, equals('21'));
expect(result.kind, equals(InstanceKind.int));
result = await isolate.evalFrame(0, 'this') as Instance;
expect(result.valueAsString, equals('10'));
expect(result.kind, equals(InstanceKind.string));
result = await isolate.evalFrame(0, 'foo()') as Instance;
expect(result.valueAsString, equals('42'));
expect(result.kind, equals(InstanceKind.int));
result = await isolate.evalFrame(0, 'foo() + x') as Instance;
expect(result.valueAsString, equals('63'));
expect(result.kind, equals(InstanceKind.int));
result =
await isolate.evalFrame(0, 'foo() + x + int.parse(this)') as Instance;
expect(result.valueAsString, equals('73'));
expect(result.kind, equals(InstanceKind.int));
},
];
main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction);

View file

@ -95,6 +95,7 @@ evaluate_class_type_parameters_test: SkipByDesign # Debugger is disabled in AOT
evaluate_function_type_parameters_test: SkipByDesign # Debugger is disabled in AOT mode.
evaluate_in_async_activation_test: SkipByDesign # Debugger is disabled in AOT mode.
evaluate_in_async_star_activation_test: SkipByDesign # Debugger is disabled in AOT mode.
evaluate_in_extension_method_test: SkipByDesign # Debugger is disabled in AOT mode.
evaluate_in_frame_rpc_test: SkipByDesign # Debugger is disabled in AOT mode.
evaluate_in_frame_with_scope_test: SkipByDesign # Debugger is disabled in AOT mode.
evaluate_in_sync_star_activation_test: SkipByDesign # Debugger is disabled in AOT mode.

View file

@ -1,6 +1,6 @@
name: observatory
environment:
sdk: '>=2.2.2 <3.0.0'
sdk: '>=2.6.0 <3.0.0'
dependencies:
usage: 'any'

View file

@ -0,0 +1,53 @@
// Copyright (c) 2021, 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_2/models.dart' show InstanceKind;
import 'package:observatory_2/service_io.dart';
import 'package:test/test.dart';
import 'service_test_common.dart';
import 'test_helper.dart';
extension Foo on String {
int parseInt(int x) {
debugger();
return foo();
}
int foo() => 42;
}
void testFunction() {
print("10".parseInt(21));
}
var tests = <IsolateTest>[
hasStoppedAtBreakpoint,
(Isolate isolate) async {
Instance result;
result = await isolate.evalFrame(0, 'x') as Instance;
expect(result.valueAsString, equals('21'));
expect(result.kind, equals(InstanceKind.int));
result = await isolate.evalFrame(0, 'this') as Instance;
expect(result.valueAsString, equals('10'));
expect(result.kind, equals(InstanceKind.string));
result = await isolate.evalFrame(0, 'foo()') as Instance;
expect(result.valueAsString, equals('42'));
expect(result.kind, equals(InstanceKind.int));
result = await isolate.evalFrame(0, 'foo() + x') as Instance;
expect(result.valueAsString, equals('63'));
expect(result.kind, equals(InstanceKind.int));
result =
await isolate.evalFrame(0, 'foo() + x + int.parse(this)') as Instance;
expect(result.valueAsString, equals('73'));
expect(result.kind, equals(InstanceKind.int));
},
];
main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction);

View file

@ -95,6 +95,7 @@ evaluate_class_type_parameters_test: SkipByDesign # Debugger is disabled in AOT
evaluate_function_type_parameters_test: SkipByDesign # Debugger is disabled in AOT mode.
evaluate_in_async_activation_test: SkipByDesign # Debugger is disabled in AOT mode.
evaluate_in_async_star_activation_test: SkipByDesign # Debugger is disabled in AOT mode.
evaluate_in_extension_method_test: SkipByDesign # Debugger is disabled in AOT mode.
evaluate_in_frame_rpc_test: SkipByDesign # Debugger is disabled in AOT mode.
evaluate_in_frame_with_scope_test: SkipByDesign # Debugger is disabled in AOT mode.
evaluate_in_sync_star_activation_test: SkipByDesign # Debugger is disabled in AOT mode.

View file

@ -209,6 +209,7 @@ TEST_CASE(EvalExpression) {
/*platform_kernel=*/nullptr, /*platform_kernel_size=*/0,
expr_text.ToCString(), Array::empty_array(), Array::empty_array(),
String::Handle(lib_handle.url()).ToCString(), "A",
/* method= */ nullptr,
/* is_static= */ false);
EXPECT_EQ(Dart_KernelCompilationStatus_Ok, compilation_result.status);

View file

@ -184,6 +184,7 @@ DART_EXPORT Dart_Handle Dart_EvaluateStaticExpr(Dart_Handle lib_handle,
/* type_defintions= */ Array::empty_array(),
String::Handle(lib.url()).ToCString(),
/* klass= */ nullptr,
/* method= */ nullptr,
/* is_static= */ true);
if (compilation_result.status != Dart_KernelCompilationStatus_Ok) {
return Api::NewError("Failed to compile expression.");

View file

@ -480,6 +480,7 @@ class KernelCompilationRequest : public ValueObject {
const Array& type_definitions,
char const* library_uri,
char const* klass,
char const* method,
bool is_static,
const MallocGrowableArray<char*>* experimental_flags) {
if (port_ == ILLEGAL_PORT) {
@ -565,6 +566,14 @@ class KernelCompilationRequest : public ValueObject {
class_object.type = Dart_CObject_kNull;
}
Dart_CObject method_object;
if (method != NULL) {
method_object.type = Dart_CObject_kString;
method_object.value.as_string = const_cast<char*>(method);
} else {
method_object.type = Dart_CObject_kNull;
}
Dart_CObject is_static_object;
is_static_object.type = Dart_CObject_kBool;
is_static_object.value.as_bool = is_static;
@ -654,6 +663,7 @@ class KernelCompilationRequest : public ValueObject {
&type_definitions_object,
&library_uri_object,
&class_object,
&method_object,
&is_static_object,
&dills_object,
&num_blob_loads,
@ -1133,6 +1143,7 @@ Dart_KernelCompilationResult KernelIsolate::CompileExpressionToKernel(
const Array& type_definitions,
const char* library_url,
const char* klass,
const char* method,
bool is_static) {
Dart_Port kernel_port = WaitForKernelPort();
if (kernel_port == ILLEGAL_PORT) {
@ -1147,7 +1158,7 @@ Dart_KernelCompilationResult KernelIsolate::CompileExpressionToKernel(
ASSERT(is_static || (klass != nullptr));
return request.SendAndWaitForResponse(
kernel_port, platform_kernel, platform_kernel_size, expression,
definitions, type_definitions, library_url, klass, is_static,
definitions, type_definitions, library_url, klass, method, is_static,
experimental_flags_);
}

View file

@ -75,6 +75,7 @@ class KernelIsolate : public AllStatic {
const Array& type_definitions,
const char* library_url,
const char* klass,
const char* method,
bool is_static);
static Dart_KernelCompilationResult ListDependencies();

View file

@ -2721,6 +2721,7 @@ static void BuildExpressionEvaluationScope(Thread* thread, JSONStream* js) {
const GrowableObjectArray& type_params_names =
GrowableObjectArray::Handle(zone, GrowableObjectArray::New());
String& klass_name = String::Handle(zone);
String& method_name = String::Handle(zone);
String& library_uri = String::Handle(zone);
bool isStatic = false;
@ -2746,11 +2747,13 @@ static void BuildExpressionEvaluationScope(Thread* thread, JSONStream* js) {
klass_name = cls.UserVisibleName();
}
library_uri = Library::Handle(zone, cls.library()).url();
method_name = frame->function().UserVisibleName();
isStatic = true;
} else {
const Class& method_cls = Class::Handle(zone, frame->function().origin());
library_uri = Library::Handle(zone, method_cls.library()).url();
klass_name = method_cls.UserVisibleName();
method_name = frame->function().UserVisibleName();
isStatic = false;
}
} else {
@ -2829,6 +2832,9 @@ static void BuildExpressionEvaluationScope(Thread* thread, JSONStream* js) {
if (!klass_name.IsNull()) {
report.AddProperty("klass", klass_name.ToCString());
}
if (!method_name.IsNull()) {
report.AddProperty("method", method_name.ToCString());
}
report.AddProperty("isStatic", isStatic);
}
@ -2877,6 +2883,7 @@ static const MethodParameter* const compile_expression_params[] = {
new StringParameter("libraryUri", true),
new StringParameter("klass", false),
new BoolParameter("isStatic", false),
new StringParameter("method", false),
NULL,
};
@ -2922,7 +2929,8 @@ static void CompileExpression(Thread* thread, JSONStream* js) {
kernel_buffer, kernel_buffer_len, js->LookupParam("expression"),
Array::Handle(Array::MakeFixedLength(params)),
Array::Handle(Array::MakeFixedLength(type_params)),
js->LookupParam("libraryUri"), js->LookupParam("klass"), is_static);
js->LookupParam("libraryUri"), js->LookupParam("klass"),
js->LookupParam("method"), is_static);
if (compilation_result.status != Dart_KernelCompilationStatus_Ok) {
js->PrintError(kExpressionCompilationError, "%s", compilation_result.error);

View file

@ -648,7 +648,8 @@ Dart_Handle TestCase::EvaluateExpression(const Library& lib,
KernelIsolate::CompileExpressionToKernel(
/* platform_kernel= */ nullptr, /* platform_kernel_size= */ 0,
expr.ToCString(), param_names, Array::empty_array(),
String::Handle(lib.url()).ToCString(), /* klass=*/nullptr,
String::Handle(lib.url()).ToCString(), /* klass= */ nullptr,
/* method= */ nullptr,
/* is_static= */ true);
if (compilation_result.status != Dart_KernelCompilationStatus_Ok) {
return Api::NewError("%s", compilation_result.error);

View file

@ -132,6 +132,10 @@ class _Evaluator {
if (klass != null) {
compileParams['klass'] = klass;
}
final method = buildScopeResponseResult['method'];
if (method != null) {
compileParams['method'] = method;
}
if (externalClient != null) {
final compileExpression = Message.forMethod('compileExpression');
compileExpression.client = externalClient;