[dart2wasm] Enable dynamic module validation.
This change requires some modifications to the dart2wasm flags for
dynamic module support. Previously, we determined that dynamic module
support was needed if `--dynamic-main-module` was passed. We determined
that the main module was being compiled if `--dynamic-interface-uri` was
also passed, and a submodule (AKA "dynamic module") otherwise. This
design prevented us from passing the interface specification to the
dynamic module validator when submodules were being compiled.
Instead, we now build with dynamic support when the
`--dynamic-module-type` flag is passed. Allowable values are `main` and
`submodule`. Both the main module URI and interface URI are currently
required for both dynamic module types. (The main module URI could be
made optional if we generated a default filename like we do for dynamic
module metadata.)
Dynamic interface validation is enabled by default and is controlled by
the `--validate-dynamic-modules` flag. This flag can be negated via
`--validate-dynamic-modules=false` or `--no-validate-dynamic-modules`.
Change-Id: I3165c3a8255205a61c3ccfe546f5d436472ed0d9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/425154
Reviewed-by: Nate Biggs <natebiggs@google.com>
Reviewed-by: Ömer Ağacan <omersa@google.com>
diff --git a/pkg/dart2wasm/lib/compile.dart b/pkg/dart2wasm/lib/compile.dart
index 93c8410..11b60b2 100644
--- a/pkg/dart2wasm/lib/compile.dart
+++ b/pkg/dart2wasm/lib/compile.dart
@@ -177,17 +177,24 @@
final dynamicModuleMainUri = await resolveUri(options.dynamicModuleMainUri);
final dynamicInterfaceUri = await resolveUri(options.dynamicInterfaceUri);
final isDynamicMainModule =
- dynamicModuleMainUri != null && dynamicInterfaceUri != null;
- final isDynamicModule =
- dynamicModuleMainUri != null && dynamicInterfaceUri == null;
- if (isDynamicModule) {
- compilerOptions.additionalDills.add(dynamicModuleMainUri);
+ options.dynamicModuleType == DynamicModuleType.main;
+ final isDynamicSubmodule =
+ options.dynamicModuleType == DynamicModuleType.submodule;
+ if (isDynamicSubmodule) {
+ compilerOptions.additionalDills.add(dynamicModuleMainUri!);
+
+ if (options.validateDynamicModules) {
+ // We must pass the unresolved URI here to be compatible with the CFE
+ // dynamic interface validator.
+ compilerOptions.dynamicInterfaceSpecificationUri =
+ options.dynamicInterfaceUri;
+ }
}
CompilerResult? compilerResult;
try {
compilerResult = await kernelForProgram(options.mainUri, compilerOptions,
- requireMain: !isDynamicModule);
+ requireMain: !isDynamicSubmodule);
} catch (e, s) {
return CFECrashError(e, s);
}
@@ -215,19 +222,19 @@
coreTypes,
classHierarchy);
- if (isDynamicModule) {
+ if (isDynamicSubmodule) {
// Join the dynamic module libraries with the TFAed component from the main
// module compilation. JS interop transformer must be run before this since
// some methods it uses may have been tree-shaken from the TFAed component.
(component, jsInteropMethods) = await generateDynamicModuleComponent(
- component, coreTypes, dynamicModuleMainUri, jsInteropMethods);
+ component, coreTypes, dynamicModuleMainUri!, jsInteropMethods);
coreTypes = CoreTypes(component);
classHierarchy =
ClassHierarchy(component, coreTypes) as ClosedWorldClassHierarchy;
libraryIndex = LibraryIndex(component, _librariesToIndex);
}
- final librariesToTransform = isDynamicModule
+ final librariesToTransform = isDynamicSubmodule
? component.getDynamicModuleLibraries(coreTypes)
: component.libraries;
final constantEvaluator = ConstantEvaluator(
@@ -238,7 +245,7 @@
final Map<RecordShape, Class> recordClasses = generateRecordClasses(
component, coreTypes,
isDynamicMainModule: isDynamicMainModule,
- isDynamicModule: isDynamicModule);
+ isDynamicModule: isDynamicSubmodule);
target.recordClasses = recordClasses;
if (options.dumpKernelBeforeTfa != null) {
@@ -258,11 +265,11 @@
moduleStrategy = DynamicMainModuleStrategy(
component,
coreTypes,
- File.fromUri(dynamicInterfaceUri).readAsStringSync(),
+ File.fromUri(dynamicInterfaceUri!).readAsStringSync(),
options.dynamicInterfaceUri!);
- } else if (isDynamicModule) {
+ } else if (isDynamicSubmodule) {
moduleStrategy = DynamicModuleStrategy(
- component, options, target, coreTypes, dynamicModuleMainUri);
+ component, options, target, coreTypes, dynamicModuleMainUri!);
} else {
moduleStrategy = DefaultModuleStrategy(component);
}
@@ -272,17 +279,17 @@
MainModuleMetadata mainModuleMetadata =
MainModuleMetadata.empty(options.translatorOptions, options.environment);
- if (isDynamicModule) {
+ if (isDynamicSubmodule) {
mainModuleMetadata =
await deserializeMainModuleMetadata(component, options);
mainModuleMetadata.verifyDynamicModuleOptions(options);
} else if (isDynamicMainModule) {
MainModuleMetadata.verifyMainModuleOptions(options);
- await serializeMainModuleComponent(component, dynamicModuleMainUri,
+ await serializeMainModuleComponent(component, dynamicModuleMainUri!,
optimized: false);
}
- if (!isDynamicModule) {
+ if (!isDynamicSubmodule) {
_patchMainTearOffs(coreTypes, component);
// Keep the flags in-sync with
@@ -307,7 +314,7 @@
var translator = Translator(component, coreTypes, libraryIndex, recordClasses,
moduleOutputData, options.translatorOptions,
mainModuleMetadata: mainModuleMetadata,
- enableDynamicModules: dynamicModuleMainUri != null);
+ enableDynamicModules: options.enableDynamicModules);
String? depFile = options.depFile;
if (depFile != null) {
@@ -332,7 +339,7 @@
final jsRuntimeFinalizer = js.RuntimeFinalizer(jsInteropMethods);
- final jsRuntime = isDynamicModule
+ final jsRuntime = isDynamicSubmodule
? jsRuntimeFinalizer.generateDynamicModule(
translator.functions.translatedProcedures,
translator.internalizedStringsForJSRuntime)
@@ -347,7 +354,7 @@
final supportJs = _generateSupportJs(options.translatorOptions);
if (isDynamicMainModule) {
await serializeMainModuleMetadata(component, translator, options);
- await serializeMainModuleComponent(component, dynamicModuleMainUri,
+ await serializeMainModuleComponent(component, dynamicModuleMainUri!,
optimized: true);
}
diff --git a/pkg/dart2wasm/lib/compiler_options.dart b/pkg/dart2wasm/lib/compiler_options.dart
index da74fdc8..a3e2987 100644
--- a/pkg/dart2wasm/lib/compiler_options.dart
+++ b/pkg/dart2wasm/lib/compiler_options.dart
@@ -4,6 +4,7 @@
import 'package:front_end/src/api_unstable/vm.dart' as fe;
+import 'dynamic_modules.dart' show DynamicModuleType;
import 'translator.dart';
class WasmCompilerOptions {
@@ -15,9 +16,11 @@
Uri mainUri;
String outputFile;
String? depFile;
+ DynamicModuleType? dynamicModuleType;
Uri? dynamicModuleMainUri;
Uri? dynamicInterfaceUri;
Uri? dynamicModuleMetadataFile;
+ bool validateDynamicModules = true;
Map<String, String> environment = {};
Map<fe.ExperimentalFlag, bool> feExperimentalFlags = const {};
String? multiRootScheme;
@@ -32,11 +35,25 @@
WasmCompilerOptions({required this.mainUri, required this.outputFile});
+ bool get enableDynamicModules => dynamicModuleType != null;
+
void validate() {
if (translatorOptions.importSharedMemory &&
translatorOptions.sharedMemoryMaxPages == null) {
throw ArgumentError("--shared-memory-max-pages must be specified if "
"--import-shared-memory is used.");
}
+
+ if (enableDynamicModules) {
+ if (dynamicModuleMainUri == null) {
+ throw ArgumentError("--dynamic-module-main must be specified if "
+ "compiling dynamic modules.");
+ }
+
+ if (dynamicInterfaceUri == null) {
+ throw ArgumentError("--dynamic-module-interface must be specified if "
+ "compiling dynamic modules.");
+ }
+ }
}
}
diff --git a/pkg/dart2wasm/lib/dart2wasm.dart b/pkg/dart2wasm/lib/dart2wasm.dart
index 9138d76..ab438b2 100644
--- a/pkg/dart2wasm/lib/dart2wasm.dart
+++ b/pkg/dart2wasm/lib/dart2wasm.dart
@@ -8,6 +8,7 @@
import 'package:front_end/src/api_unstable/vm.dart' show resolveInputUri;
import 'package:front_end/src/api_unstable/vm.dart' as fe;
+import 'dynamic_modules.dart' show DynamicModuleType;
import 'generate_wasm.dart';
import 'option.dart';
@@ -103,23 +104,31 @@
Flag("enable-multi-module-stress-test-mode",
(o, value) => o.translatorOptions.enableMultiModuleStressTestMode = value,
defaultsTo: _d.translatorOptions.enableMultiModuleStressTestMode),
+
// Flags for dynamic modules
- // The modified dill file produced by the main module compilation for dynamic
- // modules. Providing this and not "dynamic-module-interface" indicates that
- // this is a compilation of a dynamic module. The dill will contain the AST
- // for the main module as well as some annotations to help identify entities
- // when compiling dynamic modules.
+ StringOption("dynamic-module-type",
+ (o, value) => o.dynamicModuleType = DynamicModuleType.parse(value)),
+
+ // The modified dill file to be output by the main module compilation for
+ // dynamic modules. The dill will contain the AST for the main module as well
+ // as some annotations to help identify entities when compiling dynamic
+ // modules.
UriOption(
"dynamic-module-main", (o, value) => o.dynamicModuleMainUri = value),
+
// A yaml file describing the interface of the main module accessible from
- // dynamic modules. Providing this indicates to the dart2wasm that the module
- // produced should support dynamic modules.
+ // dynamic modules.
UriOption(
"dynamic-module-interface", (o, value) => o.dynamicInterfaceUri = value),
+
// A binary metadata file produced by the main module compilation for dynamic
// modules.
UriOption("dynamic-module-metadata",
(o, value) => o.dynamicModuleMetadataFile = value),
+
+ Flag("validate-dynamic-modules",
+ (o, value) => o.validateDynamicModules = value,
+ defaultsTo: true, negatable: true),
];
Map<fe.ExperimentalFlag, bool> processFeExperimentalFlags(
diff --git a/pkg/dart2wasm/lib/dynamic_modules.dart b/pkg/dart2wasm/lib/dynamic_modules.dart
index 2282d27..d310257 100644
--- a/pkg/dart2wasm/lib/dynamic_modules.dart
+++ b/pkg/dart2wasm/lib/dynamic_modules.dart
@@ -36,6 +36,17 @@
const String _mainModLibPragma = 'wasm:mainMod';
const String _dynamicModuleEntryPointName = '\$invokeEntryPoint';
+enum DynamicModuleType {
+ main,
+ submodule;
+
+ static DynamicModuleType parse(String s) => switch (s) {
+ "main" => main,
+ "submodule" => submodule,
+ _ => throw ArgumentError("Unrecognized dynamic module type $s."),
+ };
+}
+
extension DynamicModuleComponent on Component {
static final Expando<Procedure> _dynamicModuleEntryPoint =
Expando<Procedure>();
diff --git a/pkg/dynamic_modules/test/runner/dart2wasm.dart b/pkg/dynamic_modules/test/runner/dart2wasm.dart
index 794fb221..46c20f6 100644
--- a/pkg/dynamic_modules/test/runner/dart2wasm.dart
+++ b/pkg/dynamic_modules/test/runner/dart2wasm.dart
@@ -65,10 +65,10 @@
// This is required while binaryen lacks support for partially closed
// world optimizations.
'-O0',
+ '--extra-compiler-option=--dynamic-module-type=${isMain ? "main" : "submodule"}',
'--extra-compiler-option=--dynamic-module-main=main.dart.dill',
- if (isMain)
- '--extra-compiler-option=--dynamic-module-interface='
- '$rootScheme:/data/$testName/dynamic_interface.yaml',
+ '--extra-compiler-option=--dynamic-module-interface='
+ '$rootScheme:/data/$testName/dynamic_interface.yaml',
'$rootScheme:/data/$testName/$source',
'$source.wasm',
];