mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 14:49:43 +00:00
Support sharing function signatures in deferred parts for fast startup
Change-Id: I2ee08817241512269fe04d7fb0e3367df847d37a Reviewed-on: https://dart-review.googlesource.com/56940 Commit-Queue: Johnni Winther <johnniwinther@google.com> Reviewed-by: Stephen Adams <sra@google.com>
This commit is contained in:
parent
ae9f5d2a1b
commit
9d9eff44c9
|
@ -115,8 +115,7 @@ class TokenCounter extends BaseVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO(28763): Remove `<dynamic>` when issue 28763 is fixed.
|
||||
void countTokens(Node node) => node.accept<dynamic>(this);
|
||||
void countTokens(Node node) => node.accept(this);
|
||||
}
|
||||
|
||||
abstract class ReferenceCountedAstNode implements Node {
|
||||
|
|
|
@ -1646,6 +1646,10 @@ class Namer {
|
|||
|
||||
String get futureOrTypeTag => r'type';
|
||||
|
||||
// The name of the variable used to offset function signatures in deferred
|
||||
// parts with the fast-startup emitter.
|
||||
String get typesOffsetName => r'typesOffset';
|
||||
|
||||
Map<FunctionType, jsAst.Name> functionTypeNameMap =
|
||||
new HashMap<FunctionType, jsAst.Name>();
|
||||
|
||||
|
|
|
@ -65,6 +65,8 @@ class _BoundMetadataEntry extends _MetadataEntry {
|
|||
}
|
||||
|
||||
int compareTo(covariant _MetadataEntry other) => other._rc - this._rc;
|
||||
|
||||
String toString() => '_BoundMetadataEntry($hashCode,rc=$_rc,_value=$_value)';
|
||||
}
|
||||
|
||||
class _MetadataList extends jsAst.DeferredExpression {
|
||||
|
|
|
@ -149,6 +149,7 @@ class RuntimeTypeGenerator {
|
|||
// potentially a subtype of a checked function. Currently we eagerly
|
||||
// generate a function type index or signature for all callable classes.
|
||||
jsAst.Expression functionTypeIndex;
|
||||
bool isDeferred = false;
|
||||
if (!type.containsTypeVariables) {
|
||||
// TODO(sigmund): use output unit of [method] when the classes mentioned
|
||||
// in [type] aren't in the main output unit. (Issue #31032)
|
||||
|
@ -156,6 +157,12 @@ class RuntimeTypeGenerator {
|
|||
if (_outputUnitVisitor.isTypeContainedIn(type, mainOutputUnit)) {
|
||||
functionTypeIndex =
|
||||
emitterTask.metadataCollector.reifyType(type, mainOutputUnit);
|
||||
} else if (!storeFunctionTypeInMetadata) {
|
||||
// TODO(johnniwinther): Support sharing deferred signatures with the
|
||||
// full emitter.
|
||||
isDeferred = true;
|
||||
functionTypeIndex = emitterTask.metadataCollector
|
||||
.reifyType(type, _outputUnitData.outputUnitForMember(method));
|
||||
}
|
||||
}
|
||||
if (storeFunctionTypeInMetadata && functionTypeIndex != null) {
|
||||
|
@ -168,7 +175,18 @@ class RuntimeTypeGenerator {
|
|||
// The signature function isn't live.
|
||||
return;
|
||||
}
|
||||
encoding = functionTypeIndex ?? encoding;
|
||||
if (functionTypeIndex != null) {
|
||||
if (isDeferred) {
|
||||
// The function type index must be offset by the number of types
|
||||
// already loaded.
|
||||
encoding = new jsAst.Binary(
|
||||
'+',
|
||||
new jsAst.VariableUse(_namer.typesOffsetName),
|
||||
functionTypeIndex);
|
||||
} else {
|
||||
encoding = functionTypeIndex;
|
||||
}
|
||||
}
|
||||
} else if (encoding == null) {
|
||||
// Generate the signature on the fly. This is only supported for
|
||||
// Dart 1.
|
||||
|
|
|
@ -264,11 +264,12 @@ function setOrUpdateLeafTags(newTags) {
|
|||
// Updates the types embedded global.
|
||||
function updateTypes(newTypes) {
|
||||
var types = #embeddedTypes;
|
||||
// This relies on the fact that types are added *after* the tear-offs have
|
||||
// been installed. The tear-off function uses the types-length to figure
|
||||
// out at which offset its types are located. If the types were added earlier
|
||||
// the offset would be wrong.
|
||||
var length = types.length;
|
||||
// The tear-off function uses another 'typesOffset' value cached in
|
||||
// [initializeDeferredHunk] so [updateTypes] can be called either before of
|
||||
// after the tearoffs have been installed.
|
||||
types.push.apply(types, newTypes);
|
||||
return length;
|
||||
}
|
||||
|
||||
// Updates the given holder with the properties of the [newHolder].
|
||||
|
@ -401,7 +402,9 @@ const String directAccessTestExpression = r'''
|
|||
/// However, at specific moments they need to contribute their data.
|
||||
/// For example, once the holders have been created, they are included into
|
||||
/// the main holders.
|
||||
const String deferredBoilerplate = '''
|
||||
///
|
||||
/// This template is used for Dart 1.
|
||||
const String deferredBoilerplateDart1 = '''
|
||||
function(inherit, mixin, lazy, makeConstList, convertToFastObject,
|
||||
installTearOff, setFunctionNamesIfNecessary, updateHolder, updateTypes,
|
||||
setOrUpdateInterceptorsByTag, setOrUpdateLeafTags,
|
||||
|
@ -443,6 +446,59 @@ updateTypes(#types);
|
|||
}''';
|
||||
|
||||
/// Soft-deferred fragments are built similarly to the main fragment.
|
||||
|
||||
/// Deferred fragments (aka 'hunks') are built similarly to the main fragment.
|
||||
///
|
||||
/// However, at specific moments they need to contribute their data.
|
||||
/// For example, once the holders have been created, they are included into
|
||||
/// the main holders.
|
||||
///
|
||||
/// This template is used for Dart 2.
|
||||
const String deferredBoilerplateDart2 = '''
|
||||
function(inherit, mixin, lazy, makeConstList, convertToFastObject,
|
||||
installTearOff, setFunctionNamesIfNecessary, updateHolder, updateTypes,
|
||||
setOrUpdateInterceptorsByTag, setOrUpdateLeafTags,
|
||||
#embeddedGlobalsObject, holdersList, #staticState) {
|
||||
|
||||
// Builds the holders. They only contain the data for new holders.
|
||||
#holders;
|
||||
|
||||
// If the name is not set on the functions, do it now.
|
||||
setFunctionNamesIfNecessary(#deferredHoldersList);
|
||||
|
||||
// Updates the holders of the main-fragment. Uses the provided holdersList to
|
||||
// access the main holders.
|
||||
// The local holders are replaced by the combined holders. This is necessary
|
||||
// for the inheritance setup below.
|
||||
#updateHolders;
|
||||
// Sets the prototypes of the new classes.
|
||||
#prototypes;
|
||||
// Add signature function types and compute the types offset in `init.types`.
|
||||
// These can only refer to regular classes and in Dart 2 only closures have
|
||||
// function types so the `typesOffset` has been safely computed before it's
|
||||
// referred in the signatures of the `closures` below.
|
||||
var #typesOffset = updateTypes(#types);
|
||||
#closures;
|
||||
// Sets aliases of methods (on the prototypes of classes).
|
||||
#aliases;
|
||||
// Installs the tear-offs of functions.
|
||||
#tearOffs;
|
||||
// Builds the inheritance structure.
|
||||
#inheritance;
|
||||
|
||||
// Instantiates all constants of this deferred fragment.
|
||||
// Note that the constant-holder has been updated earlier and storing the
|
||||
// constant values in the constant-holder makes them available globally.
|
||||
#constants;
|
||||
// Initializes the static non-final fields (with their constant values).
|
||||
#staticNonFinalFields;
|
||||
// Creates lazy getters for statics that must run initializers on first access.
|
||||
#lazyStatics;
|
||||
|
||||
// Native-support uses setOrUpdateInterceptorsByTag and setOrUpdateLeafTags.
|
||||
#nativeSupport;
|
||||
}''';
|
||||
|
||||
///
|
||||
/// However, they don't contribute anything to global namespace, but just
|
||||
/// initialize existing classes. For example, they update the inheritance
|
||||
|
@ -589,27 +645,51 @@ class FragmentEmitter {
|
|||
'#holder = updateHolder(holdersList[#index], #holder)',
|
||||
{'index': js.number(i), 'holder': new js.VariableUse(holder.name)}));
|
||||
}
|
||||
|
||||
// TODO(floitsch): don't just reference 'init'.
|
||||
return js.js(deferredBoilerplate, {
|
||||
'embeddedGlobalsObject': new js.Parameter('init'),
|
||||
'staticState': new js.Parameter(namer.staticStateHolder),
|
||||
'holders': emitHolders(holders, fragment),
|
||||
'deferredHoldersList': new js.ArrayInitializer(nonStaticStateHolders
|
||||
.map((holder) => js.js("#", holder.name))
|
||||
.toList(growable: false)),
|
||||
'updateHolders': new js.Block(updateHolderAssignments),
|
||||
'prototypes': emitPrototypes(fragment),
|
||||
'inheritance': emitInheritance(fragment),
|
||||
'aliases': emitInstanceMethodAliases(fragment),
|
||||
'tearOffs': emitInstallTearOffs(fragment),
|
||||
'constants': emitConstants(fragment),
|
||||
'staticNonFinalFields': emitStaticNonFinalFields(fragment),
|
||||
'lazyStatics': emitLazilyInitializedStatics(fragment),
|
||||
'types': deferredTypes,
|
||||
// TODO(floitsch): only call emitNativeSupport if we need native.
|
||||
'nativeSupport': emitNativeSupport(fragment),
|
||||
});
|
||||
if (compiler.options.strongMode) {
|
||||
// TODO(floitsch): don't just reference 'init'.
|
||||
return js.js(deferredBoilerplateDart2, {
|
||||
'embeddedGlobalsObject': new js.Parameter('init'),
|
||||
'staticState': new js.Parameter(namer.staticStateHolder),
|
||||
'holders': emitHolders(holders, fragment),
|
||||
'deferredHoldersList': new js.ArrayInitializer(nonStaticStateHolders
|
||||
.map((holder) => js.js("#", holder.name))
|
||||
.toList(growable: false)),
|
||||
'updateHolders': new js.Block(updateHolderAssignments),
|
||||
'prototypes': emitPrototypes(fragment, includeClosures: false),
|
||||
'closures': emitPrototypes(fragment, includeClosures: true),
|
||||
'inheritance': emitInheritance(fragment),
|
||||
'aliases': emitInstanceMethodAliases(fragment),
|
||||
'tearOffs': emitInstallTearOffs(fragment),
|
||||
'constants': emitConstants(fragment),
|
||||
'staticNonFinalFields': emitStaticNonFinalFields(fragment),
|
||||
'lazyStatics': emitLazilyInitializedStatics(fragment),
|
||||
'types': deferredTypes,
|
||||
// TODO(floitsch): only call emitNativeSupport if we need native.
|
||||
'nativeSupport': emitNativeSupport(fragment),
|
||||
'typesOffset': namer.typesOffsetName,
|
||||
});
|
||||
} else {
|
||||
// TODO(floitsch): don't just reference 'init'.
|
||||
return js.js(deferredBoilerplateDart1, {
|
||||
'embeddedGlobalsObject': new js.Parameter('init'),
|
||||
'staticState': new js.Parameter(namer.staticStateHolder),
|
||||
'holders': emitHolders(holders, fragment),
|
||||
'deferredHoldersList': new js.ArrayInitializer(nonStaticStateHolders
|
||||
.map((holder) => js.js("#", holder.name))
|
||||
.toList(growable: false)),
|
||||
'updateHolders': new js.Block(updateHolderAssignments),
|
||||
'prototypes': emitPrototypes(fragment),
|
||||
'inheritance': emitInheritance(fragment),
|
||||
'aliases': emitInstanceMethodAliases(fragment),
|
||||
'tearOffs': emitInstallTearOffs(fragment),
|
||||
'constants': emitConstants(fragment),
|
||||
'staticNonFinalFields': emitStaticNonFinalFields(fragment),
|
||||
'lazyStatics': emitLazilyInitializedStatics(fragment),
|
||||
'types': deferredTypes,
|
||||
// TODO(floitsch): only call emitNativeSupport if we need native.
|
||||
'nativeSupport': emitNativeSupport(fragment),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Emits all holders, except for the static-state holder.
|
||||
|
@ -766,11 +846,25 @@ class FragmentEmitter {
|
|||
///
|
||||
/// This section updates the prototype-property of all constructors in the
|
||||
/// global holders.
|
||||
js.Statement emitPrototypes(Fragment fragment, {softDeferred = false}) {
|
||||
///
|
||||
/// [softDeferred] determine whether prototypes for soft deferred classes are
|
||||
/// generated.
|
||||
///
|
||||
/// If [includeClosures] is `true` only prototypes for closure classes are
|
||||
/// generated, if [includeClosures] is `false` only prototypes for non-closure
|
||||
/// classes are generated. Otherwise prototypes for all classes are generated.
|
||||
js.Statement emitPrototypes(Fragment fragment,
|
||||
{bool softDeferred = false, bool includeClosures}) {
|
||||
List<js.Statement> assignments = fragment.libraries
|
||||
.expand((Library library) => library.classes)
|
||||
.where((Class cls) => cls.isSoftDeferred == softDeferred)
|
||||
.map((Class cls) {
|
||||
.where((Class cls) {
|
||||
if (includeClosures != null) {
|
||||
if (cls.element.isClosure != includeClosures) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return cls.isSoftDeferred == softDeferred;
|
||||
}).map((Class cls) {
|
||||
var proto = js.js.statement(
|
||||
'#.prototype = #;', [classReference(cls), emitPrototype(cls)]);
|
||||
ClassEntity element = cls.element;
|
||||
|
|
|
@ -14,9 +14,11 @@ main() async {
|
|||
Expect.isFalse(lib1.method1() is String Function(String));
|
||||
Expect.isTrue(lib1.method5 is Object Function(Null, String, int));
|
||||
Expect.isFalse(lib1.method5 is Object Function(Null, int, String));
|
||||
Expect.isTrue(lib1.test5(lib1.method5));
|
||||
await lib2.loadLibrary();
|
||||
Expect.isFalse(lib2.method2() is int Function(int));
|
||||
Expect.isTrue(lib2.method2() is String Function(String));
|
||||
Expect.isFalse(lib2.method6 is Object Function(Null, String, int));
|
||||
Expect.isTrue(lib2.method6 is Object Function(Null, int, String));
|
||||
Expect.isTrue(lib2.test6(lib2.method6));
|
||||
}
|
||||
|
|
|
@ -14,9 +14,11 @@ main() async {
|
|||
Expect.isTrue(lib2.method2() is String Function(String));
|
||||
Expect.isFalse(lib2.method6 is Object Function(Null, String, int));
|
||||
Expect.isTrue(lib2.method6 is Object Function(Null, int, String));
|
||||
Expect.isTrue(lib2.test6(lib2.method6));
|
||||
await lib1.loadLibrary();
|
||||
Expect.isTrue(lib1.method1() is int Function(int));
|
||||
Expect.isFalse(lib1.method1() is String Function(String));
|
||||
Expect.isTrue(lib1.method5 is Object Function(Null, String, int));
|
||||
Expect.isFalse(lib1.method5 is Object Function(Null, int, String));
|
||||
Expect.isTrue(lib1.test5(lib1.method5));
|
||||
}
|
||||
|
|
|
@ -15,3 +15,5 @@ method3() {
|
|||
test3(o) => o is Class1 Function(Class1);
|
||||
|
||||
method5(Class1 c, String s, int i) {}
|
||||
|
||||
test5(o) => o is Function(Class1, String, int);
|
||||
|
|
|
@ -15,3 +15,5 @@ method4() {
|
|||
test4(o) => o is Class2 Function(Class2, Class2);
|
||||
|
||||
method6(Class2 c, int i, String s) {}
|
||||
|
||||
test6(o) => o is Function(Class2, int, String);
|
||||
|
|
Loading…
Reference in a new issue