diff --git a/pkg/front_end/lib/src/api_prototype/expression_compilation_tools.dart b/pkg/front_end/lib/src/api_prototype/expression_compilation_tools.dart new file mode 100644 index 00000000000..f0786277051 --- /dev/null +++ b/pkg/front_end/lib/src/api_prototype/expression_compilation_tools.dart @@ -0,0 +1,213 @@ +// Copyright (c) 2022, 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 'package:front_end/src/api_prototype/incremental_kernel_generator.dart' + show isLegalIdentifier; + +import 'package:front_end/src/api_prototype/lowering_predicates.dart' + show isExtensionThisName; + +import 'package:kernel/ast.dart' + show + Class, + DartType, + DynamicType, + InterfaceType, + Library, + Nullability, + TypeParameter; + +import 'package:kernel/library_index.dart' show LibraryIndex; + +Map? createDefinitionsWithTypes( + Iterable? knownLibraries, + List definitionTypes, + List definitions) { + if (knownLibraries == null) { + return null; + } + + List definitionTypesParsed = + parseDefinitionTypes(definitionTypes); + if (definitionTypesParsed.length != definitions.length) { + return null; + } + + Set libraryUris = collectParsedTypeUris(definitionTypesParsed); + LibraryIndex libraryIndex = + new LibraryIndex.fromLibraries(knownLibraries, libraryUris); + + Map completeDefinitions = {}; + for (int i = 0; i < definitions.length; i++) { + String name = definitions[i]; + if (isLegalIdentifier(name) || (i == 0 && isExtensionThisName(name))) { + ParsedType type = definitionTypesParsed[i]; + DartType dartType = type.createDartType(libraryIndex); + completeDefinitions[name] = dartType; + } + } + return completeDefinitions; +} + +List? createTypeParametersWithBounds( + Iterable? knownLibraries, + List typeBounds, + List typeDefaults, + List typeDefinitions) { + if (knownLibraries == null) { + return null; + } + + List typeBoundsParsed = parseDefinitionTypes(typeBounds); + if (typeBoundsParsed.length != typeDefinitions.length) { + return null; + } + List typeDefaultsParsed = parseDefinitionTypes(typeDefaults); + if (typeDefaultsParsed.length != typeDefinitions.length) { + return null; + } + + Set libraryUris = collectParsedTypeUris(typeBoundsParsed) + ..addAll(collectParsedTypeUris(typeDefaultsParsed)); + LibraryIndex libraryIndex = + new LibraryIndex.fromLibraries(knownLibraries, libraryUris); + + List typeParameters = []; + for (int i = 0; i < typeDefinitions.length; i++) { + String name = typeDefinitions[i]; + if (!isLegalIdentifier(name)) continue; + ParsedType bound = typeBoundsParsed[i]; + DartType dartTypeBound = bound.createDartType(libraryIndex); + ParsedType defaultType = typeDefaultsParsed[i]; + DartType dartTypeDefaultType = defaultType.createDartType(libraryIndex); + typeParameters + .add(new TypeParameter(name, dartTypeBound, dartTypeDefaultType)); + } + return typeParameters; +} + +List parseDefinitionTypes(List definitionTypes) { + List result = []; + int i = 0; + List argumentReceivers = []; + while (i < definitionTypes.length) { + String uriOrNullString = definitionTypes[i]; + if (uriOrNullString == "null") { + if (argumentReceivers.isEmpty) { + result.add(new ParsedType.nullType()); + } else { + argumentReceivers + .removeLast() + .arguments! + .add(new ParsedType.nullType()); + } + i++; + continue; + } else { + // We expect at least 4 elements: Uri, class name, nullability, + // number of type arguments. + if (i + 4 > definitionTypes.length) throw "invalid input"; + String className = definitionTypes[i + 1]; + int nullability = int.parse(definitionTypes[i + 2]); + int typeArgumentsCount = int.parse(definitionTypes[i + 3]); + ParsedType type = new ParsedType(uriOrNullString, className, nullability); + if (argumentReceivers.isEmpty) { + result.add(type); + } else { + argumentReceivers.removeLast().arguments!.add(type); + } + for (int j = 0; j < typeArgumentsCount; j++) { + argumentReceivers.add(type); + } + i += 4; + } + } + if (argumentReceivers.isNotEmpty) { + throw "Nesting error on input $definitionTypes"; + } + return result; +} + +class ParsedType { + final String? uri; + final String? className; + final int? nullability; + final List? arguments; + + bool get isNullType => uri == null; + + ParsedType(this.uri, this.className, this.nullability) : arguments = []; + + ParsedType.nullType() + : uri = null, + className = null, + nullability = null, + arguments = null; + + @override + bool operator ==(Object other) { + if (other is! ParsedType) return false; + if (uri != other.uri) return false; + if (className != other.className) return false; + if (nullability != other.nullability) return false; + if (arguments?.length != other.arguments?.length) return false; + if (arguments != null) { + for (int i = 0; i < arguments!.length; i++) { + if (arguments![i] != other.arguments![i]) return false; + } + } + return true; + } + + @override + int get hashCode { + if (isNullType) return 0; + int hash = 0x3fffffff & uri.hashCode; + hash = 0x3fffffff & (hash * 31 + (hash ^ className.hashCode)); + hash = 0x3fffffff & (hash * 31 + (hash ^ nullability.hashCode)); + for (ParsedType argument in arguments!) { + hash = 0x3fffffff & (hash * 31 + (hash ^ argument.hashCode)); + } + return hash; + } + + @override + String toString() { + if (isNullType) return "null-type"; + return "$uri[$className] ($nullability) <$arguments>"; + } + + DartType createDartType(LibraryIndex libraryIndex) { + if (isNullType) return new DynamicType(); + Class? classNode = libraryIndex.tryGetClass(uri!, className!); + if (classNode == null) return new DynamicType(); + + return new InterfaceType( + classNode, + _getDartNullability(), + arguments + ?.map((e) => e.createDartType(libraryIndex)) + .toList(growable: false)); + } + + Nullability _getDartNullability() { + if (isNullType) throw "No nullability on the null type"; + if (nullability == 0) return Nullability.nullable; + if (nullability == 1) return Nullability.nonNullable; + if (nullability == 2) return Nullability.legacy; + throw "Unknown nullability"; + } +} + +Set collectParsedTypeUris(List parsedTypes) { + Set result = {}; + List workList = new List.from(parsedTypes); + while (workList.isNotEmpty) { + ParsedType type = workList.removeLast(); + if (type.isNullType) continue; + result.add(type.uri!); + workList.addAll(type.arguments!); + } + return result; +} diff --git a/pkg/front_end/lib/src/api_unstable/vm.dart b/pkg/front_end/lib/src/api_unstable/vm.dart index b9158478e6d..9190ebc8315 100644 --- a/pkg/front_end/lib/src/api_unstable/vm.dart +++ b/pkg/front_end/lib/src/api_unstable/vm.dart @@ -18,6 +18,9 @@ export '../api_prototype/compiler_options.dart' export '../api_prototype/experimental_flags.dart' show defaultExperimentalFlags, ExperimentalFlag; +export '../api_prototype/expression_compilation_tools.dart' + show createDefinitionsWithTypes, createTypeParametersWithBounds; + export '../api_prototype/file_system.dart' show FileSystem, FileSystemEntity, FileSystemException; diff --git a/pkg/front_end/test/expression_compilation_tools_test.dart b/pkg/front_end/test/expression_compilation_tools_test.dart new file mode 100644 index 00000000000..9e12fb443b3 --- /dev/null +++ b/pkg/front_end/test/expression_compilation_tools_test.dart @@ -0,0 +1,135 @@ +// Copyright (c) 2022, 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 'package:expect/minitest.dart'; + +import 'package:front_end/src/api_prototype/expression_compilation_tools.dart'; + +void main() { + // null value. + expect(parseDefinitionTypes(["null"]), [new ParsedType.nullType()]); + + // String, kNonNullable + expect( + parseDefinitionTypes([ + "dart:core", + "_OneByteString", + "1", + "0", + ]), + [new ParsedType("dart:core", "_OneByteString", 1)]); + + // List, kNonNullable. + expect(parseDefinitionTypes(["dart:core", "List", "1", "1", "null"]), [ + new ParsedType("dart:core", "List", 1) + ..arguments!.add(new ParsedType.nullType()) + ]); + + // List, kNonNullable + expect( + parseDefinitionTypes([ + "dart:core", + "_GrowableList", + "1", + "1", + "dart:core", + "int", + "1", + "0", + ]), + [ + new ParsedType("dart:core", "_GrowableList", 1) + ..arguments!.add(new ParsedType("dart:core", "int", 1)) + ]); + + // Map, kNonNullable + expect( + parseDefinitionTypes([ + "dart:core", + "Map", + "1", + "2", + "dart:core", + "int", + "1", + "0", + "dart:core", + "int", + "1", + "0", + ]), + [ + new ParsedType("dart:core", "Map", 1) + ..arguments!.add(new ParsedType("dart:core", "int", 1)) + ..arguments!.add(new ParsedType("dart:core", "int", 1)) + ]); + + // [0] = String + // [1] = int + // [2] = List + // [3] = Bar + // [4] = null + // [5] = HashMap>, List> + expect( + parseDefinitionTypes([ + // String, kNonNullable, no arguments + "dart:core", "_OneByteString", "1", "0", + // Int, kNonNullable, no arguments + "dart:core", "_Smi", "1", "0", + // List, kNonNullable, 1 argument + "dart:core", "_GrowableList", "1", "1", + // -> String, kNonNullable (i.e. the above is List) + /**/ "dart:core", "String", "1", "0", + // "Bar", kNonNullable, no arguments + "file://wherever/t.dart", "Bar", "1", "0", + // null value + "null", + // HashMap, kNonNullable, 2 arguments + "dart:collection", "_InternalLinkedHashMap", "1", "2", + // -> Map, kNonNullable, 2 arguments + /**/ "dart:core", "Map", "1", "2", + // -> -> int, kNonNullable, no arguments + /*/**/*/ "dart:core", "int", "1", "0", + // -> -> List, kNonNullable, 1 argument + /*/**/*/ "dart:core", "List", "1", "1", + // -> -> -> int, kNonNullable, no arguments + /*/*/**/*/*/ "dart:core", "int", "1", "0", + // -> List, kNonNullable, 1 argument + "dart:core", "List", "1", "1", + // -> -> String, kNonNullable, no arguments + "dart:core", "String", "1", "0" + ]), + [ + // String + new ParsedType("dart:core", "_OneByteString", 1), + // int + new ParsedType("dart:core", "_Smi", 1), + // List + new ParsedType("dart:core", "_GrowableList", 1) + ..arguments!.addAll([ + new ParsedType("dart:core", "String", 1), + ]), + // Bar + new ParsedType("file://wherever/t.dart", "Bar", 1), + // null value + new ParsedType.nullType(), + // HashMap>, List> + new ParsedType("dart:collection", "_InternalLinkedHashMap", 1) + ..arguments!.addAll([ + new ParsedType("dart:core", "Map", 1) + ..arguments!.addAll([ + new ParsedType("dart:core", "int", 1), + new ParsedType("dart:core", "List", 1) + ..arguments!.addAll([ + new ParsedType("dart:core", "int", 1), + ]), + ]), + new ParsedType("dart:core", "List", 1) + ..arguments!.addAll([ + new ParsedType("dart:core", "String", 1), + ]), + ]), + ]); +} diff --git a/pkg/front_end/test/fasta/expression_suite.dart b/pkg/front_end/test/fasta/expression_suite.dart index 03eeb59e61c..520e7e1fb0b 100644 --- a/pkg/front_end/test/fasta/expression_suite.dart +++ b/pkg/front_end/test/fasta/expression_suite.dart @@ -16,6 +16,12 @@ import "package:front_end/src/api_prototype/compiler_options.dart" show CompilerOptions, DiagnosticMessage; import 'package:front_end/src/api_prototype/experimental_flags.dart'; +import 'package:front_end/src/api_prototype/expression_compilation_tools.dart' + show createDefinitionsWithTypes, createTypeParametersWithBounds; + +import 'package:front_end/src/api_prototype/incremental_kernel_generator.dart' + show IncrementalCompilerResult; + import "package:front_end/src/api_prototype/memory_file_system.dart" show MemoryFileSystem; @@ -34,7 +40,7 @@ import 'package:front_end/src/fasta/incremental_compiler.dart' show IncrementalCompiler; import "package:kernel/ast.dart" - show Procedure, Component, DynamicType, DartType, TypeParameter; + show Component, DartType, DynamicType, Procedure, TypeParameter; import 'package:kernel/target/targets.dart' show TargetFlags; @@ -132,8 +138,14 @@ class TestCase { final List definitions; + final List definitionTypes; + final List typeDefinitions; + final List typeBounds; + + final List typeDefaults; + final bool isStaticMethod; final Uri? library; @@ -151,7 +163,10 @@ class TestCase { this.entryPoint, this.import, this.definitions, + this.definitionTypes, this.typeDefinitions, + this.typeBounds, + this.typeDefaults, this.isStaticMethod, this.library, this.className, @@ -164,7 +179,10 @@ class TestCase { "$entryPoint, " "$import, " "$definitions, " + "$definitionTypes, " "$typeDefinitions," + "$typeBounds," + "$typeDefaults," "$library, " "$className, " "static = $isStaticMethod)"; @@ -259,7 +277,10 @@ class ReadTest extends Step, Context> { Uri? entryPoint; Uri? import; List definitions = []; + List definitionTypes = []; List typeDefinitions = []; + List typeBounds = []; + List typeDefaults = []; bool isStaticMethod = false; Uri? library; String? className; @@ -289,9 +310,16 @@ class ReadTest extends Step, Context> { methodName = value as String; } else if (key == "definitions") { definitions = (value as YamlList).map((x) => x as String).toList(); + } else if (key == "definition_types") { + definitionTypes = + (value as YamlList).map((x) => x as String).toList(); } else if (key == "type_definitions") { typeDefinitions = (value as YamlList).map((x) => x as String).toList(); + } else if (key == "type_bounds") { + typeBounds = (value as YamlList).map((x) => x as String).toList(); + } else if (key == "type_defaults") { + typeDefaults = (value as YamlList).map((x) => x as String).toList(); } else if (key == "static") { isStaticMethod = value; } else if (key == "expression") { @@ -303,7 +331,10 @@ class ReadTest extends Step, Context> { entryPoint, import, definitions, + definitionTypes, typeDefinitions, + typeBounds, + typeDefaults, isStaticMethod, library, className, @@ -328,15 +359,29 @@ class CompileExpression extends Step, List, Context> { // Compile [test.expression], update [test.errors] with results. // As a side effect - verify that generated procedure can be serialized. Future compileExpression(TestCase test, IncrementalCompiler compiler, - Component component, Context context) async { - Map definitions = {}; - for (String name in test.definitions) { - definitions[name] = new DynamicType(); + IncrementalCompilerResult sourceCompilerResult, Context context) async { + Map? definitions = createDefinitionsWithTypes( + sourceCompilerResult.classHierarchy?.knownLibraries, + test.definitionTypes, + test.definitions); + + if (definitions == null) { + definitions = {}; + for (String name in test.definitions) { + definitions[name] = new DynamicType(); + } } - List typeParams = []; - for (String name in test.typeDefinitions) { - typeParams - .add(new TypeParameter(name, new DynamicType(), new DynamicType())); + List? typeParams = createTypeParametersWithBounds( + sourceCompilerResult.classHierarchy?.knownLibraries, + test.typeBounds, + test.typeDefaults, + test.typeDefinitions); + if (typeParams == null) { + typeParams = []; + for (String name in test.typeDefinitions) { + typeParams + .add(new TypeParameter(name, new DynamicType(), new DynamicType())); + } } Procedure? compiledProcedure = await compiler.compileExpression( @@ -353,7 +398,7 @@ class CompileExpression extends Step, List, Context> { test.results.add(new CompilationResult(compiledProcedure, errors)); if (compiledProcedure != null) { // Confirm we can serialize generated procedure. - component.computeCanonicalNames(); + sourceCompilerResult.component.computeCanonicalNames(); List list = serializeProcedure(compiledProcedure); assert(list.length > 0); } @@ -371,8 +416,9 @@ class CompileExpression extends Step, List, Context> { await new File.fromUri(test.import!).readAsBytes()); } - var sourceCompiler = new IncrementalCompiler(context.compilerContext); - var sourceCompilerResult = + IncrementalCompiler sourceCompiler = + new IncrementalCompiler(context.compilerContext); + IncrementalCompilerResult sourceCompilerResult = await sourceCompiler.computeDelta(entryPoints: [test.entryPoint!]); Component component = sourceCompilerResult.component; var errors = context.takeErrors(); @@ -387,11 +433,12 @@ class CompileExpression extends Step, List, Context> { path: test.entryPoint!.path + ".dill"); Uint8List dillData = await serializeComponent(component); context.fileSystem.entityForUri(dillFileUri).writeAsBytesSync(dillData); - await compileExpression(test, sourceCompiler, component, context); + await compileExpression( + test, sourceCompiler, sourceCompilerResult, context); - var dillCompiler = + IncrementalCompiler dillCompiler = new IncrementalCompiler(context.compilerContext, dillFileUri); - var dillCompilerResult = + IncrementalCompilerResult dillCompilerResult = await dillCompiler.computeDelta(entryPoints: [test.entryPoint!]); component = dillCompilerResult.component; component.computeCanonicalNames(); @@ -400,7 +447,7 @@ class CompileExpression extends Step, List, Context> { // Since it compiled successfully from source, the bootstrap-from-Dill // should also succeed without errors. assert(errors.isEmpty); - await compileExpression(test, dillCompiler, component, context); + await compileExpression(test, dillCompiler, dillCompilerResult, context); } return new Result.pass(tests); } diff --git a/pkg/front_end/test/spell_checking_list_tests.txt b/pkg/front_end/test/spell_checking_list_tests.txt index bea0c4db436..33568c9cd6a 100644 --- a/pkg/front_end/test/spell_checking_list_tests.txt +++ b/pkg/front_end/test/spell_checking_list_tests.txt @@ -398,6 +398,7 @@ micro minimize minimizer minimizing +minitest minst mintty minutes diff --git a/pkg/front_end/testcases/expression/evaluate_extension_method.expression.yaml b/pkg/front_end/testcases/expression/evaluate_extension_method.expression.yaml new file mode 100644 index 00000000000..ad1a058b007 --- /dev/null +++ b/pkg/front_end/testcases/expression/evaluate_extension_method.expression.yaml @@ -0,0 +1,17 @@ +# Copyright (c) 2022, 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: ["x"] +# List +definition_types: ["dart:core", "List", "1", "1", "dart:core", "String", "1", "0"] +type_definitions: [""] +type_bounds: [] +type_defaults: [] +position: "main.dart" +method: "hasList" +# x is List with entries, so x.first is String, String has extension +# with method getFortyTwo. +expression: | + x.first.getFortyTwo() diff --git a/pkg/front_end/testcases/expression/evaluate_extension_method.expression.yaml.expect b/pkg/front_end/testcases/expression/evaluate_extension_method.expression.yaml.expect new file mode 100644 index 00000000000..8545fbaa42e --- /dev/null +++ b/pkg/front_end/testcases/expression/evaluate_extension_method.expression.yaml.expect @@ -0,0 +1,4 @@ +Errors: { +} +method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr< extends dynamic>(dart.core::List x) → dynamic + return main::Foo|getFortyTwo(x.{dart.core::Iterable::first}{dart.core::String*}); diff --git a/pkg/front_end/testcases/expression/evaluate_fold_on_list.expression.yaml b/pkg/front_end/testcases/expression/evaluate_fold_on_list.expression.yaml new file mode 100644 index 00000000000..e43e163dde4 --- /dev/null +++ b/pkg/front_end/testcases/expression/evaluate_fold_on_list.expression.yaml @@ -0,0 +1,16 @@ +# Copyright (c) 2022, 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: ["x", "y"] +# List, int +definition_types: ["dart:core", "List", "1", "1", "dart:core", "String", "1", "0", "dart:core", "int", "1", "0"] +type_definitions: [] +type_bounds: [] +type_defaults: [] +position: "main.dart" +method: "hasList" +# Because x has a type (List) x.fold knows that element is a String. +expression: | + x.fold(0, (previousValue, element) => previousValue + element.length) diff --git a/pkg/front_end/testcases/expression/evaluate_fold_on_list.expression.yaml.expect b/pkg/front_end/testcases/expression/evaluate_fold_on_list.expression.yaml.expect new file mode 100644 index 00000000000..84d4688a996 --- /dev/null +++ b/pkg/front_end/testcases/expression/evaluate_fold_on_list.expression.yaml.expect @@ -0,0 +1,4 @@ +Errors: { +} +method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr(dart.core::List x, dart.core::int y) → dynamic + return x.{dart.core::Iterable::fold}(0, (dart.core::int* previousValue, dart.core::String* element) → dart.core::int* => previousValue.{dart.core::num::+}(element.{dart.core::String::length}{dart.core::int*}){(dart.core::num*) →* dart.core::int*}){(dart.core::int*, (dart.core::int*, dart.core::String*) →* dart.core::int*) →* dart.core::int*}; diff --git a/pkg/front_end/testcases/expression/main.dart b/pkg/front_end/testcases/expression/main.dart index 0c25d47089a..59f53f00702 100644 --- a/pkg/front_end/testcases/expression/main.dart +++ b/pkg/front_end/testcases/expression/main.dart @@ -100,3 +100,30 @@ class E { T _t; T get t => _t; } + +void withBound(List x) { + List y = []; + List z = []; + z.addAll(y); +} + +void withBound2() { + print(E); +} + +void hasList() { + List x = ["a", "b", "c"]; + int xCombinedLength = x.fold( + 0, (previousValue, element) => previousValue + element.length); + print("xCombinedLength = $xCombinedLength"); +} + +void hasClosure() { + List x() { + return ["hello"]; + } + + int xCombinedLength = x() + .fold(0, (previousValue, element) => previousValue + element.length); + print("xCombinedLength = $xCombinedLength"); +} diff --git a/pkg/front_end/testcases/expression/type_closure_evaluation.expression.yaml b/pkg/front_end/testcases/expression/type_closure_evaluation.expression.yaml new file mode 100644 index 00000000000..5565212416e --- /dev/null +++ b/pkg/front_end/testcases/expression/type_closure_evaluation.expression.yaml @@ -0,0 +1,15 @@ +# Copyright (c) 2022, 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: ["x"] +# _Closure --- note that this is not what the VM sends (anymore). +definition_types: ["dart:core", "_Closure", "1", "0"] +type_definitions: [] +type_bounds: [] +type_defaults: [] +position: "main.dart" +method: "hasClosure" +expression: | + x().fold(0, (previousValue, element) => previousValue + element.length) diff --git a/pkg/front_end/testcases/expression/type_closure_evaluation.expression.yaml.expect b/pkg/front_end/testcases/expression/type_closure_evaluation.expression.yaml.expect new file mode 100644 index 00000000000..5865e1f0314 --- /dev/null +++ b/pkg/front_end/testcases/expression/type_closure_evaluation.expression.yaml.expect @@ -0,0 +1,9 @@ +Errors: { + org-dartlang-debug:synthetic_debug_expression:1:2: Error: Cannot invoke an instance of '_Closure' because it declares 'call' to be something other than a method. + - '_Closure' is from 'dart:core'. + Try changing 'call' to a method or explicitly invoke 'call'. + x().fold(0, (previousValue, element) => previousValue + element.length) + ^ +} +method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr(dart.core::_Closure x) → dynamic + return invalid-expression "org-dartlang-debug:synthetic_debug_expression:1:2: Error: Cannot invoke an instance of '_Closure' because it declares 'call' to be something other than a method.\n - '_Closure' is from 'dart:core'.\nTry changing 'call' to a method or explicitly invoke 'call'.\nx().fold(0, (previousValue, element) => previousValue + element.length)\n ^"{dynamic}.fold(0, (dynamic previousValue, dynamic element) → dynamic => previousValue{dynamic}.+(element{dynamic}.length)); diff --git a/pkg/front_end/testcases/expression/type_closure_evaluation_2.expression.yaml b/pkg/front_end/testcases/expression/type_closure_evaluation_2.expression.yaml new file mode 100644 index 00000000000..f441fdd5501 --- /dev/null +++ b/pkg/front_end/testcases/expression/type_closure_evaluation_2.expression.yaml @@ -0,0 +1,15 @@ +# Copyright (c) 2022, 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: ["x"] +# _Closure is send as null aka dynamic. +definition_types: ["null"] +type_definitions: [] +type_bounds: [] +type_defaults: [] +position: "main.dart" +method: "hasClosure" +expression: | + x().fold(0, (previousValue, element) => previousValue + element.length) diff --git a/pkg/front_end/testcases/expression/type_closure_evaluation_2.expression.yaml.expect b/pkg/front_end/testcases/expression/type_closure_evaluation_2.expression.yaml.expect new file mode 100644 index 00000000000..7b31ce4254d --- /dev/null +++ b/pkg/front_end/testcases/expression/type_closure_evaluation_2.expression.yaml.expect @@ -0,0 +1,4 @@ +Errors: { +} +method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr(dynamic x) → dynamic + return x{dynamic}.call(){dynamic}.fold(0, (dynamic previousValue, dynamic element) → dynamic => previousValue{dynamic}.+(element{dynamic}.length)); diff --git a/pkg/front_end/testcases/expression/type_definition_bound_1.expression.yaml b/pkg/front_end/testcases/expression/type_definition_bound_1.expression.yaml new file mode 100644 index 00000000000..c8bbfc0a105 --- /dev/null +++ b/pkg/front_end/testcases/expression/type_definition_bound_1.expression.yaml @@ -0,0 +1,18 @@ +# Copyright (c) 2022, 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: ["x"] +# List +definition_types: ["dart:core", "List", "1", "1", "dart:core", "String", "1", "0"] +type_definitions: ["E"] +# String +type_bounds: ["dart:core", "String", "1", "0"] +# String +type_defaults: ["dart:core", "String", "1", "0"] +position: "main.dart" +method: "withBound" +# Can add List to List as E extends String. +expression: | + () { List y = []; x.addAll(y); }() diff --git a/pkg/front_end/testcases/expression/type_definition_bound_1.expression.yaml.expect b/pkg/front_end/testcases/expression/type_definition_bound_1.expression.yaml.expect new file mode 100644 index 00000000000..680d8c5975c --- /dev/null +++ b/pkg/front_end/testcases/expression/type_definition_bound_1.expression.yaml.expect @@ -0,0 +1,7 @@ +Errors: { +} +method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr(dart.core::List x) → dynamic + return (() → Null { + dart.core::List<#lib1::debugExpr::E*>* y = dart.core::_GrowableList::•<#lib1::debugExpr::E*>(0); + x.{dart.core::List::addAll}(y){(dart.core::Iterable*) →* void}; + })(){() →* Null}; diff --git a/pkg/front_end/testcases/expression/type_definition_bound_2.expression.yaml b/pkg/front_end/testcases/expression/type_definition_bound_2.expression.yaml new file mode 100644 index 00000000000..ca8e9039d54 --- /dev/null +++ b/pkg/front_end/testcases/expression/type_definition_bound_2.expression.yaml @@ -0,0 +1,18 @@ +# Copyright (c) 2022, 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: ["x"] +# List +definition_types: ["dart:core", "List", "1", "1", "dart:core", "String", "1", "0"] +type_definitions: ["E"] +# String +type_bounds: ["dart:core", "String", "1", "0"] +# String +type_defaults: ["dart:core", "String", "1", "0"] +position: "main.dart" +method: "withBound" +# Can't add List to List :( +expression: | + () { List y = []; y.addAll(x); } diff --git a/pkg/front_end/testcases/expression/type_definition_bound_2.expression.yaml.expect b/pkg/front_end/testcases/expression/type_definition_bound_2.expression.yaml.expect new file mode 100644 index 00000000000..be07da06f2c --- /dev/null +++ b/pkg/front_end/testcases/expression/type_definition_bound_2.expression.yaml.expect @@ -0,0 +1,12 @@ +Errors: { + org-dartlang-debug:synthetic_debug_expression:1:31: Error: The argument type 'List' can't be assigned to the parameter type 'Iterable'. + - 'List' is from 'dart:core'. + - 'Iterable' is from 'dart:core'. + () { List y = []; y.addAll(x); } + ^ +} +method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr(dart.core::List x) → dynamic + return () → Null { + dart.core::List<#lib1::debugExpr::E*>* y = dart.core::_GrowableList::•<#lib1::debugExpr::E*>(0); + y.{dart.core::List::addAll}(invalid-expression "org-dartlang-debug:synthetic_debug_expression:1:31: Error: The argument type 'List' can't be assigned to the parameter type 'Iterable'.\n - 'List' is from 'dart:core'.\n - 'Iterable' is from 'dart:core'.\n() { List y = []; y.addAll(x); }\n ^" in x as{TypeError} Never){(dart.core::Iterable<#lib1::debugExpr::E*>*) →* void}; + }; diff --git a/pkg/front_end/testcases/expression/type_definition_bound_3.expression.yaml b/pkg/front_end/testcases/expression/type_definition_bound_3.expression.yaml new file mode 100644 index 00000000000..80d4f5b1473 --- /dev/null +++ b/pkg/front_end/testcases/expression/type_definition_bound_3.expression.yaml @@ -0,0 +1,16 @@ +# Copyright (c) 2022, 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: [] +definition_types: [] +type_definitions: ["E"] +# Object? +type_bounds: ["dart:core", "Object", "0", "0"] +# null (because dynamic). +type_defaults: ["null"] +position: "main.dart" +method: "withBound2" +expression: | + print(E) diff --git a/pkg/front_end/testcases/expression/type_definition_bound_3.expression.yaml.expect b/pkg/front_end/testcases/expression/type_definition_bound_3.expression.yaml.expect new file mode 100644 index 00000000000..f70deb222dc --- /dev/null +++ b/pkg/front_end/testcases/expression/type_definition_bound_3.expression.yaml.expect @@ -0,0 +1,4 @@ +Errors: { +} +method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr() → dynamic + return dart.core::print(#lib1::debugExpr::E*); diff --git a/pkg/frontend_server/lib/frontend_server.dart b/pkg/frontend_server/lib/frontend_server.dart index c1dac119306..d59f08270dc 100644 --- a/pkg/frontend_server/lib/frontend_server.dart +++ b/pkg/frontend_server/lib/frontend_server.dart @@ -285,7 +285,10 @@ abstract class CompilerInterface { Future compileExpression( String expression, List definitions, + List definitionTypes, List typeDefinitions, + List typeBounds, + List typeDefaults, String libraryUri, String klass, String method, @@ -840,15 +843,27 @@ class FrontendCompiler implements CompilerInterface { Future compileExpression( String expression, List definitions, + List definitionTypes, List typeDefinitions, + List typeBounds, + List typeDefaults, 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, method, isStatic); + Procedure procedure = await _generator.compileExpression( + expression, + definitions, + definitionTypes, + typeDefinitions, + typeBounds, + typeDefaults, + libraryUri, + klass, + method, + isStatic); if (procedure != null) { Component component = createExpressionEvaluationComponent(procedure); final IOSink sink = File(_kernelBinaryFilename).openWrite(); @@ -1127,7 +1142,10 @@ class _CompileExpressionRequest { // Note that FE will reject a compileExpression command by returning a null // procedure when defs or typeDefs include an illegal identifier. List defs = []; + List defTypes = []; List typeDefs = []; + List typeBounds = []; + List typeDefaults = []; String library; String klass; String method; @@ -1268,7 +1286,10 @@ StreamSubscription listenAndCompile(CompilerInterface compiler, compiler.compileExpression( compileExpressionRequest.expression, compileExpressionRequest.defs, + compileExpressionRequest.defTypes, compileExpressionRequest.typeDefs, + compileExpressionRequest.typeBounds, + compileExpressionRequest.typeDefaults, compileExpressionRequest.library, compileExpressionRequest.klass, compileExpressionRequest.method, diff --git a/pkg/kernel/lib/library_index.dart b/pkg/kernel/lib/library_index.dart index e2f89df649b..5f231b44d5a 100644 --- a/pkg/kernel/lib/library_index.dart +++ b/pkg/kernel/lib/library_index.dart @@ -22,9 +22,14 @@ class LibraryIndex { final Map _libraries = {}; /// Indexes the libraries with the URIs given in [libraryUris]. - LibraryIndex(Component component, Iterable libraryUris) { + LibraryIndex(Component component, Iterable libraryUris) + : this.fromLibraries(component.libraries, libraryUris); + + /// Indexes the libraries with the URIs given in [libraryUris]. + LibraryIndex.fromLibraries( + Iterable libraries, Iterable libraryUris) { Set libraryUriSet = libraryUris.toSet(); - for (Library library in component.libraries) { + for (Library library in libraries) { String uri = '${library.importUri}'; if (libraryUriSet.contains(uri)) { _libraries[uri] = new _ClassTable(library); diff --git a/pkg/vm/bin/kernel_service.dart b/pkg/vm/bin/kernel_service.dart index f18f0d20e22..43240f8944d 100644 --- a/pkg/vm/bin/kernel_service.dart +++ b/pkg/vm/bin/kernel_service.dart @@ -500,16 +500,19 @@ Future _processExpressionCompilationRequest(request) async { final dynamic dart_platform_kernel = request[3]; final String expression = request[4]; final List definitions = request[5].cast(); - final List typeDefinitions = request[6].cast(); - final String libraryUri = request[7]; - final String? klass = request[8]; - 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 definitionTypes = request[6].cast(); + final List typeDefinitions = request[7].cast(); + final List typeBounds = request[8].cast(); + final List typeDefaults = request[9].cast(); + final String libraryUri = request[10]; + final String? klass = request[11]; + final String? method = request[12]; + final bool isStatic = request[13]; + final List> dillData = request[14].cast>(); + final int blobLoadCount = request[15]; + final bool enableAsserts = request[16]; final List? experimentalFlags = - request[14] != null ? request[14].cast() : null; + request[17] != null ? request[17].cast() : null; IncrementalCompilerWrapper? compiler = isolateCompilers[isolateGroupId]; @@ -623,7 +626,10 @@ Future _processExpressionCompilationRequest(request) async { Procedure? procedure = await compiler.generator!.compileExpression( expression, definitions, + definitionTypes, typeDefinitions, + typeBounds, + typeDefaults, libraryUri, klass, method, diff --git a/pkg/vm/lib/incremental_compiler.dart b/pkg/vm/lib/incremental_compiler.dart index 08b00e705fc..3794fbfb344 100644 --- a/pkg/vm/lib/incremental_compiler.dart +++ b/pkg/vm/lib/incremental_compiler.dart @@ -194,23 +194,43 @@ class IncrementalCompiler { Future compileExpression( String expression, List definitions, + List definitionTypes, List typeDefinitions, + List typeBounds, + List typeDefaults, String libraryUri, String? klass, String? method, bool isStatic) { - Map completeDefinitions = {}; - for (int i = 0; i < definitions.length; i++) { - String name = definitions[i]; - if (isLegalIdentifier(name) || (i == 0 && isExtensionThisName(name))) { - completeDefinitions[name] = new DynamicType(); + ClassHierarchy? classHierarchy = + (_lastKnownGood ?? _combinePendingDeltas(false)).classHierarchy; + Map? completeDefinitions = createDefinitionsWithTypes( + classHierarchy?.knownLibraries, definitionTypes, definitions); + if (completeDefinitions == null) { + completeDefinitions = {}; + // No class hierarchy or wasn't provided correct types. + // Revert to old behaviour of setting everything to dynamic. + 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 = []; - for (String name in typeDefinitions) { - if (!isLegalIdentifier(name)) continue; - typeParameters.add(new TypeParameter(name, new DynamicType())); + List? typeParameters = createTypeParametersWithBounds( + classHierarchy?.knownLibraries, + typeBounds, + typeDefaults, + typeDefinitions); + if (typeParameters == null) { + // No class hierarchy or wasn't provided correct types. + // Revert to old behaviour of setting everything to dynamic. + typeParameters = []; + for (String name in typeDefinitions) { + if (!isLegalIdentifier(name)) continue; + typeParameters.add(new TypeParameter(name, new DynamicType())); + } } Uri library = Uri.parse(libraryUri); diff --git a/pkg/vm/test/incremental_compiler_test.dart b/pkg/vm/test/incremental_compiler_test.dart index 056d540b238..66cf72db455 100644 --- a/pkg/vm/test/incremental_compiler_test.dart +++ b/pkg/vm/test/incremental_compiler_test.dart @@ -115,14 +115,32 @@ main() { await compiler.compile(); compiler.accept(); { - Procedure? procedure = await compiler.compileExpression('main', - [], [], main.uri.toString(), null, 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, null, true); + Procedure? procedure = await compiler.compileExpression( + 'main1', + [], + [], + [], + [], + [], + main.uri.toString(), + null, + null, + true); expect(procedure, isNotNull); expect(errorsReported, equals(1)); errorsReported = 0; @@ -1038,8 +1056,17 @@ main() { } compiler.accept(); { - Procedure? procedure = await compiler.compileExpression('a', [], - [], 'package:foo/bar.dart', 'A', null, true); + Procedure? procedure = await compiler.compileExpression( + 'a', + [], + [], + [], + [], + [], + 'package:foo/bar.dart', + 'A', + null, + true); expect(procedure, isNotNull); } @@ -1055,8 +1082,17 @@ main() { } await compiler.reject(); { - Procedure? procedure = await compiler.compileExpression('a', [], - [], 'package:foo/bar.dart', 'A', null, true); + Procedure? procedure = await compiler.compileExpression( + 'a', + [], + [], + [], + [], + [], + 'package:foo/bar.dart', + 'A', + null, + true); expect(procedure, isNotNull); } }); @@ -1106,7 +1142,16 @@ main() { compiler.accept(); { final Procedure procedure = (await compiler.compileExpression( - 'a', [], [], barUri.toString(), 'A', null, 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(); @@ -1153,7 +1198,16 @@ main() { } { final Procedure procedure = (await compiler.compileExpression( - 'a', [], [], barUri.toString(), 'A', null, 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_fold_on_list_test.dart b/runtime/observatory/tests/service/evaluate_fold_on_list_test.dart new file mode 100644 index 00000000000..36df1f44c99 --- /dev/null +++ b/runtime/observatory/tests/service/evaluate_fold_on_list_test.dart @@ -0,0 +1,32 @@ +// Copyright (c) 2022, 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'; + +void testFunction() { + List x = ["a", "b", "c"]; + int xCombinedLength = x.fold( + 0, (previousValue, element) => previousValue + element.length); + debugger(); + print("xCombinedLength = $xCombinedLength"); +} + +var tests = [ + hasStoppedAtBreakpoint, + (Isolate isolate) async { + Instance result = await isolate.evalFrame(0, '''x.fold( + 0, (previousValue, element) => previousValue + element.length)''') + as Instance; + expect(result.valueAsString, equals('3')); + expect(result.kind, equals(InstanceKind.int)); + }, +]; + +main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction); diff --git a/runtime/observatory/tests/service/evaluate_type_arguments_test.dart b/runtime/observatory/tests/service/evaluate_type_arguments_test.dart new file mode 100644 index 00000000000..50861ece8c7 --- /dev/null +++ b/runtime/observatory/tests/service/evaluate_type_arguments_test.dart @@ -0,0 +1,116 @@ +// Copyright (c) 2022, 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:collection'; +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'; + +class A {} + +class B extends A {} + +class C extends Object with ListMixin implements List { + int length = 0; + C operator [](int index) => throw UnimplementedError(); + void operator []=(int index, C value) {} +} + +void testFunction4>() { + debugger(); + print("T4 = $T4"); +} + +void testFunction3() { + debugger(); + print("T3 = $T3"); + print("S3 = $S3"); +} + +void testFunction2(List x) { + debugger(); + print("x = $x"); +} + +void testFunction() { + testFunction2(["a", "b", "c"]); + testFunction3(); + testFunction4(); +} + +void fooxx(List y) { + List x = new List.from(["hello"]); +} + +var tests = [ + hasStoppedAtBreakpoint, + (Isolate isolate) async { + { + // Can add List to List directly. + Instance result = await isolate.evalFrame(0, '''() { + List y = new List.from(["hello"]); + x.addAll(y); + return x.last; + }()''') as Instance; + expect(result.valueAsString, equals("hello")); + expect(result.kind, equals(InstanceKind.string)); + } + { + // Can't add List to List directly. + DartError result = await isolate.evalFrame(0, '''() { + List y = []; + y.addAll(x); + return y.last; + }()''') as DartError; + expect( + result.message, + contains( + "The argument type '_GrowableList' can't be assigned " + "to the parameter type 'Iterable'")); + } + { + // Can add List to List via cast. + Instance result = await isolate.evalFrame(0, '''() { + List y = []; + y.addAll(x.cast()); + return y.toString(); + }()''') as Instance; + // Notice how "hello" was added a few evaluations back. + expect(result.valueAsString, equals("[a, b, c, hello]")); + expect(result.kind, equals(InstanceKind.string)); + } + { + // Can create List from List. + Instance result = await isolate.evalFrame(0, '''() { + List y = new List.from(x); + return y.toString(); + }()''') as Instance; + // Notice how "hello" was added a few evaluations back. + expect(result.valueAsString, equals("[a, b, c, hello]")); + expect(result.kind, equals(InstanceKind.string)); + } + }, + resumeIsolate, + (Isolate isolate) async { + // This is just to make sure the VM doesn't crash. + Instance result = + await isolate.evalFrame(0, '''S3.toString()''') as Instance; + expect(result.valueAsString, equals("B")); + expect(result.kind, equals(InstanceKind.string)); + }, + resumeIsolate, + (Isolate isolate) async { + // This is just to make sure the VM doesn't crash. + Instance result = + await isolate.evalFrame(0, '''T4.toString()''') as Instance; + expect(result.valueAsString, equals("C")); + expect(result.kind, equals(InstanceKind.string)); + }, +]; + +main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction); diff --git a/runtime/observatory/tests/service/evaluate_type_with_extension_test.dart b/runtime/observatory/tests/service/evaluate_type_with_extension_test.dart new file mode 100644 index 00000000000..b998aed9b21 --- /dev/null +++ b/runtime/observatory/tests/service/evaluate_type_with_extension_test.dart @@ -0,0 +1,38 @@ +// Copyright (c) 2022, 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 on String { + String printAndReturnHello() { + String response = "Hello from String '$this'"; + print(response); + return response; + } +} + +void testFunction() { + String x = "hello"; + String value = x.printAndReturnHello(); + debugger(); + print("value = $value"); +} + +var tests = [ + hasStoppedAtBreakpoint, + (Isolate isolate) async { + Instance result = + await isolate.evalFrame(0, 'x.printAndReturnHello()') as Instance; + expect(result.valueAsString, equals("Hello from String 'hello'")); + expect(result.kind, equals(InstanceKind.string)); + }, +]; + +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 ee4f627b244..95723e972b0 100644 --- a/runtime/observatory/tests/service/service_kernel.status +++ b/runtime/observatory/tests/service/service_kernel.status @@ -95,6 +95,7 @@ evaluate_activation_in_method_class_test: SkipByDesign # Debugger is disabled in evaluate_activation_test: SkipByDesign evaluate_async_closure_test: SkipByDesign evaluate_class_type_parameters_test: SkipByDesign # Debugger is disabled in AOT mode. +evaluate_fold_on_list_test: SkipByDesign # Debugger is disabled in AOT mode. 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. @@ -102,6 +103,8 @@ evaluate_in_extension_method_test: SkipByDesign # Debugger is disabled in AOT mo 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. +evaluate_type_arguments_test: SkipByDesign # Debugger is disabled in AOT mode. +evaluate_type_with_extension_test: SkipByDesign # Debugger is disabled in AOT mode. evaluate_with_escaping_closure_test: SkipByDesign # Debugger is disabled in AOT mode. evaluate_with_scope_test: SkipByDesign # Debugger is disabled in AOT mode. field_script_test: SkipByDesign # Debugger is disabled in AOT mode. diff --git a/runtime/observatory_2/tests/service_2/evaluate_fold_on_list_test.dart b/runtime/observatory_2/tests/service_2/evaluate_fold_on_list_test.dart new file mode 100644 index 00000000000..87c169845e5 --- /dev/null +++ b/runtime/observatory_2/tests/service_2/evaluate_fold_on_list_test.dart @@ -0,0 +1,32 @@ +// Copyright (c) 2022, 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'; + +void testFunction() { + List x = ["a", "b", "c"]; + int xCombinedLength = x.fold( + 0, (previousValue, element) => previousValue + element.length); + debugger(); + print("xCombinedLength = $xCombinedLength"); +} + +var tests = [ + hasStoppedAtBreakpoint, + (Isolate isolate) async { + Instance result = await isolate.evalFrame(0, '''x.fold( + 0, (previousValue, element) => previousValue + element.length)''') + as Instance; + expect(result.valueAsString, equals('3')); + expect(result.kind, equals(InstanceKind.int)); + }, +]; + +main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction); diff --git a/runtime/observatory_2/tests/service_2/evaluate_type_arguments_test.dart b/runtime/observatory_2/tests/service_2/evaluate_type_arguments_test.dart new file mode 100644 index 00000000000..694be967fd2 --- /dev/null +++ b/runtime/observatory_2/tests/service_2/evaluate_type_arguments_test.dart @@ -0,0 +1,116 @@ +// Copyright (c) 2022, 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:collection'; +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'; + +class A {} + +class B extends A {} + +class C extends Object with ListMixin implements List { + int length = 0; + C operator [](int index) => throw UnimplementedError(); + void operator []=(int index, C value) {} +} + +void testFunction4>() { + debugger(); + print("T4 = $T4"); +} + +void testFunction3() { + debugger(); + print("T3 = $T3"); + print("S3 = $S3"); +} + +void testFunction2(List x) { + debugger(); + print("x = $x"); +} + +void testFunction() { + testFunction2(["a", "b", "c"]); + testFunction3(); + testFunction4(); +} + +void fooxx(List y) { + List x = new List.from(["hello"]); +} + +var tests = [ + hasStoppedAtBreakpoint, + (Isolate isolate) async { + { + // Can add List to List directly. + Instance result = await isolate.evalFrame(0, '''() { + List y = new List.from(["hello"]); + x.addAll(y); + return x.last; + }()''') as Instance; + expect(result.valueAsString, equals("hello")); + expect(result.kind, equals(InstanceKind.string)); + } + { + // Can't add List to List directly. + DartError result = await isolate.evalFrame(0, '''() { + List y = []; + y.addAll(x); + return y.last; + }()''') as DartError; + expect( + result.message, + contains( + "The argument type '_GrowableList' can't be assigned " + "to the parameter type 'Iterable'")); + } + { + // Can add List to List via cast. + Instance result = await isolate.evalFrame(0, '''() { + List y = []; + y.addAll(x.cast()); + return y.toString(); + }()''') as Instance; + // Notice how "hello" was added a few evaluations back. + expect(result.valueAsString, equals("[a, b, c, hello]")); + expect(result.kind, equals(InstanceKind.string)); + } + { + // Can create List from List. + Instance result = await isolate.evalFrame(0, '''() { + List y = new List.from(x); + return y.toString(); + }()''') as Instance; + // Notice how "hello" was added a few evaluations back. + expect(result.valueAsString, equals("[a, b, c, hello]")); + expect(result.kind, equals(InstanceKind.string)); + } + }, + resumeIsolate, + (Isolate isolate) async { + // This is just to make sure the VM doesn't crash. + Instance result = + await isolate.evalFrame(0, '''S3.toString()''') as Instance; + expect(result.valueAsString, equals("B")); + expect(result.kind, equals(InstanceKind.string)); + }, + resumeIsolate, + (Isolate isolate) async { + // This is just to make sure the VM doesn't crash. + Instance result = + await isolate.evalFrame(0, '''T4.toString()''') as Instance; + expect(result.valueAsString, equals("C")); + expect(result.kind, equals(InstanceKind.string)); + }, +]; + +main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction); diff --git a/runtime/observatory_2/tests/service_2/evaluate_type_with_extension_test.dart b/runtime/observatory_2/tests/service_2/evaluate_type_with_extension_test.dart new file mode 100644 index 00000000000..333eb897d92 --- /dev/null +++ b/runtime/observatory_2/tests/service_2/evaluate_type_with_extension_test.dart @@ -0,0 +1,38 @@ +// Copyright (c) 2022, 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 on String { + String printAndReturnHello() { + String response = "Hello from String '$this'"; + print(response); + return response; + } +} + +void testFunction() { + String x = "hello"; + String value = x.printAndReturnHello(); + debugger(); + print("value = $value"); +} + +var tests = [ + hasStoppedAtBreakpoint, + (Isolate isolate) async { + Instance result = + await isolate.evalFrame(0, 'x.printAndReturnHello()') as Instance; + expect(result.valueAsString, equals("Hello from String 'hello'")); + expect(result.kind, equals(InstanceKind.string)); + }, +]; + +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 914b102fb42..b27568f7403 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_activation_in_method_class_test: SkipByDesign # Debugger is disabled in evaluate_activation_test: SkipByDesign evaluate_async_closure_test: SkipByDesign evaluate_class_type_parameters_test: SkipByDesign # Debugger is disabled in AOT mode. +evaluate_fold_on_list_test: SkipByDesign # Debugger is disabled in AOT mode. 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. @@ -102,6 +103,8 @@ evaluate_in_extension_method_test: SkipByDesign # Debugger is disabled in AOT mo 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. +evaluate_type_arguments_test: SkipByDesign # Debugger is disabled in AOT mode. +evaluate_type_with_extension_test: SkipByDesign # Debugger is disabled in AOT mode. evaluate_with_escaping_closure_test: SkipByDesign # Debugger is disabled in AOT mode. evaluate_with_scope_test: SkipByDesign # Debugger is disabled in AOT mode. field_script_test: SkipByDesign # Debugger is disabled in AOT mode. diff --git a/runtime/vm/compiler_test.cc b/runtime/vm/compiler_test.cc index 649bca40fe1..d2229cda3a5 100644 --- a/runtime/vm/compiler_test.cc +++ b/runtime/vm/compiler_test.cc @@ -208,6 +208,7 @@ TEST_CASE(EvalExpression) { KernelIsolate::CompileExpressionToKernel( /*platform_kernel=*/nullptr, /*platform_kernel_size=*/0, expr_text.ToCString(), Array::empty_array(), Array::empty_array(), + Array::empty_array(), Array::empty_array(), Array::empty_array(), String::Handle(lib_handle.url()).ToCString(), "A", /* method= */ nullptr, /* is_static= */ false); diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc index 7bbf92b6e4b..cb64707ed1a 100644 --- a/runtime/vm/debugger.cc +++ b/runtime/vm/debugger.cc @@ -1287,7 +1287,9 @@ ObjectPtr ActivationFrame::EvaluateCompiledExpression( TypeArgumentsPtr ActivationFrame::BuildParameters( const GrowableObjectArray& param_names, const GrowableObjectArray& param_values, - const GrowableObjectArray& type_params_names) { + const GrowableObjectArray& type_params_names, + const GrowableObjectArray& type_params_bounds, + const GrowableObjectArray& type_params_defaults) { GetDescIndices(); bool type_arguments_available = false; String& name = String::Handle(); @@ -1329,6 +1331,12 @@ TypeArgumentsPtr ActivationFrame::BuildParameters( intptr_t num_vars = function().NumTypeArguments(); type_params_names.Grow(num_vars); type_params_names.SetLength(num_vars); + type_params_bounds.Grow(num_vars); + type_params_bounds.SetLength(num_vars); + type_params_defaults.Grow(num_vars); + type_params_defaults.SetLength(num_vars); + AbstractType& bound = AbstractType::Handle(); + AbstractType& defaultType = AbstractType::Handle(); TypeParameters& type_params = TypeParameters::Handle(); Function& current = Function::Handle(function().ptr()); intptr_t mapping_offset = num_vars; @@ -1341,12 +1349,16 @@ TypeArgumentsPtr ActivationFrame::BuildParameters( mapping_offset -= size; for (intptr_t j = 0; j < size; ++j) { name = type_params.NameAt(j); + bound = type_params.BoundAt(j); + defaultType = type_params.DefaultAt(j); // Write the names in backwards in terms of chain of functions. // But keep the order of names within the same function. so they // match up with the order of the types in 'type_arguments'. // Index:0 1 2 3 ... // |Names in Grandparent| |Names in Parent| ..|Names in Child| type_params_names.SetAt(mapping_offset + j, name); + type_params_bounds.SetAt(mapping_offset + j, bound); + type_params_defaults.SetAt(mapping_offset + j, defaultType); } } if (!type_arguments.IsNull()) { diff --git a/runtime/vm/debugger.h b/runtime/vm/debugger.h index 231bc3fede6..0c95bd3f89e 100644 --- a/runtime/vm/debugger.h +++ b/runtime/vm/debugger.h @@ -392,7 +392,9 @@ class ActivationFrame : public ZoneAllocated { TypeArgumentsPtr BuildParameters( const GrowableObjectArray& param_names, const GrowableObjectArray& param_values, - const GrowableObjectArray& type_params_names); + const GrowableObjectArray& type_params_names, + const GrowableObjectArray& type_params_bounds, + const GrowableObjectArray& type_params_defaults); ObjectPtr EvaluateCompiledExpression(const ExternalTypedData& kernel_data, const Array& arguments, diff --git a/runtime/vm/debugger_api_impl_test.cc b/runtime/vm/debugger_api_impl_test.cc index f7b5cfde113..3f04831901b 100644 --- a/runtime/vm/debugger_api_impl_test.cc +++ b/runtime/vm/debugger_api_impl_test.cc @@ -181,7 +181,10 @@ DART_EXPORT Dart_Handle Dart_EvaluateStaticExpr(Dart_Handle lib_handle, /* platform_kernel= */ nullptr, /* platform_kernel_size= */ 0, expr.ToCString(), /* definitions= */ Array::empty_array(), + /* definition_types= */ Array::empty_array(), /* type_defintions= */ Array::empty_array(), + /* type_bounds= */ Array::empty_array(), + /* type_defaults= */ Array::empty_array(), String::Handle(lib.url()).ToCString(), /* klass= */ nullptr, /* method= */ nullptr, diff --git a/runtime/vm/kernel_isolate.cc b/runtime/vm/kernel_isolate.cc index 7e098bf1c4e..c6f32226cf0 100644 --- a/runtime/vm/kernel_isolate.cc +++ b/runtime/vm/kernel_isolate.cc @@ -476,7 +476,10 @@ class KernelCompilationRequest : public ValueObject { intptr_t platform_kernel_size, const char* expression, const Array& definitions, + const Array& definition_types, const Array& type_definitions, + const Array& type_bounds, + const Array& type_defaults, char const* library_uri, char const* klass, char const* method, @@ -537,6 +540,22 @@ class KernelCompilationRequest : public ValueObject { } definitions_object.value.as_array.values = definitions_array; + Dart_CObject definition_types_object; + intptr_t num_definition_types = definition_types.Length(); + definition_types_object.type = Dart_CObject_kArray; + definition_types_object.value.as_array.length = num_definition_types; + + Dart_CObject** definition_types_array = + new Dart_CObject*[num_definition_types]; + for (intptr_t i = 0; i < num_definition_types; ++i) { + definition_types_array[i] = new Dart_CObject; + definition_types_array[i]->type = Dart_CObject_kString; + definition_types_array[i]->value.as_string = const_cast( + String::CheckedHandle(thread->zone(), definition_types.At(i)) + .ToCString()); + } + definition_types_object.value.as_array.values = definition_types_array; + Dart_CObject type_definitions_object; intptr_t num_type_definitions = type_definitions.Length(); type_definitions_object.type = Dart_CObject_kArray; @@ -553,6 +572,35 @@ class KernelCompilationRequest : public ValueObject { } type_definitions_object.value.as_array.values = type_definitions_array; + Dart_CObject type_bounds_object; + intptr_t num_type_bounds = type_bounds.Length(); + type_bounds_object.type = Dart_CObject_kArray; + type_bounds_object.value.as_array.length = num_type_bounds; + + Dart_CObject** type_bounds_array = new Dart_CObject*[num_type_bounds]; + for (intptr_t i = 0; i < num_type_bounds; ++i) { + type_bounds_array[i] = new Dart_CObject; + type_bounds_array[i]->type = Dart_CObject_kString; + type_bounds_array[i]->value.as_string = const_cast( + String::CheckedHandle(thread->zone(), type_bounds.At(i)).ToCString()); + } + type_bounds_object.value.as_array.values = type_bounds_array; + + Dart_CObject type_defaults_object; + intptr_t num_type_defaults = type_defaults.Length(); + type_defaults_object.type = Dart_CObject_kArray; + type_defaults_object.value.as_array.length = num_type_defaults; + + Dart_CObject** type_defaults_array = new Dart_CObject*[num_type_defaults]; + for (intptr_t i = 0; i < num_type_defaults; ++i) { + type_defaults_array[i] = new Dart_CObject; + type_defaults_array[i]->type = Dart_CObject_kString; + type_defaults_array[i]->value.as_string = const_cast( + String::CheckedHandle(thread->zone(), type_defaults.At(i)) + .ToCString()); + } + type_defaults_object.value.as_array.values = type_defaults_array; + Dart_CObject library_uri_object; library_uri_object.type = Dart_CObject_kString; library_uri_object.value.as_string = const_cast(library_uri); @@ -659,7 +707,10 @@ class KernelCompilationRequest : public ValueObject { &dart_platform_kernel, &expression_object, &definitions_object, + &definition_types_object, &type_definitions_object, + &type_bounds_object, + &type_defaults_object, &library_uri_object, &class_object, &method_object, @@ -690,11 +741,26 @@ class KernelCompilationRequest : public ValueObject { } delete[] definitions_array; + for (intptr_t i = 0; i < num_definition_types; ++i) { + delete definition_types_array[i]; + } + delete[] definition_types_array; + for (intptr_t i = 0; i < num_type_definitions; ++i) { delete type_definitions_array[i]; } delete[] type_definitions_array; + for (intptr_t i = 0; i < num_type_bounds; ++i) { + delete type_bounds_array[i]; + } + delete[] type_bounds_array; + + for (intptr_t i = 0; i < num_type_defaults; ++i) { + delete type_defaults_array[i]; + } + delete[] type_defaults_array; + for (intptr_t i = 0; i < num_dills; ++i) { delete dills_array[i]; } @@ -1142,7 +1208,10 @@ Dart_KernelCompilationResult KernelIsolate::CompileExpressionToKernel( intptr_t platform_kernel_size, const char* expression, const Array& definitions, + const Array& definition_types, const Array& type_definitions, + const Array& type_bounds, + const Array& type_defaults, const char* library_url, const char* klass, const char* method, @@ -1160,7 +1229,8 @@ 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, method, is_static, + definitions, definition_types, type_definitions, type_bounds, + type_defaults, library_url, klass, method, is_static, experimental_flags_); } diff --git a/runtime/vm/kernel_isolate.h b/runtime/vm/kernel_isolate.h index d5d529753dd..08a7427f748 100644 --- a/runtime/vm/kernel_isolate.h +++ b/runtime/vm/kernel_isolate.h @@ -73,7 +73,10 @@ class KernelIsolate : public AllStatic { intptr_t platform_kernel_size, const char* expression, const Array& definitions, + const Array& definition_types, const Array& type_definitions, + const Array& type_bounds, + const Array& type_defaults, const char* library_url, const char* klass, const char* method, diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc index 553097bdde6..336cd88cf8c 100644 --- a/runtime/vm/service.cc +++ b/runtime/vm/service.cc @@ -2857,6 +2857,51 @@ static const MethodParameter* const build_expression_evaluation_scope_params[] = NULL, }; +static void CollectStringifiedType(Zone* zone, + const AbstractType& type, + const GrowableObjectArray& output) { + Instance& instance = Instance::Handle(zone); + if (type.IsFunctionType()) { + // The closure class + // (IsolateGroup::Current()->object_store()->closure_class()) + // is statically typed weird (the call method redirects to itself) + // and the type is therefore not useful for the CFE. We use null instead. + output.Add(instance); + return; + } + if (type.IsDynamicType()) { + // Dynamic is weird in that it seems to have a class with no name and a + // library called something like '7189777121420'. We use null instead. + output.Add(instance); + return; + } + if (type.IsTypeParameter() && type.IsAbstractType()) { + // Calling type_class on an abstract type parameter will crash the VM. + // We use null instead. + output.Add(instance); + return; + } + const Class& cls = Class::Handle(type.type_class()); + const Library& lib = Library::Handle(zone, cls.library()); + + instance ^= lib.url(); + output.Add(instance); + + instance ^= cls.ScrubbedName(); + output.Add(instance); + + instance ^= Smi::New((intptr_t)type.nullability()); + output.Add(instance); + + const TypeArguments& srcArguments = TypeArguments::Handle(type.arguments()); + instance ^= Smi::New(srcArguments.Length()); + output.Add(instance); + for (int i = 0; i < srcArguments.Length(); i++) { + const AbstractType& src_type = AbstractType::Handle(srcArguments.TypeAt(i)); + CollectStringifiedType(zone, src_type, output); + } +} + static void BuildExpressionEvaluationScope(Thread* thread, JSONStream* js) { if (CheckDebuggerDisabled(thread, js)) { return; @@ -2877,6 +2922,10 @@ static void BuildExpressionEvaluationScope(Thread* thread, JSONStream* js) { GrowableObjectArray::Handle(zone, GrowableObjectArray::New()); const GrowableObjectArray& type_params_names = GrowableObjectArray::Handle(zone, GrowableObjectArray::New()); + const GrowableObjectArray& type_params_bounds = + GrowableObjectArray::Handle(zone, GrowableObjectArray::New()); + const GrowableObjectArray& type_params_defaults = + GrowableObjectArray::Handle(zone, GrowableObjectArray::New()); String& klass_name = String::Handle(zone); String& method_name = String::Handle(zone); String& library_uri = String::Handle(zone); @@ -2896,7 +2945,8 @@ static void BuildExpressionEvaluationScope(Thread* thread, JSONStream* js) { } ActivationFrame* frame = stack->FrameAt(framePos); - frame->BuildParameters(param_names, param_values, type_params_names); + frame->BuildParameters(param_names, param_values, type_params_names, + type_params_bounds, type_params_defaults); if (frame->function().is_static()) { const Class& cls = Class::Handle(zone, frame->function().Owner()); @@ -2976,6 +3026,28 @@ static void BuildExpressionEvaluationScope(Thread* thread, JSONStream* js) { jsonParamNames.AddValue(param_name.ToCString()); } } + { + const JSONArray jsonParamTypes(&report, "param_types"); + Object& obj = Object::Handle(); + Instance& instance = Instance::Handle(); + const GrowableObjectArray& param_types = + GrowableObjectArray::Handle(zone, GrowableObjectArray::New()); + AbstractType& type = AbstractType::Handle(); + for (intptr_t i = 0; i < param_names.Length(); i++) { + obj = param_values.At(i); + if (obj.IsNull()) { + param_types.Add(obj); + } else if (obj.IsInstance()) { + instance ^= param_values.At(i); + type = instance.GetType(Heap::kNew); + CollectStringifiedType(zone, type, param_types); + } + } + for (intptr_t i = 0; i < param_types.Length(); i++) { + instance ^= param_types.At(i); + jsonParamTypes.AddValue(instance.ToCString()); + } + } { JSONArray jsonTypeParamsNames(&report, "type_params_names"); @@ -2985,6 +3057,36 @@ static void BuildExpressionEvaluationScope(Thread* thread, JSONStream* js) { jsonTypeParamsNames.AddValue(type_param_name.ToCString()); } } + { + const JSONArray jsonParamTypes(&report, "type_params_bounds"); + const GrowableObjectArray& type_params_bounds_strings = + GrowableObjectArray::Handle(zone, GrowableObjectArray::New()); + AbstractType& type = AbstractType::Handle(); + for (intptr_t i = 0; i < type_params_bounds.Length(); i++) { + type ^= type_params_bounds.At(i); + CollectStringifiedType(zone, type, type_params_bounds_strings); + } + Instance& instance = Instance::Handle(); + for (intptr_t i = 0; i < type_params_bounds_strings.Length(); i++) { + instance ^= type_params_bounds_strings.At(i); + jsonParamTypes.AddValue(instance.ToCString()); + } + } + { + const JSONArray jsonParamTypes(&report, "type_params_defaults"); + const GrowableObjectArray& type_params_defaults_strings = + GrowableObjectArray::Handle(zone, GrowableObjectArray::New()); + AbstractType& type = AbstractType::Handle(); + for (intptr_t i = 0; i < type_params_defaults.Length(); i++) { + type ^= type_params_defaults.At(i); + CollectStringifiedType(zone, type, type_params_defaults_strings); + } + Instance& instance = Instance::Handle(); + for (intptr_t i = 0; i < type_params_defaults_strings.Length(); i++) { + instance ^= type_params_defaults_strings.At(i); + jsonParamTypes.AddValue(instance.ToCString()); + } + } report.AddProperty("libraryUri", library_uri.ToCString()); if (!klass_name.IsNull()) { report.AddProperty("klass", klass_name.ToCString()); @@ -3036,7 +3138,10 @@ static const MethodParameter* const compile_expression_params[] = { RUNNABLE_ISOLATE_PARAMETER, new StringParameter("expression", true), new StringParameter("definitions", false), + new StringParameter("definitionTypes", false), new StringParameter("typeDefinitions", false), + new StringParameter("typeBounds", false), + new StringParameter("typeDefaults", false), new StringParameter("libraryUri", true), new StringParameter("klass", false), new BoolParameter("isStatic", false), @@ -3070,6 +3175,12 @@ static void CompileExpression(Thread* thread, JSONStream* js) { PrintInvalidParamError(js, "definitions"); return; } + const GrowableObjectArray& param_types = + GrowableObjectArray::Handle(thread->zone(), GrowableObjectArray::New()); + if (!ParseCSVList(js->LookupParam("definitionTypes"), param_types)) { + PrintInvalidParamError(js, "definitionTypes"); + return; + } const GrowableObjectArray& type_params = GrowableObjectArray::Handle(thread->zone(), GrowableObjectArray::New()); @@ -3077,6 +3188,18 @@ static void CompileExpression(Thread* thread, JSONStream* js) { PrintInvalidParamError(js, "typedDefinitions"); return; } + const GrowableObjectArray& type_bounds = + GrowableObjectArray::Handle(thread->zone(), GrowableObjectArray::New()); + if (!ParseCSVList(js->LookupParam("typeBounds"), type_bounds)) { + PrintInvalidParamError(js, "typeBounds"); + return; + } + const GrowableObjectArray& type_defaults = + GrowableObjectArray::Handle(thread->zone(), GrowableObjectArray::New()); + if (!ParseCSVList(js->LookupParam("typeDefaults"), type_defaults)) { + PrintInvalidParamError(js, "typeDefaults"); + return; + } const uint8_t* kernel_buffer = Service::dart_library_kernel(); const intptr_t kernel_buffer_len = Service::dart_library_kernel_length(); @@ -3085,7 +3208,10 @@ static void CompileExpression(Thread* thread, JSONStream* js) { KernelIsolate::CompileExpressionToKernel( kernel_buffer, kernel_buffer_len, js->LookupParam("expression"), Array::Handle(Array::MakeFixedLength(params)), + Array::Handle(Array::MakeFixedLength(param_types)), Array::Handle(Array::MakeFixedLength(type_params)), + Array::Handle(Array::MakeFixedLength(type_bounds)), + Array::Handle(Array::MakeFixedLength(type_defaults)), js->LookupParam("libraryUri"), js->LookupParam("klass"), js->LookupParam("method"), is_static); @@ -3145,6 +3271,10 @@ static void EvaluateCompiledExpression(Thread* thread, JSONStream* js) { } const GrowableObjectArray& type_params_names = GrowableObjectArray::Handle(zone, GrowableObjectArray::New()); + const GrowableObjectArray& type_params_bounds = + GrowableObjectArray::Handle(zone, GrowableObjectArray::New()); + const GrowableObjectArray& type_params_defaults = + GrowableObjectArray::Handle(zone, GrowableObjectArray::New()); const ExternalTypedData& kernel_data = ExternalTypedData::Handle( zone, DecodeKernelBuffer(js->LookupParam("kernelBytes"))); @@ -3160,7 +3290,8 @@ static void EvaluateCompiledExpression(Thread* thread, JSONStream* js) { ActivationFrame* frame = stack->FrameAt(frame_pos); TypeArguments& type_arguments = TypeArguments::Handle( zone, - frame->BuildParameters(param_names, param_values, type_params_names)); + frame->BuildParameters(param_names, param_values, type_params_names, + type_params_bounds, type_params_defaults)); const Object& result = Object::Handle( zone, diff --git a/runtime/vm/unit_test.cc b/runtime/vm/unit_test.cc index 524259249c7..31187ebc469 100644 --- a/runtime/vm/unit_test.cc +++ b/runtime/vm/unit_test.cc @@ -648,7 +648,9 @@ 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, + Array::empty_array(), Array::empty_array(), Array::empty_array(), + String::Handle(lib.url()).ToCString(), + /* klass= */ nullptr, /* method= */ nullptr, /* is_static= */ true); if (compilation_result.status != Dart_KernelCompilationStatus_Ok) { diff --git a/sdk/lib/vmservice/running_isolates.dart b/sdk/lib/vmservice/running_isolates.dart index 17ba44877b1..e699cb31cd6 100644 --- a/sdk/lib/vmservice/running_isolates.dart +++ b/sdk/lib/vmservice/running_isolates.dart @@ -124,7 +124,10 @@ class _Evaluator { 'isolateId': _message.params['isolateId']!, 'expression': _message.params['expression']!, 'definitions': buildScopeResponseResult['param_names']!, + 'definitionTypes': buildScopeResponseResult['param_types']!, 'typeDefinitions': buildScopeResponseResult['type_params_names']!, + 'typeBounds': buildScopeResponseResult['type_params_bounds']!, + 'typeDefaults': buildScopeResponseResult['type_params_defaults']!, 'libraryUri': buildScopeResponseResult['libraryUri']!, 'isStatic': buildScopeResponseResult['isStatic']!, };