273ae87679
`dartifyRaw` converts based on the runtime type and it's slow. Use
dartify functions specific to the expected Dart types when converting JS
values to Dart.
Benchmarks before:
WasmJSInterop.call.bool.0Args(RunTimeRaw): 0.023022675490940652 ns.
WasmJSInterop.call.nullableBool.0Args(RunTimeRaw): 0.02189111747851003 ns.
WasmJSInterop.call.num.0Args(RunTimeRaw): 0.028148974752406197 ns.
WasmJSInterop.call.nullableNum.0Args(RunTimeRaw): 0.028181565023670287 ns.
WasmJSInterop.call.double.0Args(RunTimeRaw): 0.022986874778290176 ns.
WasmJSInterop.call.nullableDouble.0Args(RunTimeRaw): 0.02907384054925038 ns.
WasmJSInterop.call.int.0Args(RunTimeRaw): 0.029822888283378746 ns.
WasmJSInterop.call.nullableInt.0Args(RunTimeRaw): 0.029088963619688168 ns.
WasmJSInterop.call.string.0Args(RunTimeRaw): 0.020517746991606835 ns.
WasmJSInterop.call.nullableString.0Args(RunTimeRaw): 0.02004299314157027 ns.
WasmJSInterop.call.JSArray.0Args(RunTimeRaw): 0.020724174653887113 ns.
WasmJSInterop.call.nullableJSArray.0Args(RunTimeRaw): 0.020034212115113702 ns.
WasmJSInterop.call.JSUint8Array.0Args(RunTimeRaw): 0.030711090544501003 ns.
WasmJSInterop.call.nullableJSUint8Array.0Args(RunTimeRaw): 0.030679086538461537 ns.
Benchmarks after:
WasmJSInterop.call.bool.0Args(RunTimeRaw): 0.019944312674022892 ns.
WasmJSInterop.call.nullableBool.0Args(RunTimeRaw): 0.0206124732933157 ns.
WasmJSInterop.call.num.0Args(RunTimeRaw): 0.024475179056557175 ns.
WasmJSInterop.call.nullableNum.0Args(RunTimeRaw): 0.02481147236988503 ns.
WasmJSInterop.call.double.0Args(RunTimeRaw): 0.01829122645842903 ns.
WasmJSInterop.call.nullableDouble.0Args(RunTimeRaw): 0.024581756014165346 ns.
WasmJSInterop.call.int.0Args(RunTimeRaw): 0.01788861473387381 ns.
WasmJSInterop.call.nullableInt.0Args(RunTimeRaw): 0.02333933933933934 ns.
WasmJSInterop.call.string.0Args(RunTimeRaw): 0.018238532110091743 ns.
WasmJSInterop.call.nullableString.0Args(RunTimeRaw): 0.019454117647058823 ns.
WasmJSInterop.call.JSArray.0Args(RunTimeRaw): 0.020055434567412395 ns.
WasmJSInterop.call.nullableJSArray.0Args(RunTimeRaw): 0.019759825327510916 ns.
WasmJSInterop.call.JSUint8Array.0Args(RunTimeRaw): 0.030023959269242288 ns.
WasmJSInterop.call.nullableJSUint8Array.0Args(RunTimeRaw): 0.03057233704292528 ns.
JSValue wrapper type performance (`JSUint8Array`, `JSArray`) does not
change as we had a special case (before this CL) for those types and
directly boxed them.
Issue: https://github.com/dart-lang/sdk/issues/60357
Change-Id: I0ee8e49cf3ed801634e4c6e13b1c597b2e730d59
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/424021
Commit-Queue: Ömer Ağacan <omersa@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
614 lines
21 KiB
Dart
614 lines
21 KiB
Dart
// 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:_fe_analyzer_shared/src/messages/codes.dart'
|
|
show Message, LocatedMessage, messageWasmImportOrExportInUserCode;
|
|
import 'package:_js_interop_checks/js_interop_checks.dart';
|
|
import 'package:_js_interop_checks/src/js_interop.dart' as jsInteropHelper;
|
|
import 'package:_js_interop_checks/src/transformations/shared_interop_transformer.dart';
|
|
import 'package:front_end/src/api_prototype/const_conditional_simplifier.dart'
|
|
show ConstConditionalSimplifier;
|
|
import 'package:front_end/src/api_prototype/constant_evaluator.dart'
|
|
as constantEvaluator show ConstantEvaluator;
|
|
import 'package:kernel/ast.dart';
|
|
import 'package:kernel/class_hierarchy.dart';
|
|
import 'package:kernel/clone.dart';
|
|
import 'package:kernel/core_types.dart';
|
|
import 'package:kernel/reference_from_index.dart';
|
|
import 'package:kernel/target/changed_structure_notifier.dart';
|
|
import 'package:kernel/target/targets.dart';
|
|
import 'package:kernel/type_environment.dart';
|
|
import 'package:kernel/verifier.dart';
|
|
import 'package:vm/modular/transformations/ffi/common.dart' as ffiHelper
|
|
show calculateTransitiveImportsOfDartFfiIfUsed;
|
|
import 'package:vm/modular/transformations/ffi/definitions.dart'
|
|
as transformFfiDefinitions show transformLibraries;
|
|
import 'package:vm/modular/transformations/ffi/use_sites.dart'
|
|
as transformFfiUseSites show transformLibraries;
|
|
import 'package:vm/modular/transformations/mixin_full_resolution.dart'
|
|
as transformMixins show transformLibraries;
|
|
|
|
import 'await_transformer.dart' as awaitTrans;
|
|
import 'ffi_native_address_transformer.dart' as wasmFfiNativeAddressTrans;
|
|
import 'ffi_native_transformer.dart' as wasmFfiNativeTrans;
|
|
import 'records.dart' show RecordShape;
|
|
import 'transformers.dart' as wasmTrans;
|
|
|
|
enum Mode {
|
|
regular,
|
|
jsCompatibility,
|
|
}
|
|
|
|
class Dart2WasmConstantsBackend extends ConstantsBackend {
|
|
const Dart2WasmConstantsBackend();
|
|
|
|
@override
|
|
bool get supportsUnevaluatedConstants => true;
|
|
}
|
|
|
|
class ConstantResolver extends Transformer {
|
|
ConstantResolver(this.evaluator);
|
|
|
|
final constantEvaluator.ConstantEvaluator evaluator;
|
|
|
|
StaticTypeContext? _context;
|
|
|
|
@override
|
|
TreeNode visitLibrary(Library library) {
|
|
final oldContext = _context;
|
|
_context =
|
|
StaticTypeContext.forAnnotations(library, evaluator.typeEnvironment);
|
|
final result = super.visitLibrary(library);
|
|
_context = oldContext;
|
|
return result;
|
|
}
|
|
|
|
@override
|
|
TreeNode defaultMember(Member member) {
|
|
final oldContext = _context;
|
|
_context = StaticTypeContext(member, evaluator.typeEnvironment);
|
|
final result = super.defaultMember(member);
|
|
_context = oldContext;
|
|
return result;
|
|
}
|
|
|
|
@override
|
|
TreeNode visitConstantExpression(ConstantExpression node) {
|
|
final constant = node.constant;
|
|
if (constant is UnevaluatedConstant) {
|
|
final expression = constant.expression;
|
|
final newConstant = evaluator.evaluate(_context!, expression);
|
|
ConstantExpression result =
|
|
ConstantExpression(newConstant, node.getStaticType(_context!))
|
|
..fileOffset = node.fileOffset;
|
|
|
|
return result;
|
|
}
|
|
return node;
|
|
}
|
|
}
|
|
|
|
class WasmTarget extends Target {
|
|
WasmTarget(
|
|
{this.enableExperimentalFfi = true,
|
|
this.enableExperimentalWasmInterop = true,
|
|
this.removeAsserts = false,
|
|
this.mode = Mode.regular});
|
|
|
|
final bool removeAsserts;
|
|
final Mode mode;
|
|
final bool enableExperimentalFfi;
|
|
final bool enableExperimentalWasmInterop;
|
|
Class? _growableList;
|
|
Class? _immutableList;
|
|
Class? _wasmDefaultMap;
|
|
Class? _wasmDefaultSet;
|
|
Class? _wasmImmutableMap;
|
|
Class? _wasmImmutableSet;
|
|
Class? _jsString;
|
|
Class? _closure;
|
|
Class? _boxedInt;
|
|
Class? _boxedDouble;
|
|
Map<String, Class>? _nativeClasses;
|
|
|
|
@override
|
|
bool get enableNoSuchMethodForwarders => true;
|
|
|
|
@override
|
|
ConstantsBackend get constantsBackend => const Dart2WasmConstantsBackend();
|
|
|
|
@override
|
|
Verification get verification => const WasmVerification();
|
|
|
|
@override
|
|
String get name {
|
|
return switch (mode) {
|
|
Mode.regular => 'wasm',
|
|
Mode.jsCompatibility => 'wasm_js_compatibility'
|
|
};
|
|
}
|
|
|
|
String get platformFile {
|
|
return switch (mode) {
|
|
Mode.regular => 'dart2wasm_platform.dill',
|
|
Mode.jsCompatibility => 'dart2wasm_js_compatibility_platform.dill'
|
|
};
|
|
}
|
|
|
|
@override
|
|
TargetFlags get flags => TargetFlags();
|
|
|
|
@override
|
|
List<String> get extraRequiredLibraries => [
|
|
'dart:_boxed_bool',
|
|
'dart:_boxed_double',
|
|
'dart:_boxed_int',
|
|
'dart:_compact_hash',
|
|
'dart:_http',
|
|
'dart:_internal',
|
|
'dart:_js_helper',
|
|
'dart:_js_types',
|
|
'dart:_list',
|
|
'dart:_string',
|
|
'dart:_wasm',
|
|
'dart:async',
|
|
'dart:developer',
|
|
'dart:ffi',
|
|
'dart:io',
|
|
'dart:js_interop',
|
|
'dart:js_interop_unsafe',
|
|
'dart:js_util',
|
|
'dart:nativewrappers',
|
|
'dart:typed_data',
|
|
];
|
|
|
|
@override
|
|
List<String> get extraIndexedLibraries => [
|
|
'dart:_boxed_bool',
|
|
'dart:_boxed_double',
|
|
'dart:_boxed_int',
|
|
'dart:_compact_hash',
|
|
'dart:_error_utils',
|
|
'dart:_js_helper',
|
|
'dart:_js_types',
|
|
'dart:_list',
|
|
'dart:_string',
|
|
'dart:_wasm',
|
|
'dart:collection',
|
|
'dart:js_interop',
|
|
'dart:js_interop_unsafe',
|
|
'dart:js_util',
|
|
'dart:typed_data',
|
|
];
|
|
|
|
@override
|
|
bool mayDefineRestrictedType(Uri uri) => uri.isScheme('dart');
|
|
|
|
@override
|
|
bool allowPlatformPrivateLibraryAccess(Uri importer, Uri imported) {
|
|
if (super.allowPlatformPrivateLibraryAccess(importer, imported)) {
|
|
return true;
|
|
}
|
|
|
|
if (imported.toString() == 'dart:_wasm') {
|
|
return enableExperimentalWasmInterop;
|
|
}
|
|
|
|
final importerString = importer.toString();
|
|
|
|
// We have some tests that import dart:js*
|
|
if (importerString.contains('tests/web/wasm')) return true;
|
|
|
|
// Flutter's dart:ui is also package:ui (in test mode)
|
|
if (importerString.startsWith('package:ui/')) return true;
|
|
|
|
// package:js can import dart:js_util & dart:_js_*
|
|
if (importerString.startsWith('package:js/')) return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void _patchHostEndian(CoreTypes coreTypes) {
|
|
// Fix Endian.host to be a const field equal to Endian.little instead of
|
|
// a final field. Wasm is a little-endian platform.
|
|
// Can't use normal patching process for this because CFE does not
|
|
// support patching fields.
|
|
// See http://dartbug.com/32836 for the background.
|
|
final Field host =
|
|
coreTypes.index.getField('dart:typed_data', 'Endian', 'host');
|
|
final Field little =
|
|
coreTypes.index.getField('dart:typed_data', 'Endian', 'little');
|
|
host.isConst = true;
|
|
host.initializer = CloneVisitorNotMembers().clone(little.initializer!)
|
|
..parent = host;
|
|
}
|
|
|
|
void _performJSInteropTransformations(
|
|
Component component,
|
|
CoreTypes coreTypes,
|
|
ClassHierarchy hierarchy,
|
|
Set<Library> interopDependentLibraries,
|
|
DiagnosticReporter diagnosticReporter,
|
|
ReferenceFromIndex? referenceFromIndex) {
|
|
_nativeClasses ??= JsInteropChecks.getNativeClasses(component);
|
|
final jsInteropReporter = JsInteropDiagnosticReporter(
|
|
diagnosticReporter as DiagnosticReporter<Message, LocatedMessage>);
|
|
final jsInteropChecks = JsInteropChecks(
|
|
coreTypes, hierarchy, jsInteropReporter, _nativeClasses!,
|
|
isDart2Wasm: true, enableExperimentalFfi: enableExperimentalFfi);
|
|
// Process and validate first before doing anything with exports.
|
|
for (Library library in interopDependentLibraries) {
|
|
jsInteropChecks.visitLibrary(library);
|
|
}
|
|
final sharedInteropTransformer = SharedInteropTransformer(
|
|
TypeEnvironment(coreTypes, hierarchy),
|
|
jsInteropReporter,
|
|
jsInteropChecks.exportChecker,
|
|
jsInteropChecks.extensionIndex);
|
|
for (Library library in interopDependentLibraries) {
|
|
sharedInteropTransformer.visitLibrary(library);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void performPreConstantEvaluationTransformations(
|
|
Component component,
|
|
CoreTypes coreTypes,
|
|
List<Library> libraries,
|
|
DiagnosticReporter diagnosticReporter,
|
|
{void Function(String msg)? logger,
|
|
ChangedStructureNotifier? changedStructureNotifier}) {
|
|
_patchHostEndian(coreTypes);
|
|
}
|
|
|
|
@override
|
|
void performModularTransformationsOnLibraries(
|
|
Component component,
|
|
CoreTypes coreTypes,
|
|
ClassHierarchy hierarchy,
|
|
List<Library> libraries,
|
|
Map<String, String>? environmentDefines,
|
|
DiagnosticReporter diagnosticReporter,
|
|
ReferenceFromIndex? referenceFromIndex,
|
|
{void Function(String msg)? logger,
|
|
ChangedStructureNotifier? changedStructureNotifier}) {
|
|
if (!enableExperimentalWasmInterop) {
|
|
// Check `wasm:import` and `wasm:export` pragmas before FFI transforms as
|
|
// FFI transforms convert JS interop annotations to these pragmas.
|
|
_checkWasmImportExportPragmas(libraries, coreTypes,
|
|
diagnosticReporter as DiagnosticReporter<Message, LocatedMessage>);
|
|
}
|
|
|
|
Set<Library> transitiveImportingJSInterop = {
|
|
...jsInteropHelper.calculateTransitiveImportsOfJsInteropIfUsed(
|
|
component.libraries, Uri.parse("package:js/js.dart")),
|
|
...jsInteropHelper.calculateTransitiveImportsOfJsInteropIfUsed(
|
|
component.libraries, Uri.parse("dart:_js_annotations")),
|
|
...jsInteropHelper.calculateTransitiveImportsOfJsInteropIfUsed(
|
|
component.libraries, Uri.parse("dart:js_interop")),
|
|
...jsInteropHelper.calculateTransitiveImportsOfJsInteropIfUsed(
|
|
component.libraries, Uri.parse("dart:convert")),
|
|
...jsInteropHelper.calculateTransitiveImportsOfJsInteropIfUsed(
|
|
component.libraries, Uri.parse("dart:_string")),
|
|
};
|
|
if (transitiveImportingJSInterop.isEmpty) {
|
|
logger?.call("Skipped JS interop transformations");
|
|
} else {
|
|
_performJSInteropTransformations(component, coreTypes, hierarchy,
|
|
transitiveImportingJSInterop, diagnosticReporter, referenceFromIndex);
|
|
logger?.call("Transformed JS interop classes");
|
|
}
|
|
|
|
// If we are compiling with a null environment, skip constant resolution
|
|
// and simplification.
|
|
if (environmentDefines != null) {
|
|
void reportError(LocatedMessage message,
|
|
[List<LocatedMessage>? context]) {
|
|
diagnosticReporter.report(message.messageObject, message.charOffset,
|
|
message.length, message.uri);
|
|
if (context != null) {
|
|
for (final m in context) {
|
|
diagnosticReporter.report(
|
|
m.messageObject, m.charOffset, m.length, m.uri);
|
|
}
|
|
}
|
|
}
|
|
|
|
final simplifier = ConstConditionalSimplifier(
|
|
dartLibrarySupport,
|
|
constantsBackend,
|
|
component,
|
|
reportError,
|
|
environmentDefines: environmentDefines,
|
|
coreTypes: coreTypes,
|
|
classHierarchy: hierarchy,
|
|
removeAsserts: removeAsserts,
|
|
);
|
|
final evaluator = simplifier.constantEvaluator;
|
|
ConstantResolver(evaluator).transform(component);
|
|
simplifier.run();
|
|
}
|
|
|
|
transformMixins.transformLibraries(
|
|
this, coreTypes, hierarchy, libraries, referenceFromIndex);
|
|
logger?.call("Transformed mixin applications");
|
|
|
|
List<Library>? transitiveImportingDartFfi = ffiHelper
|
|
.calculateTransitiveImportsOfDartFfiIfUsed(component, libraries);
|
|
if (transitiveImportingDartFfi == null) {
|
|
logger?.call("Skipped ffi transformation");
|
|
} else {
|
|
wasmFfiNativeAddressTrans.transformLibraries(
|
|
component,
|
|
coreTypes,
|
|
hierarchy,
|
|
transitiveImportingDartFfi,
|
|
diagnosticReporter,
|
|
referenceFromIndex);
|
|
wasmFfiNativeTrans.transformLibraries(component, coreTypes, hierarchy,
|
|
transitiveImportingDartFfi, diagnosticReporter, referenceFromIndex);
|
|
transformFfiDefinitions.transformLibraries(
|
|
component,
|
|
coreTypes,
|
|
hierarchy,
|
|
transitiveImportingDartFfi,
|
|
diagnosticReporter,
|
|
referenceFromIndex,
|
|
changedStructureNotifier);
|
|
transformFfiUseSites.transformLibraries(component, coreTypes, hierarchy,
|
|
transitiveImportingDartFfi, diagnosticReporter, referenceFromIndex);
|
|
logger?.call("Transformed ffi annotations");
|
|
}
|
|
|
|
wasmTrans.transformLibraries(libraries, coreTypes, hierarchy);
|
|
|
|
awaitTrans.transformLibraries(libraries, hierarchy, coreTypes);
|
|
}
|
|
|
|
@override
|
|
void performTransformationsOnProcedure(
|
|
CoreTypes coreTypes,
|
|
ClassHierarchy hierarchy,
|
|
Procedure procedure,
|
|
Map<String, String>? environmentDefines,
|
|
{void Function(String msg)? logger}) {
|
|
wasmTrans.transformProcedure(procedure, coreTypes, hierarchy);
|
|
}
|
|
|
|
Expression _instantiateInvocation(
|
|
CoreTypes coreTypes, String name, Arguments arguments) {
|
|
if (name.startsWith("set:")) {
|
|
name = name.substring(4);
|
|
Procedure invocationSetter = coreTypes.invocationClass.procedures
|
|
.firstWhere((c) => c.name.text == "setter");
|
|
return StaticInvocation(invocationSetter,
|
|
Arguments([SymbolLiteral(name), arguments.positional.single]));
|
|
} else if (name.startsWith("get:")) {
|
|
name = name.substring(4);
|
|
Procedure invocationGetter = coreTypes.invocationClass.procedures
|
|
.firstWhere((c) => c.name.text == "getter");
|
|
return StaticInvocation(
|
|
invocationGetter, Arguments([SymbolLiteral(name)]));
|
|
} else if (arguments.types.isEmpty) {
|
|
Procedure invocationMethod = coreTypes.invocationClass.procedures
|
|
.firstWhere((c) => c.name.text == "method");
|
|
return StaticInvocation(
|
|
invocationMethod,
|
|
Arguments([
|
|
SymbolLiteral(name),
|
|
ListLiteral(arguments.positional),
|
|
MapLiteral(List<MapLiteralEntry>.from(
|
|
arguments.named.map((NamedExpression arg) {
|
|
return MapLiteralEntry(SymbolLiteral(arg.name), arg.value);
|
|
})), keyType: coreTypes.symbolNonNullableRawType)
|
|
..isConst = (arguments.named.isEmpty)
|
|
]));
|
|
} else {
|
|
Procedure invocationGenericMethod = coreTypes.invocationClass.procedures
|
|
.firstWhere((c) => c.name.text == "genericMethod");
|
|
return StaticInvocation(
|
|
invocationGenericMethod,
|
|
Arguments([
|
|
SymbolLiteral(name),
|
|
ListLiteral(arguments.types.map((t) => TypeLiteral(t)).toList()),
|
|
ListLiteral(arguments.positional),
|
|
MapLiteral(List<MapLiteralEntry>.from(
|
|
arguments.named.map((NamedExpression arg) {
|
|
return MapLiteralEntry(SymbolLiteral(arg.name), arg.value);
|
|
})), keyType: coreTypes.symbolNonNullableRawType)
|
|
..isConst = (arguments.named.isEmpty)
|
|
]));
|
|
}
|
|
}
|
|
|
|
@override
|
|
Expression instantiateInvocation(CoreTypes coreTypes, Expression receiver,
|
|
String name, Arguments arguments, int offset, bool isSuper) {
|
|
return _instantiateInvocation(coreTypes, name, arguments);
|
|
}
|
|
|
|
@override
|
|
Expression instantiateNoSuchMethodError(CoreTypes coreTypes,
|
|
Expression receiver, String name, Arguments arguments, int offset,
|
|
{bool isMethod = false,
|
|
bool isGetter = false,
|
|
bool isSetter = false,
|
|
bool isField = false,
|
|
bool isLocalVariable = false,
|
|
bool isDynamic = false,
|
|
bool isSuper = false,
|
|
bool isStatic = false,
|
|
bool isConstructor = false,
|
|
bool isTopLevel = false}) {
|
|
return StaticInvocation(
|
|
coreTypes.noSuchMethodErrorDefaultConstructor,
|
|
Arguments(
|
|
[receiver, _instantiateInvocation(coreTypes, name, arguments)]));
|
|
}
|
|
|
|
@override
|
|
bool get supportsSetLiterals => true;
|
|
|
|
@override
|
|
int get enabledLateLowerings => LateLowering.all;
|
|
|
|
@override
|
|
int get enabledConstructorTearOffLowerings => ConstructorTearOffLowering.all;
|
|
|
|
@override
|
|
bool get supportsExplicitGetterCalls => true;
|
|
|
|
@override
|
|
bool get supportsLateLoweringSentinel => false;
|
|
|
|
@override
|
|
bool get useStaticFieldLowering => false;
|
|
|
|
@override
|
|
Class concreteListLiteralClass(CoreTypes coreTypes) {
|
|
return _growableList ??=
|
|
coreTypes.index.getClass('dart:_list', 'GrowableList');
|
|
}
|
|
|
|
@override
|
|
Class concreteConstListLiteralClass(CoreTypes coreTypes) {
|
|
return _immutableList ??=
|
|
coreTypes.index.getClass('dart:_list', 'ImmutableList');
|
|
}
|
|
|
|
@override
|
|
Class concreteMapLiteralClass(CoreTypes coreTypes) {
|
|
return _wasmDefaultMap ??=
|
|
coreTypes.index.getClass('dart:_compact_hash', 'DefaultMap');
|
|
}
|
|
|
|
@override
|
|
Class concreteConstMapLiteralClass(CoreTypes coreTypes) {
|
|
return _wasmImmutableMap ??=
|
|
coreTypes.index.getClass('dart:_compact_hash', '_ConstMap');
|
|
}
|
|
|
|
@override
|
|
Class concreteSetLiteralClass(CoreTypes coreTypes) {
|
|
return _wasmDefaultSet ??=
|
|
coreTypes.index.getClass('dart:_compact_hash', 'DefaultSet');
|
|
}
|
|
|
|
@override
|
|
Class concreteConstSetLiteralClass(CoreTypes coreTypes) {
|
|
return _wasmImmutableSet ??=
|
|
coreTypes.index.getClass('dart:_compact_hash', '_ConstSet');
|
|
}
|
|
|
|
@override
|
|
Class concreteStringLiteralClass(CoreTypes coreTypes, String value) {
|
|
return _jsString ??=
|
|
coreTypes.index.getClass("dart:_string", "JSStringImpl");
|
|
}
|
|
|
|
// In dart2wasm we can't assume that `x == "hello"` means `x`'s class is
|
|
// `concreteStringLiteralClass("hello")`, it may also be `JSStringImpl` when
|
|
// it's obtained from a JS call, or `TwoByteString` when it's a substring of a
|
|
// `TwoByteString`.
|
|
@override
|
|
bool get canInferStringClassAfterEqualityComparison => false;
|
|
|
|
@override
|
|
Class concreteClosureClass(CoreTypes coreTypes) {
|
|
return _closure ??= coreTypes.index.getClass('dart:core', '_Closure');
|
|
}
|
|
|
|
@override
|
|
bool isSupportedPragma(String pragmaName) =>
|
|
pragmaName.startsWith("wasm:") || pragmaName.startsWith("dyn-module:");
|
|
|
|
late final Map<RecordShape, Class> recordClasses;
|
|
|
|
@override
|
|
Class getRecordImplementationClass(CoreTypes coreTypes,
|
|
int numPositionalFields, List<String> namedFields) =>
|
|
recordClasses[RecordShape(numPositionalFields, namedFields)]!;
|
|
|
|
@override
|
|
Class concreteIntLiteralClass(CoreTypes coreTypes, int value) =>
|
|
_boxedInt ??= coreTypes.index.getClass("dart:_boxed_int", "BoxedInt");
|
|
|
|
@override
|
|
Class concreteDoubleLiteralClass(CoreTypes coreTypes, double value) =>
|
|
_boxedDouble ??=
|
|
coreTypes.index.getClass("dart:_boxed_double", "BoxedDouble");
|
|
|
|
@override
|
|
DartLibrarySupport get dartLibrarySupport => CustomizedDartLibrarySupport(
|
|
unsupported: {if (!enableExperimentalFfi) 'ffi'});
|
|
}
|
|
|
|
class WasmVerification extends Verification {
|
|
const WasmVerification();
|
|
|
|
@override
|
|
bool allowNoFileOffset(VerificationStage stage, TreeNode node) {
|
|
if (super.allowNoFileOffset(stage, node)) {
|
|
return true;
|
|
}
|
|
if (stage >= VerificationStage.afterModularTransformations) {
|
|
// Allow synthesized classes, procedures, fields and casts.
|
|
// TODO(askesc): Improve the precision of these exceptions.
|
|
return node is Class ||
|
|
node is Constructor ||
|
|
node is Procedure ||
|
|
node is Field ||
|
|
node is AsExpression;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
final _dartCoreUri = Uri.parse('dart:core');
|
|
|
|
/// Check that `wasm:import` and `wasm:export` pragmas are only used in `dart:`
|
|
/// libraries and in tests, with the exception of
|
|
/// `reject_import_export_pragmas` test.
|
|
void _checkWasmImportExportPragmas(List<Library> libraries, CoreTypes coreTypes,
|
|
DiagnosticReporter<Message, LocatedMessage> diagnosticReporter) {
|
|
for (Library library in libraries) {
|
|
final importUri = library.importUri;
|
|
if (importUri.isScheme('dart') ||
|
|
(importUri.isScheme('package') &&
|
|
JsInteropChecks.allowedInteropLibrariesInDart2WasmPackages
|
|
.any((pkg) => importUri.pathSegments.first == pkg))) {
|
|
continue;
|
|
}
|
|
|
|
for (Member member in library.members) {
|
|
for (Expression annotation in member.annotations) {
|
|
if (annotation is! ConstantExpression) {
|
|
continue;
|
|
}
|
|
final annotationConstant = annotation.constant;
|
|
if (annotationConstant is! InstanceConstant) {
|
|
continue;
|
|
}
|
|
final cls = annotationConstant.classNode;
|
|
if (cls.name == 'pragma' &&
|
|
cls.enclosingLibrary.importUri == _dartCoreUri) {
|
|
final pragmaName = annotationConstant
|
|
.fieldValues[coreTypes.pragmaName.fieldReference];
|
|
if (pragmaName is StringConstant) {
|
|
if (pragmaName.value == 'wasm:import' ||
|
|
pragmaName.value == 'wasm:export') {
|
|
diagnosticReporter.report(
|
|
messageWasmImportOrExportInUserCode,
|
|
annotation.fileOffset,
|
|
0,
|
|
library.fileUri,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|