Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(749)

Unified Diff: pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart

Issue 1229923005: dart2js: support tear-offs in the startup emitter. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Fix bad assert. Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
index da22a2f6ed25555194fdf6b4a2f7073fb4aaa6e9..1ec4838ad21de392ff03ed030a82804fc0a28102 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
@@ -112,7 +112,56 @@ function makeConstList(list) {
return list;
}
-// TODO(floitsch): provide code for tear-offs.
+// This variable is used by the tearOffCode to guarantee unique functions per
+// tear-offs.
+var functionCounter = 0;
+#tearOffCode;
+
+// Each deferred hunk comes with its own types which are added to the end
+// of the types-array.
+// The `funTypes` passed to the `installTearOff` function below is relative to
+// the hunk the function comes from. The `typesOffset` variable encodes the
+// offset at which the new types will be added.
+var typesOffset = 0;
+
+// Adapts the stored data, so it's suitable for a tearOff call.
+//
+// Stores the tear-off getter-function in the [container]'s [getterName]
+// property.
+//
+// The [container] is either a class (that is, its prototype), or the holder for
+// static functions.
+//
+// The argument [funsOrNames] is an array of strings or functions. If it is a
+// name, then the function should be fetched from the container. The first
+// entry in that array *must* be a string.
+//
+// TODO(floitsch): Change tearOffCode to accept the data directly, or create a
+// different tearOffCode?
+function installTearOff(
+ container, getterName, isStatic, isIntercepted, requiredParameterCount,
+ optionalParameterDefaultValues, callNames, funsOrNames, funType) {
+ var funs = [];
+ for (var i = 0; i < funsOrNames.length; i++) {
+ var fun = funsOrNames[i];
+ if ((typeof fun) == 'string') fun = container[fun];
+ fun.#callName = callNames[i];
+ funs.push(fun);
+ }
+
+ funs[0][#argumentCount] = requiredParameterCount;
+ funs[0][#defaultArgumentValues] = optionalParameterDefaultValues;
+ var reflectionInfo = funType;
+ if (typeof reflectionInfo == "number") {
+ // The reflectionInfo can either be a function, or a pointer into the types
+ // table. If it points into the types-table we need to update the index,
+ // in case the tear-off is part of a deferred hunk.
+ reflectionInfo = reflectionInfo + typesOffset;
+ }
+ var name = funsOrNames[0];
+ container[getterName] =
+ tearOff(funs, reflectionInfo, isStatic, name, isIntercepted);
+}
// Instead of setting the interceptor tags directly we use this update
// function. This makes it easier for deferred fragments to contribute to the
@@ -141,6 +190,10 @@ 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.
types.push.apply(types, newTypes);
}
@@ -159,6 +212,9 @@ function updateHolder(holder, newHolder) {
// 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.
function initializeDeferredHunk(hunk) {
+ // Update the typesOffset for the next deferred library.
+ typesOffset = #embeddedTypes.length;
+
// TODO(floitsch): extend natives.
hunk(inherit, mixin, lazy, makeConstList, installTearOff,
updateHolder, updateTypes, updateInterceptorsByTag, updateLeafTags,
@@ -286,6 +342,7 @@ class FragmentEmitter {
'cyclicThrow': backend.emitter.staticFunctionAccess(
backend.getCyclicThrowHelper()),
'operatorIsPrefix': js.string(namer.operatorIsPrefix),
+ 'tearOffCode': new js.Block(buildTearOffCode(backend)),
'embeddedTypes': generateEmbeddedGlobalAccess(TYPES),
'embeddedInterceptorTags':
generateEmbeddedGlobalAccess(INTERCEPTORS_BY_TAG),
@@ -427,13 +484,15 @@ class FragmentEmitter {
Map<js.Name, js.Expression> emitStaticMethod(StaticMethod method) {
Map<js.Name, js.Expression> jsMethods = <js.Name, js.Expression>{};
- jsMethods[method.name] = method.code;
- // TODO(floitsch): can there be anything else than a StaticDartMethod?
- if (method is StaticDartMethod) {
- for (ParameterStubMethod stubMethod in method.parameterStubs) {
- jsMethods[stubMethod.name] = stubMethod.code;
+ // We don't need to install stub-methods. They can only be used when there
+ // are tear-offs, in which case they are emitted there.
+ assert(() {
+ if (method is StaticDartMethod) {
+ return method.needsTearOff || method.parameterStubs.isEmpty;
}
- }
+ return true;
+ });
+ jsMethods[method.name] = method.code;
return jsMethods;
}
@@ -488,7 +547,7 @@ class FragmentEmitter {
Iterable<Method> noSuchMethodStubs = cls.noSuchMethodStubs;
Iterable<Method> gettersSetters = generateGettersSetters(cls);
Iterable<Method> allMethods =
- [methods, isChecks, callStubs, typeVariableReaderStubs,
+ [methods, isChecks, callStubs, typeVariableReaderStubs,
noSuchMethodStubs, gettersSetters].expand((x) => x);
List<js.Property> properties = <js.Property>[];
@@ -634,9 +693,118 @@ class FragmentEmitter {
return new js.Block(assignments);
}
+ /// Encodes the optional default values so that the runtime Function.apply
+ /// can use them.
+ js.Expression _encodeOptionalParameterDefaultValues(DartMethod method) {
+ // TODO(herhut): Replace [js.LiteralNull] with [js.ArrayHole].
+ if (method.optionalParameterDefaultValues is List) {
+ List<ConstantValue> defaultValues = method.optionalParameterDefaultValues;
+ Iterable<js.Expression> elements =
+ defaultValues.map(generateConstantReference);
+ return new js.ArrayInitializer(elements.toList());
+ } else {
+ Map<String, ConstantValue> defaultValues =
+ method.optionalParameterDefaultValues;
+ List<js.Property> properties = <js.Property>[];
+ defaultValues.forEach((String name, ConstantValue value) {
+ properties.add(new js.Property(js.string(name),
+ generateConstantReference(value)));
+ });
+ return new js.ObjectInitializer(properties);
+ }
+ }
+
+ /// Emits the statement that installs a tear off for a method.
+ ///
+ /// Tear-offs might be passed to `Function.apply` which means that all
+ /// calling-conventions (with or without optional positional/named arguments)
+ /// are possible. As such, the tear-off needs enough information to fill in
+ /// missing parameters.
+ js.Statement emitInstallTearOff(js.Expression container, DartMethod method) {
+ List<js.Name> callNames = <js.Name>[];
+ List<js.Expression> funsOrNames = <js.Expression>[];
+
+ /// Adds the stub-method's code or name to the [funsOrNames] array.
+ ///
+ /// Static methods don't need stub-methods except for tear-offs. As such,
+ /// they are not emitted in the prototype, but directly passed here.
+ ///
+ /// Instance-methods install the stub-methods in their prototype, and we
+ /// use string-based redirections to find them there.
+ void addFunOrName(StubMethod stubMethod) {
+ if (method.isStatic) {
+ funsOrNames.add(stubMethod.code);
+ } else {
+ funsOrNames.add(js.quoteName(stubMethod.name));
+ }
+ }
+
+ callNames.add(method.callName);
+ // The first entry in the funsOrNames-array must be a string.
+ funsOrNames.add(js.quoteName(method.name));
+ for (ParameterStubMethod stubMethod in method.parameterStubs) {
+ callNames.add(stubMethod.callName);
+ addFunOrName(stubMethod);
+ }
+
+ js.ArrayInitializer callNameArray =
+ new js.ArrayInitializer(callNames.map(js.quoteName).toList());
+ js.ArrayInitializer funsOrNamesArray = new js.ArrayInitializer(funsOrNames);
+
+ bool isIntercepted = false;
+ if (method is InstanceMethod) {
+ isIntercepted = backend.isInterceptedMethod(method.element);
+ }
+ int requiredParameterCount = 0;
+ js.Expression optionalParameterDefaultValues = new js.LiteralNull();
+ if (method.canBeApplied) {
+ requiredParameterCount = method.requiredParameterCount;
+ optionalParameterDefaultValues =
+ _encodeOptionalParameterDefaultValues(method);
+ }
+
+ return js.js.statement('''
+ installTearOff(#container, #getterName, #isStatic, #isIntercepted,
+ #requiredParameterCount, #optionalParameterDefaultValues,
+ #callNames, #funsOrNames, #funType)''',
+ {
+ "container": container,
+ "getterName": js.quoteName(method.tearOffName),
+ "isStatic": new js.LiteralBool(method.isStatic),
+ "isIntercepted": new js.LiteralBool(isIntercepted),
+ "requiredParameterCount": js.number(requiredParameterCount),
+ "optionalParameterDefaultValues": optionalParameterDefaultValues,
+ "callNames": callNameArray,
+ "funsOrNames": funsOrNamesArray,
+ "funType": method.functionType,
+ });
+ }
+
/// Emits the section that installs tear-off getters.
- js.Statement emitInstallTearOffs(fragment) {
- throw new UnimplementedError('emitInstallTearOffs');
+ js.Statement emitInstallTearOffs(Fragment fragment) {
+ List<js.Statement> inits = <js.Statement>[];
+
+ for (Library library in fragment.libraries) {
+ for (StaticMethod method in library.statics) {
+ // TODO(floitsch): can there be anything else than a StaticDartMethod?
+ if (method is StaticDartMethod) {
+ if (method.needsTearOff) {
+ Holder holder = method.holder;
+ inits.add(
+ emitInstallTearOff(new js.VariableUse(holder.name), method));
+ }
+ }
+ }
+ for (Class cls in library.classes) {
+ for (InstanceMethod method in cls.methods) {
+ if (method.needsTearOff) {
+ js.Expression container = js.js("#.prototype", classReference(cls));
+ inits.add(emitInstallTearOff(container, method));
+ }
+ }
+ }
+ }
+ return new js.Block(inits);
}
/// Emits the constants section.
« no previous file with comments | « pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart ('k') | pkg/compiler/lib/src/patch_parser.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698