[package:js] Add static interop stub for outlines

Fixes b/235393918

@staticInterop replaces factories with a new named node, a static
method. In order to persist this transformation in modular
compilation, this needs to be done to outlines that can then be
consumed by the source library. In order to allow erasure at the
time of 'performOutlineTransformations', coreTypes is added to that
API.

Change-Id: I90d17fff8bbe143982fcd12cfb06dc3e8d58781a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/247928
Commit-Queue: Srujan Gaddam <srujzs@google.com>
Reviewed-by: Joshua Litt <joshualitt@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
Srujan Gaddam
2022-06-17 19:58:49 +00:00
committed by Commit Bot
parent f364733555
commit 459d57c8b8
9 changed files with 87 additions and 27 deletions
@@ -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);
}
}
@@ -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,
+22 -13
View File
@@ -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<Library> 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<Library> 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);
}
}
@@ -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,
@@ -188,7 +188,8 @@ Future<CompilerResult> _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).
+2 -1
View File
@@ -396,7 +396,8 @@ Future<ComputeKernelResult> computeKernel(List<String> 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));
+6 -5
View File
@@ -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<Library> libraries = new List.of(component.libraries);
@@ -6,5 +6,5 @@ import 'static_interop.dart';
void main() {
setUp();
var staticJs = StaticJSClass.factory();
var staticJs = StaticJSClass.factory(StaticJSClass());
}
@@ -14,7 +14,7 @@ external void eval(String code);
@staticInterop
class StaticJSClass {
external StaticJSClass();
factory StaticJSClass.factory() {
factory StaticJSClass.factory(StaticJSClass _) {
return StaticJSClass();
}
}