diff --git a/pkg/dev_compiler/lib/src/compiler/js_names.dart b/pkg/dev_compiler/lib/src/compiler/js_names.dart index f884331c7c2..985496d46a3 100644 --- a/pkg/dev_compiler/lib/src/compiler/js_names.dart +++ b/pkg/dev_compiler/lib/src/compiler/js_names.dart @@ -9,6 +9,22 @@ import '../js_ast/js_ast.dart'; /// The ES6 name for the Dart SDK. All dart:* libraries are in this module. const String dartSdkModule = 'dart_sdk'; +/// Names expected to be used without renaming. +/// +/// These are fixed by the dart:_rti library and are accessed through the +/// `JsGetName` enum. +abstract class FixedNames { + static const operatorIsPrefix = r'$is'; + static const operatorSignature = r'$signature'; + static const rtiName = r'$ti'; + // TODO(48585) Fix this name. + static const futureClassName = 'TODO'; + // TODO(48585) Fix this name. + static const listClassName = 'TODO'; + static const rtiAsField = '_as'; + static const rtiIsField = '_is'; +} + /// Unique instance for temporary variables. Will be renamed consistently /// across the entire file. Different instances will be named differently /// even if they have the same name, this makes it safe to use in code diff --git a/pkg/dev_compiler/lib/src/compiler/js_utils.dart b/pkg/dev_compiler/lib/src/compiler/js_utils.dart index aaecf7fbddc..57adce879a1 100644 --- a/pkg/dev_compiler/lib/src/compiler/js_utils.dart +++ b/pkg/dev_compiler/lib/src/compiler/js_utils.dart @@ -2,6 +2,9 @@ // 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:js_shared/synced/embedded_names.dart' + show RtiUniverseFieldNames; + import '../js_ast/js_ast.dart'; /// Simplify `(args) => (() => { ... })()` to `(args) => { ... }`. @@ -51,3 +54,21 @@ class SourceInformationClearer extends BaseVisitorVoid { node.sourceInformation = null; } } + +/// Returns an expression that creates the initial Rti Universe. +/// +/// This needs to be kept in sync with `_Universe.create` in `dart:_rti`. +Expression createRtiUniverse() { + Property initField(String name, String value) => + Property(js.string(name), js(value)); + + var universeFields = [ + initField(RtiUniverseFieldNames.evalCache, 'new Map()'), + initField(RtiUniverseFieldNames.typeRules, '{}'), + initField(RtiUniverseFieldNames.erasedTypes, '{}'), + initField(RtiUniverseFieldNames.typeParameterVariances, '{}'), + initField(RtiUniverseFieldNames.sharedEmptyArray, '[]'), + ]; + + return ObjectInitializer(universeFields); +} diff --git a/pkg/dev_compiler/lib/src/compiler/shared_compiler.dart b/pkg/dev_compiler/lib/src/compiler/shared_compiler.dart index fd2b9c82c5f..d2382d9a171 100644 --- a/pkg/dev_compiler/lib/src/compiler/shared_compiler.dart +++ b/pkg/dev_compiler/lib/src/compiler/shared_compiler.dart @@ -178,6 +178,10 @@ abstract class SharedCompiler { @protected String libraryToModule(Library library); + /// Returns true if [library] is identified by [name]. + @protected + bool isDartLibrary(Library library, String name); + /// Returns true if the library [l] is "dart:_runtime". @protected bool isSdkInternalRuntime(Library l); diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart index 506b32d30ae..6ad41f77a66 100644 --- a/pkg/dev_compiler/lib/src/kernel/compiler.dart +++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart @@ -9,6 +9,7 @@ import 'dart:convert'; import 'dart:math' show max, min; import 'package:front_end/src/api_unstable/ddc.dart'; +import 'package:js_shared/synced/embedded_names.dart' show JsGetName, JsBuiltin; import 'package:kernel/class_hierarchy.dart'; import 'package:kernel/core_types.dart'; import 'package:kernel/kernel.dart'; @@ -368,6 +369,17 @@ class ProgramCompiler extends ComputeOnceConstantVisitor // Initialize library variables. isBuildingSdk = libraries.any(isSdkInternalRuntime); + // TODO(48585) Remove after new type system has landed. + if (isBuildingSdk && !_options.newRuntimeTypes) { + libraries.removeWhere((library) { + var path = library.importUri.path; + return path == '_js_shared_embedded_names' || + path == '_js_names' || + path == '_recipe_syntax' || + path == '_rti'; + }); + } + // For runtime performance reasons, we only containerize SDK symbols in web // libraries. Otherwise, we use a 600-member cutoff before a module is // containerized. This is somewhat arbitrary but works promisingly for the @@ -475,6 +487,8 @@ class ProgramCompiler extends ComputeOnceConstantVisitor if (!isBuildingSdk) { items.add( runtimeStatement('_checkModuleNullSafetyMode(#)', [soundNullSafety])); + items.add(runtimeStatement('_checkModuleRuntimeTypes(#)', + [js_ast.LiteralBool(_options.newRuntimeTypes)])); } // Emit the hoisted type table cache variables @@ -519,9 +533,16 @@ class ProgramCompiler extends ComputeOnceConstantVisitor library.parts.map((part) => part.partUri); /// True when [library] is the sdk internal library 'dart:_internal'. - bool _isDartInternal(Library library) { + bool _isDartInternal(Library library) => isDartLibrary(library, '_internal'); + + /// True when [library] is the sdk internal library 'dart:_internal'. + bool _isDartForeignHelper(Library library) => + isDartLibrary(library, '_foreign_helper'); + + @override + bool isDartLibrary(Library library, String name) { var importUri = library.importUri; - return importUri.isScheme('dart') && importUri.path == '_internal'; + return importUri.isScheme('dart') && importUri.path == name; } @override @@ -559,6 +580,12 @@ class ProgramCompiler extends ComputeOnceConstantVisitor } if (isSdkInternalRuntime(library)) { + if (_options.newRuntimeTypes) { + // Add embedded globals. + moduleItems.add( + runtimeCall('typeUniverse = #', [js_ast.createRtiUniverse()]) + .toStatement()); + } // `dart:_runtime` uses a different order for bootstrapping. // // Functions are first because we use them to associate type info @@ -5535,17 +5562,45 @@ class ProgramCompiler extends ComputeOnceConstantVisitor if (isInlineJS(target)) return _emitInlineJSCode(node) as js_ast.Expression; if (target.isFactory) return _emitFactoryInvocation(node); - // Optimize some internal SDK calls. - if (_isDartInternal(target.enclosingLibrary)) { + var enclosingLibrary = target.enclosingLibrary; + if (_isDartInternal(enclosingLibrary)) { var args = node.arguments; if (args.positional.length == 1 && args.types.length == 1 && args.named.isEmpty && target.name.text == 'unsafeCast') { + // Optimize some internal SDK calls by avoiding the insertion of a + // runtime cast. return args.positional.single.accept(this); } } - if (isSdkInternalRuntime(target.enclosingLibrary)) { + + if (_isDartForeignHelper(enclosingLibrary)) { + var args = node.arguments.positional; + var name = target.name.text; + + if (args.length == 1) { + if (name == 'JS_GET_NAME') { + var staticGet = args.single as StaticGet; + var enumField = staticGet.target as Field; + return _emitNameForJsGetName(asJsGetName(enumField)); + } + } else if (args.length == 2) { + if (name == 'JS_EMBEDDED_GLOBAL') return _emitEmbeddedGlobal(node); + if (name == 'JS_STRING_CONCAT') { + var left = _visitExpression(args.first); + var right = _visitExpression(args.last); + return js.call('# + #', [left, right]); + } + } + if (name == 'JS_BUILTIN') { + var staticGet = args[1] as StaticGet; + var enumField = staticGet.target as Field; + return _emitOperationForJsBuiltIn(asJsBuiltin(enumField)); + } + } + + if (isSdkInternalRuntime(enclosingLibrary)) { var name = target.name.text; if (node.arguments.positional.isEmpty && node.arguments.types.length == 1) { @@ -5577,6 +5632,9 @@ class ProgramCompiler extends ComputeOnceConstantVisitor if (flagName == 'soundNullSafety') { return js.boolean(_options.soundNullSafety); } + if (flagName == 'newRuntimeTypes') { + return js.boolean(_options.newRuntimeTypes); + } throw UnsupportedError('Invalid flag in call to $name: $flagName'); } } else if (node.arguments.positional.length == 2) { @@ -5762,6 +5820,62 @@ class ProgramCompiler extends ComputeOnceConstantVisitor return result.withSourceInformation(_nodeStart(node)); } + js_ast.Expression _emitEmbeddedGlobal(StaticInvocation node) { + var constantExpression = node.arguments.positional[1] as ConstantExpression; + var name = constantExpression.constant as StringConstant; + return runtimeCall('#', [name.value]); + } + + /// Returns the string literal that is to be used as the result of a call to + /// [JS_GET_NAME] for [name]. + js_ast.LiteralString _emitNameForJsGetName(JsGetName name) { + switch (name) { + case JsGetName.OPERATOR_IS_PREFIX: + return js.string(js_ast.FixedNames.operatorIsPrefix); + case JsGetName.SIGNATURE_NAME: + return js.string(js_ast.FixedNames.operatorSignature); + case JsGetName.RTI_NAME: + return js.string(js_ast.FixedNames.rtiName); + case JsGetName.FUTURE_CLASS_TYPE_NAME: + return js.string(js_ast.FixedNames.futureClassName); + case JsGetName.LIST_CLASS_TYPE_NAME: + return js.string(js_ast.FixedNames.listClassName); + case JsGetName.RTI_FIELD_AS: + return js.string(js_ast.FixedNames.rtiAsField); + case JsGetName.RTI_FIELD_IS: + return js.string(js_ast.FixedNames.rtiIsField); + default: + throw UnsupportedError('JsGetName has no name for "$name".'); + } + } + + /// Returns the expression that is to be used as the result of a call to + /// [JS_BUILTIN] for [builtin]. + js_ast.Expression _emitOperationForJsBuiltIn(JsBuiltin builtin) { + switch (builtin) { + case JsBuiltin.dartClosureConstructor: + // TODO(48585) How to get constructor for a Dart Function? + return js.call('TODO'); + case JsBuiltin.dartObjectConstructor: + // TODO(48585) How to get constructor for a Dart Object? + return js.call('TODO'); + default: + throw UnsupportedError('JsBuiltin has no operation for "$builtin".'); + } + } + + String _enumValueName(Field field) { + var enumName = field.enclosingClass.name; + var valueName = field.name.text; + return '$enumName.$valueName'; + } + + JsGetName asJsGetName(Field field) => JsGetName.values + .firstWhere((val) => val.toString() == _enumValueName(field)); + + JsBuiltin asJsBuiltin(Field field) => JsBuiltin.values + .firstWhere((val) => val.toString() == _enumValueName(field)); + bool _isWebLibrary(Uri importUri) => importUri != null && importUri.isScheme('dart') && diff --git a/pkg/dev_compiler/lib/src/kernel/kernel_helpers.dart b/pkg/dev_compiler/lib/src/kernel/kernel_helpers.dart index fa868d90d16..0576c6fc767 100644 --- a/pkg/dev_compiler/lib/src/kernel/kernel_helpers.dart +++ b/pkg/dev_compiler/lib/src/kernel/kernel_helpers.dart @@ -203,9 +203,13 @@ Expression? getInvocationReceiver(InvocationExpression node) { } bool isInlineJS(Member e) => - e is Procedure && - e.name.text == 'JS' && - e.enclosingLibrary.importUri.toString() == 'dart:_foreign_helper'; + e is Procedure && _isProcedureFromForeignHelper('JS', e); + +/// Returns `true` if [p] is the procedure named [name] from the +/// 'dart:_foreign_helper' library. +bool _isProcedureFromForeignHelper(String name, Procedure p) => + p.name.text == name && + p.enclosingLibrary.importUri.toString() == 'dart:_foreign_helper'; /// Whether the parameter [p] is covariant (either explicitly `covariant` or /// implicitly due to generics) and needs a check for soundness. diff --git a/pkg/dev_compiler/lib/src/kernel/target.dart b/pkg/dev_compiler/lib/src/kernel/target.dart index 9bddfc7802f..0a3e3ff965f 100644 --- a/pkg/dev_compiler/lib/src/kernel/target.dart +++ b/pkg/dev_compiler/lib/src/kernel/target.dart @@ -57,6 +57,9 @@ class DevCompilerTarget extends Target { @override List get extraRequiredLibraries => const [ 'dart:_runtime', + 'dart:_js_shared_embedded_names', + 'dart:_recipe_syntax', + 'dart:_rti', 'dart:_dart2js_runtime_metrics', 'dart:_debugger', 'dart:_foreign_helper', @@ -64,6 +67,7 @@ class DevCompilerTarget extends Target { 'dart:_internal', 'dart:_isolate_helper', 'dart:_js_helper', + 'dart:_js_names', 'dart:_js_primitives', 'dart:_metadata', 'dart:_native_typed_data', @@ -103,6 +107,7 @@ class DevCompilerTarget extends Target { 'dart:_js_helper', 'dart:_native_typed_data', 'dart:_runtime', + 'dart:_rti', ]; @override diff --git a/pkg/dev_compiler/pubspec.yaml b/pkg/dev_compiler/pubspec.yaml index 5ec84aa49d5..f666b47bd2b 100644 --- a/pkg/dev_compiler/pubspec.yaml +++ b/pkg/dev_compiler/pubspec.yaml @@ -14,6 +14,7 @@ dependencies: build_integration: any collection: any front_end: any + js_shared: any kernel: any meta: any path: any diff --git a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart index b798adf3399..f028cb7c680 100644 --- a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart +++ b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart @@ -44,6 +44,21 @@ void _checkModuleNullSafetyMode(@notNull bool isModuleSound) { } } +/// Throws if [useNewTypes] does not match the version of the type +/// representation of this SDK. +/// +/// The call to this method is inserted into every module at compile time. +void _checkModuleRuntimeTypes(@notNull bool useNewTypes) { + if (useNewTypes != compileTimeFlag('newRuntimeTypes')) { + var sdkTypes = compileTimeFlag('newRuntimeTypes') ? 'new' : 'old'; + var moduleTypes = useNewTypes ? 'new' : 'old'; + + throw AssertionError('The Dart SDK module is using the $sdkTypes runtime ' + 'type representation and is incompatible with the $moduleTypes ' + 'representation used in this module.'); + } +} + final _nullFailedSet = JS('!', 'new Set()'); String _nullFailedMessage(variableName) => diff --git a/sdk/lib/_internal/js_dev_runtime/private/foreign_helper.dart b/sdk/lib/_internal/js_dev_runtime/private/foreign_helper.dart index 6551c93a53f..354f4487fca 100644 --- a/sdk/lib/_internal/js_dev_runtime/private/foreign_helper.dart +++ b/sdk/lib/_internal/js_dev_runtime/private/foreign_helper.dart @@ -4,6 +4,9 @@ library dart._foreign_helper; +import 'dart:_js_shared_embedded_names' show JsBuiltin, JsGetName; +import 'dart:_rti' show Rti; + /** * Emits a JavaScript code fragment parameterized by arguments. * @@ -221,7 +224,7 @@ external String JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG(); external String JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG(); /// Returns the JS name for [name] from the Namer. -external String JS_GET_NAME(String name); +external String JS_GET_NAME(JsGetName name); /// Returns the state of a flag that is determined by the state of the compiler /// when the program has been analyzed. @@ -274,3 +277,52 @@ dynamic spread(args) { throw StateError('The spread function cannot be called, ' 'it should be compiled away.'); } + +/// Reads an embedded global. +/// +/// The [name] should be a constant defined in the `_js_shared_embedded_names` +/// library. +external JS_EMBEDDED_GLOBAL(String typeDescription, String name); + +/// Instructs the compiler to execute the [builtinName] action at the call-site. +/// +/// The [builtin] should be a constant defined in the +/// `_js_shared_embedded_names` library. +// Add additional optional arguments if needed. The method is treated internally +// as a variable argument method. +external JS_BUILTIN(String typeDescription, JsBuiltin builtin, + [arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11]); + +/// Returns the interceptor for [object]. +/// +// TODO(nshahan) Replace calls at compile time? +external getInterceptor(object); + +/// Returns the Rti object for the type for JavaScript arrays via JS-interop. +/// +// TODO(nshahan) Replace calls at compile time? +external Object getJSArrayInteropRti(); + +/// Returns a raw reference to the JavaScript function which implements +/// [function]. +/// +/// Warning: this is dangerous, you should probably use +/// [DART_CLOSURE_TO_JS] instead. The returned object is not a valid +/// Dart closure, does not store the isolate context or arity. +/// +/// A valid example of where this can be used is as the second argument +/// to V8's Error.captureStackTrace. See +/// https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi. +// TODO(nshahan) Replace calls at compile time? +external RAW_DART_FUNCTION_REF(Function function); + +/// Returns a TypeReference to [T]. +// TODO(nshahan) Replace calls at compile time? +external Rti TYPE_REF(); + +/// Returns a TypeReference to [T]*. +// TODO(nshahan) Replace calls at compile time? +external Rti LEGACY_TYPE_REF(); + +/// JavaScript string concatenation. Inputs must be Strings. +external String JS_STRING_CONCAT(String a, String b); diff --git a/sdk/lib/_internal/js_dev_runtime/private/interceptors.dart b/sdk/lib/_internal/js_dev_runtime/private/interceptors.dart index 8e700e5fe2f..8bbd911011e 100644 --- a/sdk/lib/_internal/js_dev_runtime/private/interceptors.dart +++ b/sdk/lib/_internal/js_dev_runtime/private/interceptors.dart @@ -23,9 +23,6 @@ abstract class Interceptor { String toString() => JS('!', '#.toString()', this); } -// TODO(jmesserly): remove -getInterceptor(obj) => obj; - /** * The interceptor class for [bool]. */ @@ -296,3 +293,6 @@ setDispatchProperty(object, value) {} // TODO(sigmund): revisit whether this method is still needed after reoganizing // all web tests. findInterceptorForType(Type? type) {} + +// TODO(nshahan) Find a correct representation for JS functions. +typedef JavaScriptFunction = dart.FunctionType; diff --git a/sdk/lib/_internal/js_dev_runtime/private/js_names.dart b/sdk/lib/_internal/js_dev_runtime/private/js_names.dart new file mode 100644 index 00000000000..93e5e0d3d8c --- /dev/null +++ b/sdk/lib/_internal/js_dev_runtime/private/js_names.dart @@ -0,0 +1,9 @@ +// 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. + +library dart._js_names; + +String? unmangleGlobalNameIfPreservedAnyways(String name) { + throw ('Unimplemented'); +} diff --git a/sdk/lib/libraries.json b/sdk/lib/libraries.json index 4c5eb19962e..df96cc0ca0a 100644 --- a/sdk/lib/libraries.json +++ b/sdk/lib/libraries.json @@ -424,9 +424,15 @@ "_js_annotations": { "uri": "js/_js_annotations.dart" }, + "_js_shared_embedded_names": { + "uri": "_internal/js_shared/lib/synced/embedded_names.dart" + }, "_js_helper": { "uri": "_internal/js_dev_runtime/private/js_helper.dart" }, + "_js_names": { + "uri": "_internal/js_dev_runtime/private/js_names.dart" + }, "_js_primitives": { "uri": "_internal/js_dev_runtime/private/js_primitives.dart" }, @@ -439,6 +445,12 @@ "_dart2js_runtime_metrics": { "uri": "_internal/js_dev_runtime/private/runtime_metrics.dart" }, + "_rti": { + "uri": "_internal/js_shared/lib/rti.dart" + }, + "_recipe_syntax": { + "uri": "_internal/js_shared/lib/synced/recipe_syntax.dart" + }, "async": { "uri": "async/async.dart", "patches": "_internal/js_dev_runtime/patch/async_patch.dart" diff --git a/sdk/lib/libraries.yaml b/sdk/lib/libraries.yaml index c09c34ea18a..f19ba03cbea 100644 --- a/sdk/lib/libraries.yaml +++ b/sdk/lib/libraries.yaml @@ -390,9 +390,15 @@ dartdevc: _js_annotations: uri: "js/_js_annotations.dart" + _js_shared_embedded_names: + uri: "_internal/js_shared/lib/synced/embedded_names.dart" + _js_helper: uri: "_internal/js_dev_runtime/private/js_helper.dart" + _js_names: + uri: "_internal/js_dev_runtime/private/js_names.dart" + _js_primitives: uri: "_internal/js_dev_runtime/private/js_primitives.dart" @@ -405,6 +411,12 @@ dartdevc: _dart2js_runtime_metrics: uri: "_internal/js_dev_runtime/private/runtime_metrics.dart" + _rti: + uri: "_internal/js_shared/lib/rti.dart" + + _recipe_syntax: + uri: "_internal/js_shared/lib/synced/recipe_syntax.dart" + async: uri: "async/async.dart" patches: "_internal/js_dev_runtime/patch/async_patch.dart" diff --git a/utils/dartdevc/BUILD.gn b/utils/dartdevc/BUILD.gn index 5d93e803f7b..d2bcd693afd 100644 --- a/utils/dartdevc/BUILD.gn +++ b/utils/dartdevc/BUILD.gn @@ -8,6 +8,11 @@ import("../../utils/compile_platform.gni") import("../application_snapshot.gni") import("../create_timestamp.gni") +declare_args() { + # Enables DDC canary features during compilation to Javascript. + ddc_canary = false +} + patched_sdk_dir = "$target_gen_dir/patched_sdk" sdk_summary = "$target_gen_dir/ddc_sdk.sum" @@ -30,6 +35,10 @@ application_snapshot("dartdevc") { rebase_path("../../pkg/dev_compiler/bin/dartdevc.dart"), ] + if (ddc_canary) { + training_args += [ "--canary" ] + } + deps = [ ":dartdevc_kernel_sdk", ":dartdevc_platform", @@ -186,6 +195,10 @@ template("dartdevc_kernel_compile") { "package:$module/$module.dart", ] + if (ddc_canary) { + args += [ "--canary" ] + } + if (defined(invoker.extra_libraries)) { foreach(lib, invoker.extra_libraries) { args += [ "package:$module/$lib.dart" ] @@ -356,6 +369,10 @@ template("dartdevc_sdk_js") { if (invoker.sound_null_safety) { args += [ "--sound-null-safety" ] } + + if (ddc_canary) { + args += [ "--canary" ] + } } }