diff --git a/pkg/_js_interop_checks/lib/src/transformations/static_interop_class_eraser.dart b/pkg/_js_interop_checks/lib/src/transformations/static_interop_class_eraser.dart index b499ca5c991..c98694ee831 100644 --- a/pkg/_js_interop_checks/lib/src/transformations/static_interop_class_eraser.dart +++ b/pkg/_js_interop_checks/lib/src/transformations/static_interop_class_eraser.dart @@ -28,6 +28,7 @@ class StaticInteropClassEraser extends Transformer { final Class _javaScriptObject; final CloneVisitorNotMembers _cloner = CloneVisitorNotMembers(); late final _TypeSubstitutor _typeSubstitutor; + late Library currLibrary; StaticInteropClassEraser(CoreTypes coreTypes, {String libraryForJavaScriptObject = 'dart:_interceptors', @@ -41,9 +42,10 @@ class StaticInteropClassEraser extends Transformer { '${factoryTarget.name}|staticInteropFactoryStub'; /// Either finds or creates a static method stub to replace factories with a - /// body in a static interop class. + /// body with in a static interop class. /// - /// Modifies [factoryTarget]'s enclosing class to include the new method. + /// Modifies [factoryTarget]'s enclosing class to include the new method if we + /// create one. Procedure _findOrCreateFactoryStub(Procedure factoryTarget) { assert(factoryTarget.isFactory); var factoryClass = factoryTarget.enclosingClass!; @@ -52,6 +54,12 @@ class StaticInteropClassEraser extends Transformer { var stubs = factoryClass.procedures .where((procedure) => procedure.name.text == stubName); if (stubs.isEmpty) { + // We should only create the stub if we're processing the library in which + // the stub should exist. Any static invocation of the factory that + // doesn't exist in the same library as the factory should be processed + // after the library in which the factory exists. In modular compilation, + // the outline of that library should already contain the needed stub. + assert(factoryClass.enclosingLibrary == currLibrary); // Note that the return type of the cloned function is transformed. var functionNode = super .visitFunctionNode(_cloner.cloneInContext(factoryTarget.function)) @@ -68,6 +76,12 @@ class StaticInteropClassEraser extends Transformer { } } + @override + TreeNode visitLibrary(Library node) { + currLibrary = node; + return super.visitLibrary(node); + } + @override TreeNode visitConstructor(Constructor node) { if (hasStaticInteropAnnotation(node.enclosingClass)) { @@ -158,9 +172,10 @@ class StaticInteropClassEraser extends Transformer { // case where we visit the factory later. Also note that a cast is not // needed since the static method already has its type erased. var args = super.visitArguments(node.arguments) as Arguments; - return StaticInvocation(_findOrCreateFactoryStub(factoryTarget), args, - isConst: node.isConst) - ..fileOffset = node.fileOffset; + var stub = _findOrCreateFactoryStub(factoryTarget); + return StaticInvocation(stub, args, isConst: node.isConst) + ..fileOffset = node.fileOffset + ..targetReference = stub.reference; } else { // Add a cast so that the result gets typed as `JavaScriptObject`. var newInvocation = super.visitStaticInvocation(node) as Expression; @@ -182,3 +197,23 @@ class StaticInteropClassEraser extends Transformer { return substitutedType != null ? substitutedType : type; } } + +/// Used to create stubs for factories when computing outlines. +/// +/// These stubs can then be used in downstream dependencies in modular +/// compilation. +class StaticInteropStubCreator extends RecursiveVisitor { + final StaticInteropClassEraser _eraser; + StaticInteropStubCreator(this._eraser); + + @override + void visitLibrary(Library node) { + _eraser.currLibrary = node; + super.visitLibrary(node); + } + + @override + void visitProcedure(Procedure node) { + _eraser.visitProcedure(node); + } +} diff --git a/pkg/compiler/lib/src/kernel/dart2js_target.dart b/pkg/compiler/lib/src/kernel/dart2js_target.dart index 5b51960304b..df7624c5f39 100644 --- a/pkg/compiler/lib/src/kernel/dart2js_target.dart +++ b/pkg/compiler/lib/src/kernel/dart2js_target.dart @@ -137,6 +137,13 @@ class Dart2jsTarget extends Target { @override bool get errorOnUnexactWebIntLiterals => true; + @override + void performOutlineTransformations( + ir.Component component, CoreTypes coreTypes) { + component + .accept(StaticInteropStubCreator(StaticInteropClassEraser(coreTypes))); + } + @override void performModularTransformationsOnLibraries( ir.Component component, diff --git a/pkg/dart2wasm/lib/target.dart b/pkg/dart2wasm/lib/target.dart index 12b44085a2c..3be12c723fd 100644 --- a/pkg/dart2wasm/lib/target.dart +++ b/pkg/dart2wasm/lib/target.dart @@ -81,6 +81,21 @@ class WasmTarget extends Target { ..parent = host; } + StaticInteropClassEraser _staticInteropClassEraser(CoreTypes coreTypes) => + StaticInteropClassEraser(coreTypes, + libraryForJavaScriptObject: 'dart:_js_helper', + classNameOfJavaScriptObject: 'JSValue'); + + void _performJSInteropTransformations(CoreTypes coreTypes, + ClassHierarchy hierarchy, List interopDependentLibraries) { + final jsUtilOptimizer = JsUtilWasmOptimizer(coreTypes, hierarchy); + final staticInteropClassEraser = _staticInteropClassEraser(coreTypes); + for (Library library in interopDependentLibraries) { + jsUtilOptimizer.visitLibrary(library); + staticInteropClassEraser.visitLibrary(library); + } + } + @override void performPreConstantEvaluationTransformations( Component component, @@ -92,6 +107,12 @@ class WasmTarget extends Target { _patchHostEndian(coreTypes); } + @override + void performOutlineTransformations(Component component, CoreTypes coreTypes) { + component + .accept(StaticInteropStubCreator(_staticInteropClassEraser(coreTypes))); + } + @override void performModularTransformationsOnLibraries( Component component, @@ -109,7 +130,7 @@ class WasmTarget extends Target { if (transitiveImportingJSInterop == null) { logger?.call("Skipped JS interop transformations"); } else { - performJSInteropTransformations( + _performJSInteropTransformations( coreTypes, hierarchy, transitiveImportingJSInterop); logger?.call("Transformed JS interop classes"); } @@ -242,15 +263,3 @@ class WasmTarget extends Target { @override bool isSupportedPragma(String pragmaName) => pragmaName.startsWith("wasm:"); } - -void performJSInteropTransformations(CoreTypes coreTypes, - ClassHierarchy hierarchy, List interopDependentLibraries) { - final jsUtilOptimizer = JsUtilWasmOptimizer(coreTypes, hierarchy); - final staticInteropClassEraser = StaticInteropClassEraser(coreTypes, - libraryForJavaScriptObject: 'dart:_js_helper', - classNameOfJavaScriptObject: 'JSValue'); - for (Library library in interopDependentLibraries) { - jsUtilOptimizer.visitLibrary(library); - staticInteropClassEraser.visitLibrary(library); - } -} diff --git a/pkg/dev_compiler/lib/src/kernel/target.dart b/pkg/dev_compiler/lib/src/kernel/target.dart index 0a3e3ff965f..fa648bb5da7 100644 --- a/pkg/dev_compiler/lib/src/kernel/target.dart +++ b/pkg/dev_compiler/lib/src/kernel/target.dart @@ -152,6 +152,12 @@ class DevCompilerTarget extends Target { @override bool get enableNoSuchMethodForwarders => true; + @override + void performOutlineTransformations(Component component, CoreTypes coreTypes) { + component + .accept(StaticInteropStubCreator(StaticInteropClassEraser(coreTypes))); + } + @override void performModularTransformationsOnLibraries( Component component, diff --git a/pkg/front_end/lib/src/kernel_generator_impl.dart b/pkg/front_end/lib/src/kernel_generator_impl.dart index b8eb49e46d5..0a41e53deca 100644 --- a/pkg/front_end/lib/src/kernel_generator_impl.dart +++ b/pkg/front_end/lib/src/kernel_generator_impl.dart @@ -188,7 +188,8 @@ Future _buildInternal( // summaries without building a full component (at this time, that's // the only need we have for these transformations). if (!buildComponent) { - options.target.performOutlineTransformations(trimmedSummaryComponent); + options.target.performOutlineTransformations( + trimmedSummaryComponent, kernelTarget.loader.coreTypes); options.ticker.logMs("Transformed outline"); } // Don't include source (but do add it above to include importUris). diff --git a/pkg/frontend_server/lib/compute_kernel.dart b/pkg/frontend_server/lib/compute_kernel.dart index 918c891827e..d9334e38f01 100644 --- a/pkg/frontend_server/lib/compute_kernel.dart +++ b/pkg/frontend_server/lib/compute_kernel.dart @@ -396,7 +396,8 @@ Future computeKernel(List args, incrementalComponent.problemsAsJson = null; incrementalComponent.setMainMethodAndMode( null, true, incrementalComponent.mode); - target.performOutlineTransformations(incrementalComponent); + target.performOutlineTransformations( + incrementalComponent, incrementalCompilerResult.coreTypes!); makeStable(incrementalComponent); return Future.value(fe.serializeComponent(incrementalComponent, includeSources: false, includeOffsets: false)); diff --git a/pkg/kernel/lib/target/targets.dart b/pkg/kernel/lib/target/targets.dart index 9c46dbb2678..ced147d512f 100644 --- a/pkg/kernel/lib/target/targets.dart +++ b/pkg/kernel/lib/target/targets.dart @@ -318,7 +318,8 @@ abstract class Target { /// transformation is not applied when compiling full kernel programs to /// prevent affecting the internal invariants of the compiler and accidentally /// slowing down compilation. - void performOutlineTransformations(Component component) {} + void performOutlineTransformations( + Component component, CoreTypes coreTypes) {} /// Perform target-specific transformations on the given libraries that must /// run before constant evaluation. @@ -1010,8 +1011,8 @@ class TargetWrapper extends Target { } @override - void performOutlineTransformations(Component component) { - _target.performOutlineTransformations(component); + void performOutlineTransformations(Component component, CoreTypes coreTypes) { + _target.performOutlineTransformations(component, coreTypes); } @override @@ -1076,8 +1077,8 @@ mixin SummaryMixin on Target { bool get excludeNonSources; @override - void performOutlineTransformations(Component component) { - super.performOutlineTransformations(component); + void performOutlineTransformations(Component component, CoreTypes coreTypes) { + super.performOutlineTransformations(component, coreTypes); if (!excludeNonSources) return; List libraries = new List.of(component.libraries); diff --git a/tests/modular/static_interop_erasure/main.dart b/tests/modular/static_interop_erasure/main.dart index 80ccc3b7c7f..da54aa94c8f 100644 --- a/tests/modular/static_interop_erasure/main.dart +++ b/tests/modular/static_interop_erasure/main.dart @@ -6,5 +6,5 @@ import 'static_interop.dart'; void main() { setUp(); - var staticJs = StaticJSClass.factory(); + var staticJs = StaticJSClass.factory(StaticJSClass()); } diff --git a/tests/modular/static_interop_erasure/static_interop.dart b/tests/modular/static_interop_erasure/static_interop.dart index a5c93c43bbc..7f25a798c4f 100644 --- a/tests/modular/static_interop_erasure/static_interop.dart +++ b/tests/modular/static_interop_erasure/static_interop.dart @@ -14,7 +14,7 @@ external void eval(String code); @staticInterop class StaticJSClass { external StaticJSClass(); - factory StaticJSClass.factory() { + factory StaticJSClass.factory(StaticJSClass _) { return StaticJSClass(); } }