diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json index b6f9a73374c..3857a2df779 100644 --- a/.dart_tool/package_config.json +++ b/.dart_tool/package_config.json @@ -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", diff --git a/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart b/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart index 23cc5bb8d68..8d195b37696 100644 --- a/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart +++ b/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart @@ -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'); diff --git a/pkg/front_end/lib/src/api_prototype/incremental_kernel_generator.dart b/pkg/front_end/lib/src/api_prototype/incremental_kernel_generator.dart index a6d071f35cf..d28d71c6058 100644 --- a/pkg/front_end/lib/src/api_prototype/incremental_kernel_generator.dart +++ b/pkg/front_end/lib/src/api_prototype/incremental_kernel_generator.dart @@ -136,8 +136,9 @@ abstract class IncrementalKernelGenerator { List typeDefinitions, String syntheticProcedureName, Uri libraryUri, - [String? className, - bool isStatic = false]); + {String? className, + String? methodName, + bool isStatic = false}); /// Sets experimental features. /// diff --git a/pkg/front_end/lib/src/api_prototype/lowering_predicates.dart b/pkg/front_end/lib/src/api_prototype/lowering_predicates.dart index d1a2a4e9b3b..1375e06a6c4 100644 --- a/pkg/front_end/lib/src/api_prototype/lowering_predicates.dart +++ b/pkg/front_end/lib/src/api_prototype/lowering_predicates.dart @@ -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); } diff --git a/pkg/front_end/lib/src/api_unstable/vm.dart b/pkg/front_end/lib/src/api_unstable/vm.dart index f592c51077c..e981bd36384 100644 --- a/pkg/front_end/lib/src/api_unstable/vm.dart +++ b/pkg/front_end/lib/src/api_unstable/vm.dart @@ -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; diff --git a/pkg/front_end/lib/src/fasta/incremental_compiler.dart b/pkg/front_end/lib/src/fasta/incremental_compiler.dart index ef7f4618ac1..d3a46e75ebb 100644 --- a/pkg/front_end/lib/src/fasta/incremental_compiler.dart +++ b/pkg/front_end/lib/src/fasta/incremental_compiler.dart @@ -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 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); diff --git a/pkg/front_end/lib/src/fasta/source/source_loader.dart b/pkg/front_end/lib/src/fasta/source/source_loader.dart index 110567c0a8f..35bf322aee0 100644 --- a/pkg/front_end/lib/src/fasta/source/source_loader.dart +++ b/pkg/front_end/lib/src/fasta/source/source_loader.dart @@ -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 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, diff --git a/pkg/front_end/test/fasta/expression_suite.dart b/pkg/front_end/test/fasta/expression_suite.dart index 94cef6eedc2..793aeb0779f 100644 --- a/pkg/front_end/test/fasta/expression_suite.dart +++ b/pkg/front_end/test/fasta/expression_suite.dart @@ -140,6 +140,8 @@ class TestCase { final String className; + final String methodName; + String expression; List 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, 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, 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, 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, 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 errors = context.takeErrors(); test.results.add(new CompilationResult(compiledProcedure, errors)); if (compiledProcedure != null) { diff --git a/pkg/front_end/testcases/expression/extension_this.expression.yaml b/pkg/front_end/testcases/expression/extension_this.expression.yaml new file mode 100644 index 00000000000..3ac2b8f2ca5 --- /dev/null +++ b/pkg/front_end/testcases/expression/extension_this.expression.yaml @@ -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; } diff --git a/pkg/front_end/testcases/expression/extension_this.expression.yaml.expect b/pkg/front_end/testcases/expression/extension_this.expression.yaml.expect new file mode 100644 index 00000000000..aed2ac61e1b --- /dev/null +++ b/pkg/front_end/testcases/expression/extension_this.expression.yaml.expect @@ -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; + }; diff --git a/pkg/front_end/testcases/expression/main.dart b/pkg/front_end/testcases/expression/main.dart index a4fefb51d0e..f65db931d22 100644 --- a/pkg/front_end/testcases/expression/main.dart +++ b/pkg/front_end/testcases/expression/main.dart @@ -79,3 +79,16 @@ class MiddlewareApi, 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; + } +} diff --git a/pkg/frontend_server/lib/frontend_server.dart b/pkg/frontend_server/lib/frontend_server.dart index 29589fc317b..9545afb69cd 100644 --- a/pkg/frontend_server/lib/frontend_server.dart +++ b/pkg/frontend_server/lib/frontend_server.dart @@ -274,6 +274,7 @@ abstract class CompilerInterface { List 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 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 typeDefs = []; String library; String klass; + String method; bool isStatic; } @@ -1231,6 +1234,7 @@ StreamSubscription listenAndCompile(CompilerInterface compiler, compileExpressionRequest.typeDefs, compileExpressionRequest.library, compileExpressionRequest.klass, + compileExpressionRequest.method, compileExpressionRequest.isStatic); } else { compiler diff --git a/pkg/vm/bin/kernel_service.dart b/pkg/vm/bin/kernel_service.dart index e3a221e59b3..88549103878 100644 --- a/pkg/vm/bin/kernel_service.dart +++ b/pkg/vm/bin/kernel_service.dart @@ -501,12 +501,13 @@ Future _processExpressionCompilationRequest(request) async { final List typeDefinitions = request[6].cast(); final String libraryUri = request[7]; final String? klass = request[8]; - final bool isStatic = request[9]; - final List> dillData = request[10].cast>(); - final int blobLoadCount = request[11]; - final bool enableAsserts = request[12]; + final String? method = request[9]; + final bool isStatic = request[10]; + final List> dillData = request[11].cast>(); + final int blobLoadCount = request[12]; + final bool enableAsserts = request[13]; final List? experimentalFlags = - request[13] != null ? request[13].cast() : null; + request[14] != null ? request[14].cast() : 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( diff --git a/pkg/vm/lib/incremental_compiler.dart b/pkg/vm/lib/incremental_compiler.dart index a8d32713a9f..3ad1270525a 100644 --- a/pkg/vm/lib/incremental_compiler.dart +++ b/pkg/vm/lib/incremental_compiler.dart @@ -188,11 +188,14 @@ class IncrementalCompiler { List typeDefinitions, String libraryUri, String? klass, + String? method, bool isStatic) { Map 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 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); } } diff --git a/pkg/vm/test/incremental_compiler_test.dart b/pkg/vm/test/incremental_compiler_test.dart index 700a5062149..1623621e9bc 100644 --- a/pkg/vm/test/incremental_compiler_test.dart +++ b/pkg/vm/test/incremental_compiler_test.dart @@ -112,14 +112,14 @@ main() { await compiler.compile(); compiler.accept(); { - Procedure? procedure = await compiler.compileExpression( - 'main', [], [], main.uri.toString(), null, true); + Procedure? procedure = await compiler.compileExpression('main', + [], [], main.uri.toString(), null, null, true); expect(procedure, isNotNull); expect(errorsReported, equals(0)); } { - Procedure? procedure = await compiler.compileExpression( - 'main1', [], [], main.uri.toString(), null, true); + Procedure? procedure = await compiler.compileExpression('main1', + [], [], 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', [], [], 'package:foo/bar.dart', 'A', true); + Procedure? procedure = await compiler.compileExpression('a', [], + [], 'package:foo/bar.dart', 'A', null, true); expect(procedure, isNotNull); } @@ -1039,8 +1039,8 @@ main() { } await compiler.reject(); { - Procedure? procedure = await compiler.compileExpression( - 'a', [], [], 'package:foo/bar.dart', 'A', true); + Procedure? procedure = await compiler.compileExpression('a', [], + [], 'package:foo/bar.dart', 'A', null, true); expect(procedure, isNotNull); } }); @@ -1088,7 +1088,7 @@ main() { compiler.accept(); { final Procedure procedure = (await compiler.compileExpression( - 'a', [], [], barUri.toString(), 'A', true))!; + 'a', [], [], 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', [], [], barUri.toString(), 'A', true))!; + 'a', [], [], barUri.toString(), 'A', null, true))!; // Verify that the expression only has links to the original bar. final LibraryReferenceCollector lrc = new LibraryReferenceCollector(); procedure.accept(lrc); diff --git a/runtime/observatory/tests/service/evaluate_in_extension_method_test.dart b/runtime/observatory/tests/service/evaluate_in_extension_method_test.dart new file mode 100644 index 00000000000..540b1f30aa1 --- /dev/null +++ b/runtime/observatory/tests/service/evaluate_in_extension_method_test.dart @@ -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 = [ + 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); diff --git a/runtime/observatory/tests/service/service_kernel.status b/runtime/observatory/tests/service/service_kernel.status index 303c0b690a7..fae5fd02c87 100644 --- a/runtime/observatory/tests/service/service_kernel.status +++ b/runtime/observatory/tests/service/service_kernel.status @@ -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. diff --git a/runtime/observatory_2/pubspec.yaml b/runtime/observatory_2/pubspec.yaml index efd6952d6ef..253ce60963f 100644 --- a/runtime/observatory_2/pubspec.yaml +++ b/runtime/observatory_2/pubspec.yaml @@ -1,6 +1,6 @@ name: observatory environment: - sdk: '>=2.2.2 <3.0.0' + sdk: '>=2.6.0 <3.0.0' dependencies: usage: 'any' diff --git a/runtime/observatory_2/tests/service_2/evaluate_in_extension_method_test.dart b/runtime/observatory_2/tests/service_2/evaluate_in_extension_method_test.dart new file mode 100644 index 00000000000..5a457481f61 --- /dev/null +++ b/runtime/observatory_2/tests/service_2/evaluate_in_extension_method_test.dart @@ -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 = [ + 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); diff --git a/runtime/observatory_2/tests/service_2/service_2_kernel.status b/runtime/observatory_2/tests/service_2/service_2_kernel.status index 16a8edc6f1f..98dd984c2bd 100644 --- a/runtime/observatory_2/tests/service_2/service_2_kernel.status +++ b/runtime/observatory_2/tests/service_2/service_2_kernel.status @@ -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. diff --git a/runtime/vm/compiler_test.cc b/runtime/vm/compiler_test.cc index 49df77ff3fb..649bca40fe1 100644 --- a/runtime/vm/compiler_test.cc +++ b/runtime/vm/compiler_test.cc @@ -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); diff --git a/runtime/vm/debugger_api_impl_test.cc b/runtime/vm/debugger_api_impl_test.cc index 51ba8bd596a..f7b5cfde113 100644 --- a/runtime/vm/debugger_api_impl_test.cc +++ b/runtime/vm/debugger_api_impl_test.cc @@ -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."); diff --git a/runtime/vm/kernel_isolate.cc b/runtime/vm/kernel_isolate.cc index 4156252e4b3..c1df5921ee5 100644 --- a/runtime/vm/kernel_isolate.cc +++ b/runtime/vm/kernel_isolate.cc @@ -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* 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(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_); } diff --git a/runtime/vm/kernel_isolate.h b/runtime/vm/kernel_isolate.h index 2ba4f2c2df4..5880beb0e71 100644 --- a/runtime/vm/kernel_isolate.h +++ b/runtime/vm/kernel_isolate.h @@ -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(); diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc index 17b42b696f2..1b05ac5c774 100644 --- a/runtime/vm/service.cc +++ b/runtime/vm/service.cc @@ -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); diff --git a/runtime/vm/unit_test.cc b/runtime/vm/unit_test.cc index cb67c6fc665..e96548eb77d 100644 --- a/runtime/vm/unit_test.cc +++ b/runtime/vm/unit_test.cc @@ -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); diff --git a/sdk/lib/vmservice/running_isolates.dart b/sdk/lib/vmservice/running_isolates.dart index f9db4a5058d..17ba44877b1 100644 --- a/sdk/lib/vmservice/running_isolates.dart +++ b/sdk/lib/vmservice/running_isolates.dart @@ -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;