| // Copyright (c) 2023, 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:dev_compiler/src/compiler/module_builder.dart' | |
| show ModuleFormat; | |
| import 'package:test/test.dart'; | |
| import '../shared_test_options.dart'; | |
| import 'expression_compiler_e2e_suite.dart'; | |
| void main(List<String> args) async { | |
| await runAllTests(args); | |
| } | |
| Future<void> runAllTests(List<String> args) async { | |
| var driver = await ExpressionEvaluationTestDriver.init(); | |
| tearDownAll(() async { | |
| await driver.finish(); | |
| }); | |
| group('(AMD module system)', () { | |
| var setup = SetupCompilerOptions( | |
| moduleFormat: ModuleFormat.amd, | |
| args: args, | |
| enableExperiments: [], | |
| ); | |
| runSharedTests(setup, driver); | |
| }); | |
| group('(DDC module system)', () { | |
| final setup = SetupCompilerOptions( | |
| moduleFormat: ModuleFormat.ddc, | |
| args: args, | |
| enableExperiments: [], | |
| ); | |
| runSharedTests(setup, driver); | |
| }); | |
| } | |
| const simpleClassSource = ''' | |
| import 'dart:js_interop'; | |
| @JS() | |
| external void eval(String code); | |
| @JS() | |
| external JSObject? catchError(JSFunction f); | |
| int get globalField { print('globalField access!'); return 0; } | |
| late final int globalLateFinalField; | |
| class BaseClass<T extends num> { | |
| static const int staticConstField = 0; | |
| static int staticField = 1; | |
| static int _staticField = 2; | |
| static int _unusedStaticField = 3; | |
| int field; | |
| T genericField; | |
| int _field; | |
| int _unusedField = 4; | |
| late final int lateFinalField; | |
| int get getter => 6; | |
| int get _privateGetter => 7; | |
| void Function() functionField = staticMethod; | |
| BaseClass? nullableField; | |
| AnotherClass nonNullableField = AnotherClass(); | |
| Ext get extensionTypeGetter => Ext(AnotherClass()); | |
| Ext get _privateExtensionTypeGetter => Ext(AnotherClass()); | |
| ExtString extensionTypeField = ExtString('hello'); | |
| ExtString _privateExtensionTypeField = ExtString('hello'); | |
| static const ExtDuration staticConstExtensionTypeField = | |
| const ExtDuration(Duration.zero); | |
| static ExtDuration staticExtensionTypeField = ExtDuration(Duration.zero); | |
| static final ExtDuration staticFinalExtensionTypeField = | |
| ExtDuration(Duration.zero); | |
| BaseClass(this.field, this._field) : genericField = 0 as T { | |
| int y = 1; | |
| lateFinalField = 35; | |
| } | |
| BaseClass.named(this.field): _field = 42, genericField = 0 as T; | |
| BaseClass.redirecting(int x) : this(x, 99); | |
| factory BaseClass.factory() => BaseClass(42, 0); | |
| void _privateMethod() {} | |
| void method() {} | |
| static void staticMethod() {} | |
| } | |
| class DerivedClass extends BaseClass { | |
| static const int _newStaticConstPrivateField = -3; | |
| final int _newPrivateField = 0; | |
| final int newPublicField = 1; | |
| DerivedClass(int field, int field2): super(field, field2); | |
| int stringLength(String input) => input.length; | |
| } | |
| DerivedClass globalFunction(int a, int b) { | |
| return DerivedClass(a, b); | |
| } | |
| class AnotherClass { | |
| int a = 0; | |
| } | |
| extension type Ext(AnotherClass _) {} | |
| extension type ExtString(String _) {} | |
| extension type const ExtDuration(Duration _) {} | |
| main() { | |
| eval(\'\'\' | |
| globalThis.catchError = function (f) { | |
| try { | |
| f(); | |
| } catch (e) { | |
| return e; | |
| } | |
| return null; | |
| }; | |
| \'\'\'); | |
| int x = 15; | |
| var derived = DerivedClass(1, 3); | |
| var base = BaseClass<int>(5, 6); | |
| var set = <String>{ 'a', 'b', 'c' }; | |
| var list = <int>[1, 2, 3]; | |
| var map = <String, int>{'a': 1, 'b': 2}; | |
| var stream = Stream.fromIterable([1, 2, 3]); | |
| var record = (0, 2, name: 'cat'); | |
| var object = Object(); | |
| var globalMethod = globalFunction; | |
| var staticMethod = BaseClass.staticMethod; | |
| var xType = x.runtimeType; | |
| var baseType = base.runtimeType; | |
| var baseTypeType = baseType.runtimeType; | |
| var setType = set.runtimeType; | |
| var listType = list.runtimeType; | |
| var mapType = map.runtimeType; | |
| var recordType = record.runtimeType; | |
| // We want to test the type representation of the thrown error. However, we | |
| // can't do this in Dart as the `catch` block unwraps the error. Instead, use | |
| // interop to catch the JS wrapped error and return that directly. | |
| var error = catchError((() { | |
| throw 'Throwing Dart error that should be wrapped with DartError.'; | |
| } as void Function()).toJS); | |
| // Breakpoint: BP | |
| print('foo'); | |
| } | |
| '''; | |
| void runSharedTests( | |
| SetupCompilerOptions setup, | |
| ExpressionEvaluationTestDriver driver, | |
| ) { | |
| group( | |
| 'Runtime debugging API after loading sources but before running main |', | |
| () { | |
| var source = simpleClassSource; | |
| // Set up and tear down after every test so that the bootstrapper script is | |
| // re-run every time, therefore triggering the `OnScheduleMain` breakpoint. | |
| setUp(() async { | |
| await driver.initSource(setup, source); | |
| }); | |
| tearDown(() async { | |
| await driver.cleanupTest(); | |
| }); | |
| test('getClassesInLibrary', () async { | |
| var getClasses = setup.emitLibraryBundle | |
| ? 'getClassesInLibrary' | |
| : 'getLibraryMetadata'; | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'OnScheduleMain', | |
| // Query a user definition to test if it's initialized. | |
| expression: 'dart.$getClasses("package:eval_test/test.dart")', | |
| expectedResult: ['BaseClass', 'DerivedClass', 'AnotherClass'], | |
| ); | |
| }); | |
| test('getClassMetadata in non-SDK library', () async { | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'OnScheduleMain', | |
| // Query a user definition to test if it's initialized. | |
| expression: 'dart.getClassMetadata("package:eval_test/test.dart", "DerivedClass")', | |
| expectedResult: { | |
| 'className': 'DerivedClass', | |
| 'superClassName': 'BaseClass', | |
| 'superClassLibraryId': 'package:eval_test/test.dart', | |
| 'fields': { | |
| 'newPublicField': { | |
| 'isFinal': true, | |
| 'className': 'int', | |
| 'classLibraryId': 'dart:core', | |
| }, | |
| '_newPrivateField': { | |
| 'isFinal': true, | |
| 'className': 'int', | |
| 'classLibraryId': 'dart:core', | |
| }, | |
| '_newStaticConstPrivateField': {'isStatic': true}, | |
| }, | |
| 'methods': { | |
| 'stringLength': {}, | |
| 'lateFinalField': {'isGetter': true}, | |
| 'getter': {'isGetter': true}, | |
| '_privateGetter': {'isGetter': true}, | |
| 'factory': {'isStatic': true}, | |
| 'staticMethod': {'isStatic': true}, | |
| 'extensionTypeGetter': {'isGetter': true}, | |
| '_privateExtensionTypeGetter': {'isGetter': true}, | |
| }, | |
| }, | |
| ); | |
| }); | |
| }, | |
| // DDC module format doesn't guarantee that libraries are initialized when | |
| // using debugging APIs before main is started. | |
| skip: setup.moduleFormat == ModuleFormat.ddc && !setup.canaryFeatures, | |
| ); | |
| group('Runtime debugging API |', () { | |
| var source = simpleClassSource; | |
| setUpAll(() async { | |
| await driver.initSource(setup, source); | |
| }); | |
| tearDownAll(() async { | |
| await driver.cleanupTest(); | |
| }); | |
| test('getClassesInLibrary', () async { | |
| var getClasses = setup.emitLibraryBundle | |
| ? 'getClassesInLibrary' | |
| : 'getLibraryMetadata'; | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.$getClasses("package:eval_test/test.dart")', | |
| expectedResult: ['BaseClass', 'DerivedClass', 'AnotherClass'], | |
| ); | |
| }); | |
| test('getClassMetadata (object)', () async { | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getClassMetadata("dart:core", "Object")', | |
| expectedResult: { | |
| 'className': 'Object', | |
| 'fields': {}, | |
| 'methods': { | |
| '_equals': {}, | |
| 'toString': {}, | |
| 'noSuchMethod': {}, | |
| 'hashCode': {'isGetter': true}, | |
| 'runtimeType': {'isGetter': true}, | |
| 'hash': {'isStatic': true}, | |
| 'hashAll': {'isStatic': true}, | |
| 'hashAllUnordered': {'isStatic': true}, | |
| }, | |
| }, | |
| ); | |
| }); | |
| test('getClassMetadata (base class)', () async { | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getClassMetadata("package:eval_test/test.dart", "BaseClass", base)', | |
| expectedResult: { | |
| 'className': 'BaseClass', | |
| 'superClassName': 'Object', | |
| 'superClassLibraryId': 'dart:core', | |
| 'fields': { | |
| 'field': {'className': 'int', 'classLibraryId': 'dart:core'}, | |
| 'functionField': {'className': '() => void'}, | |
| 'nullableField': { | |
| 'className': 'BaseClass<num>?', | |
| 'classLibraryId': 'package:eval_test/test.dart', | |
| }, | |
| 'nonNullableField': { | |
| 'className': 'AnotherClass', | |
| 'classLibraryId': 'package:eval_test/test.dart', | |
| }, | |
| '_field': {'className': 'int', 'classLibraryId': 'dart:core'}, | |
| 'genericField': {'className': 'int'}, | |
| '_unusedField': {'className': 'int', 'classLibraryId': 'dart:core'}, | |
| 'lateFinalField': { | |
| 'className': 'int?', | |
| 'classLibraryId': 'dart:core', | |
| }, | |
| 'staticConstField': {'isStatic': true}, | |
| 'staticField': {'isStatic': true}, | |
| '_staticField': {'isStatic': true}, | |
| '_unusedStaticField': {'isStatic': true}, | |
| // NOTE: Fields typed as an extension type appear as their static | |
| // erased type for now. This isn't necessarily the runtime type | |
| // of the value either. | |
| 'extensionTypeField': { | |
| 'className': 'String', | |
| 'classLibraryId': 'dart:core', | |
| }, | |
| '_privateExtensionTypeField': { | |
| 'className': 'String', | |
| 'classLibraryId': 'dart:core', | |
| }, | |
| 'staticConstExtensionTypeField': {'isStatic': true}, | |
| 'staticExtensionTypeField': {'isStatic': true}, | |
| 'staticFinalExtensionTypeField': {'isStatic': true}, | |
| }, | |
| 'methods': { | |
| 'method': {}, | |
| '_privateMethod': {}, | |
| 'lateFinalField': {'isGetter': true}, | |
| 'getter': {'isGetter': true}, | |
| '_privateGetter': {'isGetter': true}, | |
| 'factory': {'isStatic': true}, | |
| 'staticMethod': {'isStatic': true}, | |
| 'extensionTypeGetter': {'isGetter': true}, | |
| '_privateExtensionTypeGetter': {'isGetter': true}, | |
| }, | |
| }, | |
| ); | |
| }); | |
| test('getClassMetadata (derived class)', () async { | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getClassMetadata("package:eval_test/test.dart", "DerivedClass")', | |
| expectedResult: { | |
| 'className': 'DerivedClass', | |
| 'superClassName': 'BaseClass', | |
| 'superClassLibraryId': 'package:eval_test/test.dart', | |
| 'fields': { | |
| 'newPublicField': { | |
| 'isFinal': true, | |
| 'className': 'int', | |
| 'classLibraryId': 'dart:core', | |
| }, | |
| '_newPrivateField': { | |
| 'isFinal': true, | |
| 'className': 'int', | |
| 'classLibraryId': 'dart:core', | |
| }, | |
| '_newStaticConstPrivateField': {'isStatic': true}, | |
| }, | |
| 'methods': { | |
| 'stringLength': {}, | |
| 'lateFinalField': {'isGetter': true}, | |
| 'getter': {'isGetter': true}, | |
| '_privateGetter': {'isGetter': true}, | |
| 'factory': {'isStatic': true}, | |
| 'staticMethod': {'isStatic': true}, | |
| 'extensionTypeGetter': {'isGetter': true}, | |
| '_privateExtensionTypeGetter': {'isGetter': true}, | |
| }, | |
| }, | |
| ); | |
| }); | |
| test('getClassMetadata (Record)', () async { | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getClassMetadata("dart:core", "Record")', | |
| expectedResult: { | |
| 'className': 'Record', | |
| 'superClassName': 'Object', | |
| 'superClassLibraryId': 'dart:core', | |
| 'fields': {}, | |
| 'methods': { | |
| '_equals': {}, | |
| 'toString': {}, | |
| 'noSuchMethod': {}, | |
| 'hashCode': {'isGetter': true}, | |
| 'runtimeType': {'isGetter': true}, | |
| 'hash': {'isStatic': true}, | |
| 'hashAll': {'isStatic': true}, | |
| 'hashAllUnordered': {'isStatic': true}, | |
| }, | |
| }, | |
| ); | |
| }); | |
| test('getObjectMetadata (int)', () async { | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getObjectMetadata(x)', | |
| expectedResult: {}, | |
| ); | |
| }); | |
| test('getObjectMetadata (object)', () async { | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getObjectMetadata(object)', | |
| expectedResult: { | |
| 'className': 'Object', | |
| 'libraryId': 'dart:core', | |
| 'runtimeKind': 'object', | |
| 'length': 0, | |
| }, | |
| ); | |
| }); | |
| test('getObjectMetadata (object of derived class)', () async { | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getObjectMetadata(base)', | |
| expectedResult: { | |
| 'className': 'BaseClass<int>', | |
| 'libraryId': 'package:eval_test/test.dart', | |
| 'runtimeKind': 'object', | |
| 'length': 10, | |
| }, | |
| ); | |
| }); | |
| test('getObjectMetadata (Set)', () async { | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getObjectMetadata(set)', | |
| expectedResult: { | |
| 'className': 'LinkedSet<String>', | |
| 'libraryId': 'dart:_js_helper', | |
| 'runtimeKind': 'set', | |
| 'length': 3, | |
| }, | |
| ); | |
| }); | |
| test('getObjectMetadata (List)', () async { | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getObjectMetadata(list)', | |
| expectedResult: { | |
| 'className': 'JSArray<int>', | |
| 'libraryId': 'dart:_interceptors', | |
| 'runtimeKind': 'list', | |
| 'length': 3, | |
| }, | |
| ); | |
| }); | |
| test('getObjectMetadata (Map)', () async { | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getObjectMetadata(map)', | |
| expectedResult: { | |
| 'className': 'IdentityMap<String, int>', | |
| 'libraryId': 'dart:_js_helper', | |
| 'runtimeKind': 'map', | |
| 'length': 2, | |
| }, | |
| ); | |
| }); | |
| test('getObjectMetadata (Stream)', () async { | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getObjectMetadata(stream)', | |
| expectedResult: { | |
| 'className': '_MultiStream<int>', | |
| 'libraryId': 'dart:async', | |
| 'runtimeKind': 'object', | |
| 'length': 2, | |
| }, | |
| ); | |
| }); | |
| test('getObjectMetadata (Record)', () async { | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getObjectMetadata(record)', | |
| expectedResult: { | |
| 'className': 'Record', | |
| 'libraryId': 'dart:core', | |
| 'runtimeKind': 'record', | |
| 'length': 3, | |
| }, | |
| ); | |
| }); | |
| test('getObjectMetadata (LegacyJavaScriptObject)', () async { | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getObjectMetadata({})', | |
| expectedResult: { | |
| 'className': 'LegacyJavaScriptObject', | |
| 'libraryId': 'dart:_interceptors', | |
| 'runtimeKind': 'nativeObject', | |
| }, | |
| ); | |
| }); | |
| test('getObjectMetadata (NativeError)', () async { | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getObjectMetadata(new Error())', | |
| expectedResult: { | |
| 'className': 'NativeError', | |
| 'libraryId': 'dart:_interceptors', | |
| 'runtimeKind': 'nativeError', | |
| }, | |
| ); | |
| }); | |
| test('getObjectMetadata (DartError)', () async { | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getObjectMetadata(error)', | |
| expectedResult: { | |
| 'className': 'NativeError', | |
| 'libraryId': 'dart:_interceptors', | |
| 'runtimeKind': 'nativeError', | |
| }, | |
| ); | |
| }); | |
| test('typeName (int type)', () async { | |
| var typeName = await driver.evaluateDartExpressionInFrame( | |
| breakpointId: 'BP', | |
| expression: 'xType.toString()', | |
| ); | |
| expect(typeName, 'int'); | |
| }); | |
| test('typeName (base type)', () async { | |
| var typeName = await driver.evaluateDartExpressionInFrame( | |
| breakpointId: 'BP', | |
| expression: 'baseType.toString()', | |
| ); | |
| expect(typeName, 'BaseClass<int>'); | |
| }); | |
| test('getObjectMetadata (int type)', () async { | |
| var typeName = await driver.evaluateDartExpressionInFrame( | |
| breakpointId: 'BP', | |
| expression: 'xType.toString()', | |
| ); | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getObjectMetadata(xType)', | |
| expectedResult: { | |
| 'className': 'Type', | |
| 'libraryId': 'dart:core', | |
| 'runtimeKind': 'type', | |
| 'typeName': typeName, | |
| }, | |
| ); | |
| }); | |
| test('getObjectMetadata (base type)', () async { | |
| var typeName = await driver.evaluateDartExpressionInFrame( | |
| breakpointId: 'BP', | |
| expression: 'baseType.toString()', | |
| ); | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getObjectMetadata(baseType)', | |
| expectedResult: { | |
| 'className': 'Type', | |
| 'libraryId': 'dart:core', | |
| 'runtimeKind': 'type', | |
| 'typeName': typeName, | |
| }, | |
| ); | |
| }); | |
| test('getObjectMetadata (type)', () async { | |
| var typeName = await driver.evaluateDartExpressionInFrame( | |
| breakpointId: 'BP', | |
| expression: 'baseTypeType.toString()', | |
| ); | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getObjectMetadata(baseTypeType)', | |
| expectedResult: { | |
| 'className': 'Type', | |
| 'libraryId': 'dart:core', | |
| 'runtimeKind': 'type', | |
| 'typeName': typeName, | |
| }, | |
| ); | |
| }); | |
| test('getObjectMetadata (Set type)', () async { | |
| var typeName = await driver.evaluateDartExpressionInFrame( | |
| breakpointId: 'BP', | |
| expression: 'setType.toString()', | |
| ); | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getObjectMetadata(setType)', | |
| expectedResult: { | |
| 'className': 'Type', | |
| 'libraryId': 'dart:core', | |
| 'runtimeKind': 'type', | |
| 'typeName': typeName, | |
| }, | |
| ); | |
| }); | |
| test('getObjectMetadata (List type)', () async { | |
| var typeName = await driver.evaluateDartExpressionInFrame( | |
| breakpointId: 'BP', | |
| expression: 'listType.toString()', | |
| ); | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getObjectMetadata(listType)', | |
| expectedResult: { | |
| 'className': 'Type', | |
| 'libraryId': 'dart:core', | |
| 'runtimeKind': 'type', | |
| 'typeName': typeName, | |
| }, | |
| ); | |
| }); | |
| test('getObjectMetadata (Map type)', () async { | |
| var typeName = await driver.evaluateDartExpressionInFrame( | |
| breakpointId: 'BP', | |
| expression: 'mapType.toString()', | |
| ); | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getObjectMetadata(mapType)', | |
| expectedResult: { | |
| 'className': 'Type', | |
| 'libraryId': 'dart:core', | |
| 'runtimeKind': 'type', | |
| 'typeName': typeName, | |
| }, | |
| ); | |
| }); | |
| test('getObjectMetadata (Record type)', () async { | |
| var typeName = await driver.evaluateDartExpressionInFrame( | |
| breakpointId: 'BP', | |
| expression: 'recordType.toString()', | |
| ); | |
| expect(typeName, '(int, int, {String name})'); | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getObjectMetadata(recordType)', | |
| expectedResult: { | |
| 'className': 'Type', | |
| 'libraryId': 'dart:core', | |
| 'runtimeKind': 'recordType', | |
| 'length': 3, | |
| }, | |
| ); | |
| await driver.checkInFrame( | |
| breakpointId: 'BP', | |
| expression: 'record is Record', | |
| expectedResult: 'true', | |
| ); | |
| await driver.checkInFrame( | |
| breakpointId: 'BP', | |
| expression: 'recordType is Type', | |
| expectedResult: 'true', | |
| ); | |
| }); | |
| test('getObjectFieldNames (object)', () async { | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getObjectFieldNames(object)', | |
| expectedResult: [], | |
| ); | |
| }); | |
| test('getObjectFieldNames (derived class)', () async { | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getObjectFieldNames(derived)', | |
| expectedResult: [ | |
| '_field', | |
| '_newPrivateField', | |
| '_privateExtensionTypeField', | |
| '_unusedField', | |
| 'extensionTypeField', | |
| 'field', | |
| 'functionField', | |
| 'genericField', | |
| 'lateFinalField', | |
| 'newPublicField', | |
| 'nonNullableField', | |
| 'nullableField', | |
| ], | |
| ); | |
| }); | |
| test('getObjectFieldNames (base class)', () async { | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getObjectFieldNames(base)', | |
| expectedResult: [ | |
| '_field', | |
| '_privateExtensionTypeField', | |
| '_unusedField', | |
| 'extensionTypeField', | |
| 'field', | |
| 'functionField', | |
| 'genericField', | |
| 'lateFinalField', | |
| 'nonNullableField', | |
| 'nullableField', | |
| ], | |
| ); | |
| }); | |
| test('getSetElements', () async { | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getSetElements(set)', | |
| expectedResult: { | |
| 'entries': ['a', 'b', 'c'], | |
| }, | |
| ); | |
| }); | |
| test('getMapElements', () async { | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getMapElements(map)', | |
| expectedResult: { | |
| 'keys': ['a', 'b'], | |
| 'values': [1, 2], | |
| }, | |
| ); | |
| }); | |
| test('getRecordFields', () async { | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getRecordFields(record)', | |
| expectedResult: { | |
| 'positionalCount': 2, | |
| 'named': ['name'], | |
| 'values': [0, 2, 'cat'], | |
| }, | |
| ); | |
| }); | |
| // TODO(annagrin): Add recursive check for nested objects. | |
| test('getRecordTypeFields', () async { | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getRecordTypeFields(recordType)', | |
| expectedResult: { | |
| 'positionalCount': 2, | |
| 'named': ['name'], | |
| 'types': [{}, {}, {}], | |
| }, | |
| ); | |
| }); | |
| test('getFunctionName (method)', () async { | |
| var getFunctionName = setup.emitLibraryBundle | |
| ? 'getFunctionName' | |
| : 'getFunctionMetadata'; | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.$getFunctionName(base.method)', | |
| expectedResult: 'method', | |
| ); | |
| }); | |
| test('getFunctionName (static method)', () async { | |
| var getFunctionName = setup.emitLibraryBundle | |
| ? 'getFunctionName' | |
| : 'getFunctionMetadata'; | |
| var expectedName = 'BaseClass.staticMethod'; | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.$getFunctionName(staticMethod)', | |
| expectedResult: expectedName, | |
| ); | |
| }); | |
| test('getFunctionName (global method)', () async { | |
| var getFunctionName = setup.emitLibraryBundle | |
| ? 'getFunctionName' | |
| : 'getFunctionMetadata'; | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.$getFunctionName(globalMethod)', | |
| expectedResult: 'globalFunction', | |
| ); | |
| }); | |
| test('getSubRange (set)', () async { | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getSubRange(set, 0, 3)', | |
| expectedResult: ['a', 'b', 'c'], | |
| ); | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getSubRange(set, 1, 2)', | |
| expectedResult: ['b', 'c'], | |
| ); | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getSubRange(set, 1, 5)', | |
| expectedResult: ['b', 'c'], | |
| ); | |
| }); | |
| test('getSubRange (list)', () async { | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getSubRange(list, 0, 3)', | |
| expectedResult: [1, 2, 3], | |
| ); | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getSubRange(list, 1, 2)', | |
| expectedResult: [2, 3], | |
| ); | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getSubRange(list, 1, 5)', | |
| expectedResult: [2, 3], | |
| ); | |
| }); | |
| test('getSubRange (map)', () async { | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getSubRange(map, 0, 3)', | |
| expectedResult: isA<List>().having( | |
| (p) => p.length, | |
| 'length', | |
| equals(2), | |
| ), | |
| ); | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getSubRange(map, 1, 2)', | |
| expectedResult: isA<List>().having( | |
| (p) => p.length, | |
| 'length', | |
| equals(1), | |
| ), | |
| ); | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: 'dart.getSubRange(map, 1, 5)', | |
| expectedResult: isA<List>().having( | |
| (p) => p.length, | |
| 'length', | |
| equals(1), | |
| ), | |
| ); | |
| }); | |
| test('callLibraryMethod', () async { | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: "dart.getObjectMetadata(dart.callLibraryMethod('package:eval_test/test.dart', 'globalFunction', [1, 3]))", | |
| expectedResult: { | |
| 'className': 'DerivedClass', | |
| 'libraryId': 'package:eval_test/test.dart', | |
| 'runtimeKind': 'object', | |
| 'length': 12, | |
| }, | |
| ); | |
| }, skip: !setup.emitLibraryBundle); | |
| test('callInstanceMethod', () async { | |
| await driver.checkRuntimeInFrame( | |
| breakpointId: 'BP', | |
| expression: "dart.callInstanceMethod(dart.callLibraryMethod('package:eval_test/test.dart', 'globalFunction', [1, 3]), 'stringLength', ['hello'])", | |
| expectedResult: 5, | |
| ); | |
| }, skip: !setup.emitLibraryBundle); | |
| }); | |
| group('extension type expression compilations |', () { | |
| var source = r''' | |
| //@dart=3.3 | |
| void main() { | |
| Foo f = new Foo(42); | |
| Baz b = new Baz(new Bar(42)); | |
| print(f); | |
| print(b); | |
| // Breakpoint: BP1 | |
| print(f.value); | |
| print(b.value); | |
| f.printValue(); | |
| f.printThis(); | |
| b.printThis(); | |
| } | |
| class Bar { | |
| final int i; | |
| Bar(this.i); | |
| String toString() => "Bar[$i]"; | |
| } | |
| extension type Foo(int value) { | |
| void printValue() { | |
| // Breakpoint: BP2 | |
| print("This foos value is '$value'"); | |
| } | |
| String printThis() { | |
| var foo = value; | |
| // Breakpoint: BP3 | |
| print("This foos this value is '$this'"); | |
| return "I printed '$value'!"; | |
| } | |
| } | |
| extension type Baz(Bar value) { | |
| String printThis() { | |
| var foo = value; | |
| // Breakpoint: BP4 | |
| print("This Baz' this value is '$this'"); | |
| return "I printed '$value'!"; | |
| } | |
| } | |
| '''; | |
| setUpAll(() async { | |
| await driver.initSource(setup, source); | |
| }); | |
| tearDownAll(() async { | |
| await driver.cleanupTest(); | |
| }); | |
| test('value on extension type (int)', () async { | |
| var result = await driver.evaluateDartExpressionInFrame( | |
| breakpointId: 'BP1', | |
| expression: 'f.value', | |
| ); | |
| expect(result, '42'); | |
| }); | |
| test('value on extension type (custom class)', () async { | |
| var result = await driver.evaluateDartExpressionInFrame( | |
| breakpointId: 'BP1', | |
| expression: 'b.value.toString()', | |
| ); | |
| expect(result, 'Bar[42]'); | |
| }); | |
| test('method on extension type (int)', () async { | |
| var result = await driver.evaluateDartExpressionInFrame( | |
| breakpointId: 'BP1', | |
| expression: 'f.printThis()', | |
| ); | |
| expect(result, "I printed '42'!"); | |
| }); | |
| test('method on extension type (custom class)', () async { | |
| var result = await driver.evaluateDartExpressionInFrame( | |
| breakpointId: 'BP1', | |
| expression: 'b.printThis()', | |
| ); | |
| expect(result, "I printed 'Bar[42]'!"); | |
| }); | |
| test('inside extension type method (int) (1)', () async { | |
| var result = await driver.evaluateDartExpressionInFrame( | |
| breakpointId: 'BP2', | |
| expression: 'printThis()', | |
| ); | |
| expect(result, "I printed '42'!"); | |
| }); | |
| test('inside extension type method (int) (2)', () async { | |
| var result = await driver.evaluateDartExpressionInFrame( | |
| breakpointId: 'BP3', | |
| expression: 'foo + value', | |
| ); | |
| expect(result, '84'); | |
| }); | |
| test('inside extension type method (custom class) (1)', () async { | |
| var result = await driver.evaluateDartExpressionInFrame( | |
| breakpointId: 'BP4', | |
| expression: 'printThis()', | |
| ); | |
| expect(result, "I printed 'Bar[42]'!"); | |
| }); | |
| test('inside extension type method (custom class) (2)', () async { | |
| var result = await driver.evaluateDartExpressionInFrame( | |
| breakpointId: 'BP4', | |
| expression: 'foo.i + value.i', | |
| ); | |
| expect(result, '84'); | |
| }); | |
| }); | |
| group('extensions expression compilations |', () { | |
| var source = r''' | |
| void main() { | |
| int i = 42; | |
| Bar b = new Bar(42); | |
| print(i); | |
| print(b); | |
| // Breakpoint: BP1 | |
| i.printThis(); | |
| b.printThis(); | |
| } | |
| class Bar { | |
| final int i; | |
| Bar(this.i); | |
| String toString() => "Bar[$i]"; | |
| } | |
| extension Foo on int { | |
| String printThis() { | |
| var value = this; | |
| // Breakpoint: BP2 | |
| print("This foos this value is '$this'"); | |
| return "I printed '$value'!"; | |
| } | |
| } | |
| extension Baz on Bar { | |
| String printThis() { | |
| var value = this; | |
| // Breakpoint: BP3 | |
| print("This Bars this value is '$this'"); | |
| return "I printed '$value'!"; | |
| } | |
| }'''; | |
| setUpAll(() async { | |
| await driver.initSource(setup, source); | |
| }); | |
| tearDownAll(() async { | |
| await driver.cleanupTest(); | |
| }); | |
| test('call function on extension (int)', () async { | |
| var result = await driver.evaluateDartExpressionInFrame( | |
| breakpointId: 'BP1', | |
| expression: 'i.printThis()', | |
| ); | |
| expect(result, "I printed '42'!"); | |
| }); | |
| test('call function on extension (custom class)', () async { | |
| var result = await driver.evaluateDartExpressionInFrame( | |
| breakpointId: 'BP1', | |
| expression: 'b.printThis()', | |
| ); | |
| expect(result, "I printed 'Bar[42]'!"); | |
| }); | |
| test('inside extension method (int) (1)', () async { | |
| var result = await driver.evaluateDartExpressionInFrame( | |
| breakpointId: 'BP2', | |
| expression: 'printThis()', | |
| ); | |
| expect(result, "I printed '42'!"); | |
| }); | |
| test('inside extension type method (int) (2)', () async { | |
| var result = await driver.evaluateDartExpressionInFrame( | |
| breakpointId: 'BP2', | |
| expression: 'this + value', | |
| ); | |
| expect(result, '84'); | |
| }); | |
| test('inside extension method (custom class) (1)', () async { | |
| var result = await driver.evaluateDartExpressionInFrame( | |
| breakpointId: 'BP3', | |
| expression: 'printThis()', | |
| ); | |
| expect(result, "I printed 'Bar[42]'!"); | |
| }); | |
| test('inside extension type method (custom class) (2)', () async { | |
| var result = await driver.evaluateDartExpressionInFrame( | |
| breakpointId: 'BP3', | |
| expression: 'this.i + value.i', | |
| ); | |
| expect(result, '84'); | |
| }); | |
| }); | |
| group('part files expression compilations same line and offset exists |', () { | |
| // WARNING: The (main) source and the part source have been constructed | |
| // so that the same offset (71) is valid on both, and both have an 'x' | |
| // variable, where one is a String and the other is an int. The 4 dots after | |
| // 'padding' for instance is not a mistake. | |
| var source = r''' | |
| part 'part.dart'; | |
| void main() { | |
| String x = "foo"; | |
| // padding.... | |
| foo(); // Line 5, column 3, offset 71 | |
| print(x); | |
| }'''; | |
| var partSource = r''' | |
| part of 'test.dart'; | |
| void foo() { | |
| int x = 42; | |
| // Breakpoint: BP1 | |
| print(x); // Line 5, column 3, offset 71 | |
| }'''; | |
| setUpAll(() async { | |
| await driver.initSource(setup, source, partSource: partSource); | |
| }); | |
| tearDownAll(() async { | |
| await driver.cleanupTest(); | |
| }); | |
| test('can evaluate in part file', () async { | |
| var result = await driver.evaluateDartExpressionInFrame( | |
| breakpointId: 'BP1', | |
| expression: 'x + 1', | |
| ); | |
| expect(result, '43'); | |
| }); | |
| }); | |
| group('part files expression compilations line and offset mismatch |', () { | |
| var source = r''' | |
| // Line 5 is this one. There is nothing before it. | |
| part 'part.dart'; | |
| void main() { | |
| String x = "foo"; | |
| foo(); | |
| // Breakpoint: BP2 | |
| print(x); | |
| }'''; | |
| var partSource = r''' | |
| part of 'test.dart'; | |
| void foo() { | |
| int x = 42; | |
| // Breakpoint: BP1 | |
| print(x); // Line 5, column 3 | |
| }'''; | |
| setUpAll(() async { | |
| await driver.initSource(setup, source, partSource: partSource); | |
| }); | |
| tearDownAll(() async { | |
| await driver.cleanupTest(); | |
| }); | |
| test('can evaluate in part file', () async { | |
| var result = await driver.evaluateDartExpressionInFrame( | |
| breakpointId: 'BP1', | |
| expression: 'x + 1', | |
| ); | |
| expect(result, '43'); | |
| result = await driver.evaluateDartExpressionInFrame( | |
| breakpointId: 'BP2', | |
| expression: 'x.length', | |
| ); | |
| expect(result, '3'); | |
| }); | |
| }); | |
| } |