[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:
committed by
commit-bot@chromium.org
parent
d80cff6b2b
commit
48d8225b17
@@ -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",
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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.
|
||||
///
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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; }
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
@@ -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.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: observatory
|
||||
environment:
|
||||
sdk: '>=2.2.2 <3.0.0'
|
||||
sdk: '>=2.6.0 <3.0.0'
|
||||
|
||||
dependencies:
|
||||
usage: 'any'
|
||||
|
||||
@@ -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);
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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.");
|
||||
|
||||
@@ -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_);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user