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 71edb6165efe7c0f6599f1b855830f7280b38c02..c583c50c02963ab53bf660300fc0e4c250186ac9 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 |
@@ -116,7 +116,45 @@ 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. |
+// TODO(floitsch): Change tearOffCode to accept the data directly, or create a |
+// different tearOffCode? |
+function installTearOff(container, getterName, |
+ isStatic, isIntercepted, requiredParameterCount, |
Siggi Cherem (dart-lang)
2015/07/22 20:51:28
missing 1 space of indentation (FWIW, I don't thin
floitsch
2015/07/29 17:27:15
Done.
|
+ optionalParameterDefaultValues, |
+ callNames, funNames, funType) { |
+ var funs = []; |
+ for (var i = 0; i < funNames.length; i++) { |
+ var fun = container[funNames[i]]; |
+ 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 = funNames[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 |
@@ -146,6 +184,8 @@ function setOrUpdateLeafTags(newTags) { |
function updateTypes(newTypes) { |
var types = #embeddedTypes; |
types.push.apply(types, newTypes); |
+ // This relies on the fact that types are added *after* the tear-offs have |
Siggi Cherem (dart-lang)
2015/07/22 20:51:28
nit: move the comment above the push call?
floitsch
2015/07/29 17:27:15
Done. and added more comments.
|
+ // been installed. |
} |
// Updates the given holder with the properties of the [newHolder]. |
@@ -163,6 +203,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, |
@@ -290,6 +333,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), |
@@ -631,9 +675,104 @@ 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.Name> funNames = <js.Name>[]; |
+ |
+ callNames.add(method.callName); |
+ funNames.add(method.name); |
+ for (ParameterStubMethod stubMethod in method.parameterStubs) { |
+ callNames.add(stubMethod.callName); |
+ funNames.add(stubMethod.name); |
+ } |
+ |
+ js.ArrayInitializer callNameArray = |
+ new js.ArrayInitializer(callNames.map(js.quoteName).toList()); |
+ js.ArrayInitializer funNameArray = |
+ new js.ArrayInitializer(funNames.map(js.quoteName).toList()); |
+ |
+ 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); |
+ } |
+ |
+// TODO(floitsch): this can be more efficient. |
Siggi Cherem (dart-lang)
2015/07/22 20:51:28
what are you referring to by `this`?
floitsch
2015/07/29 17:27:15
removed.
The tear-off function is redirecting to a
|
+ return js.js.statement(''' |
+ installTearOff(#container, #getterName, #isStatic, #isIntercepted, |
+ #requiredParameterCount, #optionalParameterDefaultValues, |
+ #callNames, #funNames, #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, |
+ "funNames": funNameArray, |
+ "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. |