| 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.
|
|
|