[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:
committed by
Commit Bot
parent
f364733555
commit
459d57c8b8
@@ -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,
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user