mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 16:19:07 +00:00
Short form install-tear-offs for common cases
Change-Id: Ic2f3827ad391ab38b1b3eb1e3460e5f8931d05c3 Reviewed-on: https://dart-review.googlesource.com/c/84053 Reviewed-by: Sigmund Cherem <sigmund@google.com> Commit-Queue: Stephen Adams <sra@google.com>
This commit is contained in:
parent
bc03bd3805
commit
8d5d8342af
|
@ -447,8 +447,9 @@ abstract class DartMethod extends Method {
|
|||
// this field holds a function computing the function signature.
|
||||
final js.Expression functionType;
|
||||
|
||||
// Signature information for this method. This is only required and stored
|
||||
// here if the method [canBeApplied].
|
||||
// Signature information for this method. [optionalParameterDefaultValues] is
|
||||
// only required and stored here if the method [canBeApplied]. The count is
|
||||
// always stored to help select specialized tear-off paths.
|
||||
final int requiredParameterCount;
|
||||
final /* Map | List */ optionalParameterDefaultValues;
|
||||
|
||||
|
|
|
@ -919,7 +919,7 @@ class ProgramBuilder {
|
|||
int applyIndex = 0;
|
||||
if (canBeApplied) {
|
||||
optionalParameterDefaultValues = _computeParameterDefaultValues(method);
|
||||
if (element.parameterStructure.typeParameters > 0) {
|
||||
if (parameterStructure.typeParameters > 0) {
|
||||
applyIndex = 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -312,22 +312,62 @@ function updateHolder(holder, newHolder) {
|
|||
return holder;
|
||||
}
|
||||
|
||||
// TODO(sra): Minify properties of 'hunkHelpers'.
|
||||
var #hunkHelpers = {
|
||||
inherit: inherit,
|
||||
inheritMany: inheritMany,
|
||||
mixin: mixin,
|
||||
installStaticTearOff: installStaticTearOff,
|
||||
installInstanceTearOff: installInstanceTearOff,
|
||||
makeConstList: makeConstList,
|
||||
lazy: lazy,
|
||||
updateHolder: updateHolder,
|
||||
convertToFastObject: convertToFastObject,
|
||||
setFunctionNamesIfNecessary: setFunctionNamesIfNecessary,
|
||||
updateTypes: updateTypes,
|
||||
setOrUpdateInterceptorsByTag: setOrUpdateInterceptorsByTag,
|
||||
setOrUpdateLeafTags: setOrUpdateLeafTags,
|
||||
};
|
||||
var #hunkHelpers = (function(){
|
||||
var mkInstance = function(
|
||||
isIntercepted, requiredParameterCount, optionalParameterDefaultValues,
|
||||
callNames, applyIndex) {
|
||||
return function(container, getterName, name, funType) {
|
||||
return installInstanceTearOff(
|
||||
container, getterName, isIntercepted,
|
||||
requiredParameterCount, optionalParameterDefaultValues,
|
||||
callNames, [name], funType, applyIndex);
|
||||
}
|
||||
},
|
||||
|
||||
mkStatic = function(
|
||||
requiredParameterCount, optionalParameterDefaultValues,
|
||||
callNames, applyIndex) {
|
||||
return function(container, getterName, name, funType) {
|
||||
return installStaticTearOff(
|
||||
container, getterName,
|
||||
requiredParameterCount, optionalParameterDefaultValues,
|
||||
callNames, [name], funType, applyIndex);
|
||||
}
|
||||
};
|
||||
|
||||
// TODO(sra): Minify properties of 'hunkHelpers'.
|
||||
return {
|
||||
inherit: inherit,
|
||||
inheritMany: inheritMany,
|
||||
mixin: mixin,
|
||||
installStaticTearOff: installStaticTearOff,
|
||||
installInstanceTearOff: installInstanceTearOff,
|
||||
|
||||
// Unintercepted methods.
|
||||
_instance_0u: mkInstance(0, 0, null, [#call0selector], 0),
|
||||
_instance_1u: mkInstance(0, 1, null, [#call1selector], 0),
|
||||
_instance_2u: mkInstance(0, 2, null, [#call2selector], 0),
|
||||
|
||||
// Intercepted methods.
|
||||
_instance_0i: mkInstance(1, 0, null, [#call0selector], 0),
|
||||
_instance_1i: mkInstance(1, 1, null, [#call1selector], 0),
|
||||
_instance_2i: mkInstance(1, 2, null, [#call2selector], 0),
|
||||
|
||||
// Static methods.
|
||||
_static_0: mkStatic(0, null, [#call0selector], 0),
|
||||
_static_1: mkStatic(1, null, [#call1selector], 0),
|
||||
_static_2: mkStatic(2, null, [#call2selector], 0),
|
||||
|
||||
makeConstList: makeConstList,
|
||||
lazy: lazy,
|
||||
updateHolder: updateHolder,
|
||||
convertToFastObject: convertToFastObject,
|
||||
setFunctionNamesIfNecessary: setFunctionNamesIfNecessary,
|
||||
updateTypes: updateTypes,
|
||||
setOrUpdateInterceptorsByTag: setOrUpdateInterceptorsByTag,
|
||||
setOrUpdateLeafTags: setOrUpdateLeafTags,
|
||||
};
|
||||
})();
|
||||
|
||||
// Every deferred hunk (i.e. fragment) is a function that we can invoke to
|
||||
// initialize it. At this moment it contributes its data to the main hunk.
|
||||
|
@ -535,6 +575,14 @@ class FragmentEmitter {
|
|||
final ModelEmitter modelEmitter;
|
||||
final JClosedWorld _closedWorld;
|
||||
|
||||
js.Name _call0Name, _call1Name, _call2Name;
|
||||
js.Name get call0Name =>
|
||||
_call0Name ??= namer.getNameForJsGetName(null, JsGetName.CALL_PREFIX0);
|
||||
js.Name get call1Name =>
|
||||
_call1Name ??= namer.getNameForJsGetName(null, JsGetName.CALL_PREFIX1);
|
||||
js.Name get call2Name =>
|
||||
_call2Name ??= namer.getNameForJsGetName(null, JsGetName.CALL_PREFIX2);
|
||||
|
||||
FragmentEmitter(this.compiler, this.namer, this.backend, this.constantEmitter,
|
||||
this.modelEmitter, this._closedWorld);
|
||||
|
||||
|
@ -603,6 +651,10 @@ class FragmentEmitter {
|
|||
compiler.codegenWorldBuilder, _closedWorld.nativeData, namer) ??
|
||||
new js.EmptyStatement(),
|
||||
'invokeMain': fragment.invokeMain,
|
||||
|
||||
'call0selector': js.quoteName(call0Name),
|
||||
'call1selector': js.quoteName(call1Name),
|
||||
'call2selector': js.quoteName(call2Name),
|
||||
});
|
||||
if (program.hasSoftDeferredClasses) {
|
||||
return new js.Block([
|
||||
|
@ -998,9 +1050,7 @@ class FragmentEmitter {
|
|||
|
||||
// Closures taking exactly one argument are common.
|
||||
properties.add(js.Property(
|
||||
js.string(namer.callCatchAllName),
|
||||
js.quoteName(
|
||||
namer.getNameForJsGetName(null, JsGetName.CALL_PREFIX1))));
|
||||
js.string(namer.callCatchAllName), js.quoteName(call1Name)));
|
||||
properties.add(
|
||||
js.Property(js.string(namer.requiredParameterField), js.number(1)));
|
||||
|
||||
|
@ -1126,7 +1176,7 @@ class FragmentEmitter {
|
|||
List<js.Statement> inheritCalls = [];
|
||||
List<js.Statement> mixinCalls = [];
|
||||
// local caches of functions to allow minifaction of function name in call.
|
||||
Map<String, js.Expression> locals = {};
|
||||
LocalAliases locals = LocalAliases();
|
||||
|
||||
Set<Class> classesInFragment = Set();
|
||||
for (Library library in fragment.libraries) {
|
||||
|
@ -1154,11 +1204,12 @@ class FragmentEmitter {
|
|||
for (Class cls in library.classes) {
|
||||
if (cls.isSoftDeferred != softDeferred) continue;
|
||||
collect(cls);
|
||||
|
||||
if (cls.mixinClass != null) {
|
||||
locals['_mixin'] ??= js.js('hunkHelpers.mixin');
|
||||
mixinCalls.add(js.js.statement('_mixin(#, #)',
|
||||
[classReference(cls), classReference(cls.mixinClass)]));
|
||||
mixinCalls.add(js.js.statement('#(#, #)', [
|
||||
locals.find('_mixin', 'hunkHelpers.mixin'),
|
||||
classReference(cls),
|
||||
classReference(cls.mixinClass),
|
||||
]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1169,35 +1220,30 @@ class FragmentEmitter {
|
|||
? new js.LiteralNull()
|
||||
: classReference(superclass);
|
||||
if (list.length == 1) {
|
||||
locals['_inherit'] ??= js.js('hunkHelpers.inherit');
|
||||
inheritCalls.add(js.js.statement('_inherit(#, #)',
|
||||
[classReference(list.single), superclassReference]));
|
||||
inheritCalls.add(js.js.statement('#(#, #)', [
|
||||
locals.find('_inherit', 'hunkHelpers.inherit'),
|
||||
classReference(list.single),
|
||||
superclassReference
|
||||
]));
|
||||
} else {
|
||||
locals['_inheritMany'] ??= js.js('hunkHelpers.inheritMany');
|
||||
var listElements = list.map(classReference).toList();
|
||||
inheritCalls.add(js.js.statement('_inheritMany(#, #)',
|
||||
[superclassReference, js.ArrayInitializer(listElements)]));
|
||||
inheritCalls.add(js.js.statement('#(#, #)', [
|
||||
locals.find('_inheritMany', 'hunkHelpers.inheritMany'),
|
||||
superclassReference,
|
||||
js.ArrayInitializer(listElements)
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
List<js.Statement> statements = [];
|
||||
if (locals.isNotEmpty) {
|
||||
statements.add(localAliasInitializations(locals));
|
||||
statements.add(locals.toStatement());
|
||||
}
|
||||
statements.addAll(inheritCalls);
|
||||
statements.addAll(mixinCalls);
|
||||
return wrapPhase('inheritance', statements);
|
||||
}
|
||||
|
||||
js.Statement localAliasInitializations(Map<String, js.Expression> locals) {
|
||||
List<js.VariableInitialization> initializations = [];
|
||||
locals.forEach((local, value) {
|
||||
initializations
|
||||
.add(js.VariableInitialization(js.VariableDeclaration(local), value));
|
||||
});
|
||||
return js.ExpressionStatement(js.VariableDeclarationList(initializations));
|
||||
}
|
||||
|
||||
/// Emits the setup of method aliases.
|
||||
///
|
||||
/// This step consists of simply copying JavaScript functions to their
|
||||
|
@ -1275,8 +1321,7 @@ class FragmentEmitter {
|
|||
/// Emits the section that installs tear-off getters.
|
||||
js.Statement emitInstallTearOffs(Fragment fragment,
|
||||
{bool softDeferred = false}) {
|
||||
var aliasForInstallStaticTearOff;
|
||||
var aliasForInstallInstanceTearOff;
|
||||
LocalAliases locals = LocalAliases();
|
||||
|
||||
/// Emits the statement that installs a tear off for a method.
|
||||
///
|
||||
|
@ -1332,12 +1377,37 @@ class FragmentEmitter {
|
|||
var applyIndex = js.number(method.applyIndex);
|
||||
|
||||
if (method.isStatic) {
|
||||
aliasForInstallStaticTearOff ??= '_static';
|
||||
if (requiredParameterCount <= 2 &&
|
||||
callNames.length == 1 &&
|
||||
optionalParameterDefaultValues is js.LiteralNull &&
|
||||
method.applyIndex == 0) {
|
||||
js.Statement finish(int arity) {
|
||||
// Short form for exactly 0/1/2 arguments.
|
||||
var install =
|
||||
locals.find('_static_${arity}', 'hunkHelpers._static_${arity}');
|
||||
return js.js.statement('''
|
||||
#install(#container, #getterName, #name, #funType)''', {
|
||||
"install": install,
|
||||
"container": container,
|
||||
"getterName": js.quoteName(method.tearOffName),
|
||||
"name": funsOrNames.single,
|
||||
"funType": method.functionType,
|
||||
});
|
||||
}
|
||||
|
||||
var installedName = callNames.single;
|
||||
if (installedName == call0Name) return finish(0);
|
||||
if (installedName == call1Name) return finish(1);
|
||||
if (installedName == call2Name) return finish(2);
|
||||
}
|
||||
|
||||
var install =
|
||||
locals.find('_static', 'hunkHelpers.installStaticTearOff');
|
||||
return js.js.statement('''
|
||||
#install(#container, #getterName,
|
||||
#requiredParameterCount, #optionalParameterDefaultValues,
|
||||
#callNames, #funsOrNames, #funType, #applyIndex)''', {
|
||||
"install": aliasForInstallStaticTearOff,
|
||||
#callNames, #funsOrNames, #funType, #applyIndex)''', {
|
||||
"install": install,
|
||||
"container": container,
|
||||
"getterName": js.quoteName(method.tearOffName),
|
||||
"requiredParameterCount": js.number(requiredParameterCount),
|
||||
|
@ -1348,12 +1418,38 @@ class FragmentEmitter {
|
|||
"applyIndex": applyIndex,
|
||||
});
|
||||
} else {
|
||||
aliasForInstallInstanceTearOff ??= '_instance';
|
||||
if (requiredParameterCount <= 2 &&
|
||||
callNames.length == 1 &&
|
||||
optionalParameterDefaultValues is js.LiteralNull &&
|
||||
method.applyIndex == 0) {
|
||||
js.Statement finish(int arity) {
|
||||
// Short form for exactly 0/1/2 arguments.
|
||||
String isInterceptedTag = isIntercepted ? 'i' : 'u';
|
||||
var install = locals.find('_instance_${arity}_${isInterceptedTag}',
|
||||
'hunkHelpers._instance_${arity}${isInterceptedTag}');
|
||||
return js.js.statement('''
|
||||
#install(#container, #getterName, #name, #funType)''', {
|
||||
"install": install,
|
||||
"container": container,
|
||||
"getterName": js.quoteName(method.tearOffName),
|
||||
"name": funsOrNames.single,
|
||||
"funType": method.functionType,
|
||||
});
|
||||
}
|
||||
|
||||
var installedName = callNames.single;
|
||||
if (installedName == call0Name) return finish(0);
|
||||
if (installedName == call1Name) return finish(1);
|
||||
if (installedName == call2Name) return finish(2);
|
||||
}
|
||||
|
||||
var install =
|
||||
locals.find('_instance', 'hunkHelpers.installInstanceTearOff');
|
||||
return js.js.statement('''
|
||||
#install(#container, #getterName, #isIntercepted,
|
||||
#requiredParameterCount, #optionalParameterDefaultValues,
|
||||
#callNames, #funsOrNames, #funType, #applyIndex)''', {
|
||||
"install": aliasForInstallInstanceTearOff,
|
||||
"install": install,
|
||||
"container": container,
|
||||
"getterName": js.quoteName(method.tearOffName),
|
||||
// 'Truthy' values are ok for `isIntercepted`.
|
||||
|
@ -1402,20 +1498,8 @@ class FragmentEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
List<js.VariableInitialization> locals = [];
|
||||
if (aliasForInstallStaticTearOff != null) {
|
||||
locals.add(js.VariableInitialization(
|
||||
js.VariableDeclaration(aliasForInstallStaticTearOff),
|
||||
js.js('hunkHelpers.installStaticTearOff')));
|
||||
}
|
||||
if (aliasForInstallInstanceTearOff != null) {
|
||||
locals.add(js.VariableInitialization(
|
||||
js.VariableDeclaration(aliasForInstallInstanceTearOff),
|
||||
js.js('hunkHelpers.installInstanceTearOff')));
|
||||
}
|
||||
if (locals.isNotEmpty) {
|
||||
inits.insert(
|
||||
0, js.ExpressionStatement(js.VariableDeclarationList(locals)));
|
||||
inits.insert(0, locals.toStatement());
|
||||
}
|
||||
|
||||
return wrapPhase('installTearOffs', inits);
|
||||
|
@ -1471,11 +1555,11 @@ class FragmentEmitter {
|
|||
js.Statement emitLazilyInitializedStatics(Fragment fragment) {
|
||||
List<StaticField> fields = fragment.staticLazilyInitializedFields;
|
||||
List<js.Statement> statements = [];
|
||||
Map<String, js.Expression> locals = {};
|
||||
LocalAliases locals = LocalAliases();
|
||||
for (StaticField field in fields) {
|
||||
assert(field.holder.isStaticStateHolder);
|
||||
locals['_lazy'] ??= js.js('hunkHelpers.lazy');
|
||||
statements.add(js.js.statement("_lazy(#, #, #, #);", [
|
||||
statements.add(js.js.statement("#(#, #, #, #);", [
|
||||
locals.find('_lazy', 'hunkHelpers.lazy'),
|
||||
field.holder.name,
|
||||
js.quoteName(field.name),
|
||||
js.quoteName(namer.deriveLazyInitializerName(field.name)),
|
||||
|
@ -1484,7 +1568,7 @@ class FragmentEmitter {
|
|||
}
|
||||
|
||||
if (locals.isNotEmpty) {
|
||||
statements.insert(0, localAliasInitializations(locals));
|
||||
statements.insert(0, locals.toStatement());
|
||||
}
|
||||
|
||||
return wrapPhase('lazyInitializers', statements);
|
||||
|
@ -1760,6 +1844,27 @@ class FragmentEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
class LocalAliases {
|
||||
final Map<String, js.Expression> _locals = {};
|
||||
|
||||
bool get isEmpty => _locals.isEmpty;
|
||||
bool get isNotEmpty => !isEmpty;
|
||||
|
||||
String find(String alias, String expression) {
|
||||
_locals[alias] ??= js.js(expression);
|
||||
return alias;
|
||||
}
|
||||
|
||||
js.Statement toStatement() {
|
||||
List<js.VariableInitialization> initializations = [];
|
||||
_locals.forEach((local, value) {
|
||||
initializations
|
||||
.add(js.VariableInitialization(js.VariableDeclaration(local), value));
|
||||
});
|
||||
return js.ExpressionStatement(js.VariableDeclarationList(initializations));
|
||||
}
|
||||
}
|
||||
|
||||
/// Code to initialize holder with ancillary information.
|
||||
class HolderCode {
|
||||
final List<Holder> activeHolders;
|
||||
|
|
Loading…
Reference in a new issue