Chromium Code Reviews| 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. |