| Index: sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
|
| ===================================================================
|
| --- sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart (revision 19306)
|
| +++ sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart (working copy)
|
| @@ -653,12 +653,11 @@
|
| if (alreadyGenerated.contains(invocationName)) return;
|
| alreadyGenerated.add(invocationName);
|
|
|
| - bool isInterceptorClass =
|
| - backend.isInterceptorClass(member.getEnclosingClass());
|
| + bool isInterceptedMethod = backend.isInterceptedMethod(member);
|
|
|
| - // If the method is in an interceptor class, we need to also pass
|
| + // If the method is intercepted, we need to also pass
|
| // the actual receiver.
|
| - int extraArgumentCount = isInterceptorClass ? 1 : 0;
|
| + int extraArgumentCount = isInterceptedMethod ? 1 : 0;
|
| // Use '$receiver' to avoid clashes with other parameter names. Using
|
| // '$receiver' works because [:namer.safeName:] used for getting parameter
|
| // names never returns a name beginning with a single '$'.
|
| @@ -673,7 +672,7 @@
|
| parameters.parameterCount + extraArgumentCount);
|
|
|
| int count = 0;
|
| - if (isInterceptorClass) {
|
| + if (isInterceptedMethod) {
|
| count++;
|
| parametersBuffer[0] = new jsAst.Parameter(receiverArgumentName);
|
| argumentsBuffer[0] = js[receiverArgumentName];
|
| @@ -913,11 +912,6 @@
|
| void emitInstanceMembers(ClassElement classElement,
|
| ClassBuilder builder) {
|
| assert(invariant(classElement, classElement.isDeclaration));
|
| - if (classElement == backend.objectInterceptorClass) {
|
| - emitInterceptorMethods(builder);
|
| - // The ObjectInterceptor does not have any instance methods.
|
| - return;
|
| - }
|
|
|
| void visitMember(ClassElement enclosing, Element member) {
|
| assert(invariant(classElement, member.isDeclaration));
|
| @@ -995,13 +989,10 @@
|
| }
|
| }
|
|
|
| - if (backend.isInterceptorClass(classElement)) {
|
| - // The operator== method in [:Object:] does not take the same
|
| - // number of arguments as an intercepted method, therefore we
|
| - // explicitely add one to all interceptor classes. Note that we
|
| - // would not have do do that if all intercepted methods had
|
| - // a calling convention where the receiver is the first
|
| - // parameter.
|
| + if (backend.isInterceptorClass(classElement)
|
| + && classElement != compiler.objectClass) {
|
| + // We optimize the operator== on interceptor classes to
|
| + // just do a JavaScript double or triple equals.
|
| String name = backend.namer.publicInstanceMethodNameByArity(
|
| const SourceString('=='), 1);
|
| Function kind = (classElement == backend.jsNullClass)
|
| @@ -1129,10 +1120,15 @@
|
| ? member.fixedBackendName()
|
| : (isMixinNativeField ? member.name.slowToString() : accessorName);
|
| bool needsCheckedSetter = false;
|
| - if (needsSetter && compiler.enableTypeAssertions
|
| - && canGenerateCheckedSetter(member)) {
|
| - needsCheckedSetter = true;
|
| - needsSetter = false;
|
| + if (needsSetter) {
|
| + if (compiler.enableTypeAssertions
|
| + && canGenerateCheckedSetter(member)) {
|
| + needsCheckedSetter = true;
|
| + needsSetter = false;
|
| + } else if (backend.isInterceptedMethod(member)) {
|
| + // The [addField] will take care of generating the setter.
|
| + needsSetter = false;
|
| + }
|
| }
|
| // Getters and setters with suffixes will be generated dynamically.
|
| addField(member,
|
| @@ -1170,6 +1166,7 @@
|
|
|
| void generateGetter(Element member, String fieldName, String accessorName,
|
| ClassBuilder builder) {
|
| + assert(!backend.isInterceptorClass(member));
|
| String getterName = namer.getterNameFromAccessorName(accessorName);
|
| builder.addProperty(getterName,
|
| js.fun([], js.return_(js['this'][fieldName])));
|
| @@ -1177,9 +1174,13 @@
|
|
|
| void generateSetter(Element member, String fieldName, String accessorName,
|
| ClassBuilder builder) {
|
| + assert(!backend.isInterceptorClass(member));
|
| String setterName = namer.setterNameFromAccessorName(accessorName);
|
| + List<String> args = backend.isInterceptedMethod(member)
|
| + ? ['receiver', 'v']
|
| + : ['v'];
|
| builder.addProperty(setterName,
|
| - js.fun(['v'], js['this'][fieldName].assign('v')));
|
| + js.fun(args, js['this'][fieldName].assign('v')));
|
| }
|
|
|
| bool canGenerateCheckedSetter(Element member) {
|
| @@ -1210,8 +1211,11 @@
|
| }
|
|
|
| String setterName = namer.setterNameFromAccessorName(accessorName);
|
| + List<String> args = backend.isInterceptedMethod(member)
|
| + ? ['receiver', 'v']
|
| + : ['v'];
|
| builder.addProperty(setterName,
|
| - js.fun(['v'], js['this'][fieldName].assign(js[helperName](arguments))));
|
| + js.fun(args, js['this'][fieldName].assign(js[helperName](arguments))));
|
| }
|
|
|
| void emitClassConstructor(ClassElement classElement, ClassBuilder builder) {
|
| @@ -1303,6 +1307,9 @@
|
| if (needsSetter) {
|
| generateSetter(member, name, accessorName, builder);
|
| }
|
| + } else if (backend.isInterceptedMethod(member)
|
| + && instanceFieldNeedsSetter(member)) {
|
| + generateSetter(member, name, accessorName, builder);
|
| }
|
| });
|
| });
|
| @@ -1362,38 +1369,6 @@
|
| return _selectorRank(selector1) - _selectorRank(selector2);
|
| }
|
|
|
| - void emitInterceptorMethods(ClassBuilder builder) {
|
| - // Emit forwarders for the ObjectInterceptor class. We need to
|
| - // emit all possible sends on intercepted methods. Because of
|
| - // typed selectors we have to avoid generating the same forwarder
|
| - // multiple times.
|
| - Set<String> alreadyGenerated = new Set<String>();
|
| - for (Selector selector in
|
| - backend.usedInterceptors.toList()..sort(_compareSelectorNames)) {
|
| - String name = backend.namer.invocationName(selector);
|
| - if (alreadyGenerated.contains(name)) continue;
|
| - alreadyGenerated.add(name);
|
| -
|
| - List<jsAst.Parameter> parameters = <jsAst.Parameter>[];
|
| - List<jsAst.Expression> arguments = <jsAst.Expression>[];
|
| - parameters.add(new jsAst.Parameter('receiver'));
|
| -
|
| - if (selector.isSetter()) {
|
| - parameters.add(new jsAst.Parameter('value'));
|
| - arguments.add(js['value']);
|
| - } else {
|
| - for (int i = 0; i < selector.argumentCount; i++) {
|
| - String argName = 'a$i';
|
| - parameters.add(new jsAst.Parameter(argName));
|
| - arguments.add(js[argName]);
|
| - }
|
| - }
|
| - jsAst.Fun function =
|
| - js.fun(parameters, js.return_(js['receiver'][name](arguments)));
|
| - builder.addProperty(name, function);
|
| - }
|
| - }
|
| -
|
| Iterable<Element> getTypedefChecksOn(DartType type) {
|
| bool isSubtype(TypedefElement typedef) {
|
| FunctionType typedefType =
|
| @@ -1560,8 +1535,9 @@
|
| }
|
|
|
| // Add unneeded interceptors to the [unneededClasses] set.
|
| - for (ClassElement interceptor in backend.interceptedClasses.keys) {
|
| - if (!needed.contains(interceptor)) {
|
| + for (ClassElement interceptor in backend.interceptedClasses) {
|
| + if (!needed.contains(interceptor)
|
| + && interceptor != compiler.objectClass) {
|
| unneededClasses.add(interceptor);
|
| }
|
| }
|
| @@ -1711,9 +1687,9 @@
|
|
|
| Map<int, String> cache;
|
| String extraArg = null;
|
| - // Methods on interceptor classes take an extra parameter, which is the
|
| - // actual receiver of the call.
|
| - bool inInterceptor = backend.isInterceptorClass(member.getEnclosingClass());
|
| + // Intercepted methods take an extra parameter, which is the
|
| + // receiver of the call.
|
| + bool inInterceptor = backend.isInterceptedMethod(member);
|
| if (inInterceptor) {
|
| cache = interceptorClosureCache;
|
| extraArg = 'receiver';
|
| @@ -1834,10 +1810,9 @@
|
| DefineStubFunction defineStub) {
|
| assert(invariant(member, member.isDeclaration));
|
| LibraryElement memberLibrary = member.getLibrary();
|
| - // If the class is an interceptor class, the stub gets the
|
| + // If the method is intercepted, the stub gets the
|
| // receiver explicitely and we need to pass it to the getter call.
|
| - bool isInterceptorClass =
|
| - backend.isInterceptorClass(member.getEnclosingClass());
|
| + bool isInterceptedMethod = backend.isInterceptedMethod(member);
|
|
|
| const String receiverArgumentName = r'$receiver';
|
|
|
| @@ -1845,7 +1820,7 @@
|
| if (member.isGetter()) {
|
| String getterName = namer.getterName(member);
|
| return js['this'][getterName](
|
| - isInterceptorClass
|
| + isInterceptedMethod
|
| ? <jsAst.Expression>[js[receiverArgumentName]]
|
| : <jsAst.Expression>[]);
|
| } else {
|
| @@ -1873,7 +1848,7 @@
|
|
|
| List<jsAst.Parameter> parameters = <jsAst.Parameter>[];
|
| List<jsAst.Expression> arguments = <jsAst.Expression>[];
|
| - if (isInterceptorClass) {
|
| + if (isInterceptedMethod) {
|
| parameters.add(new jsAst.Parameter(receiverArgumentName));
|
| }
|
|
|
| @@ -1897,6 +1872,8 @@
|
| Iterable<VariableElement> staticNonFinalFields =
|
| handler.getStaticNonFinalFieldsForEmission();
|
| for (Element element in Elements.sortedByPosition(staticNonFinalFields)) {
|
| + // [:interceptedNames:] is handled in [emitInterceptedNames].
|
| + if (element == backend.interceptedNames) continue;
|
| compiler.withCurrentElement(element, () {
|
| Constant initialValue = handler.getInitialValueFor(element);
|
| jsAst.Expression init =
|
| @@ -2035,14 +2012,19 @@
|
| String createInvocationMirror = namer.getName(
|
| compiler.createInvocationMirrorElement);
|
|
|
| + assert(backend.isInterceptedName(Compiler.NO_SUCH_METHOD));
|
| jsAst.Expression expression = js['this.$noSuchMethodName'](
|
| - js[namer.CURRENT_ISOLATE][createInvocationMirror]([
|
| - js.string(methodName),
|
| - js.string(internalName),
|
| - type,
|
| - new jsAst.ArrayInitializer.from(
|
| - parameters.map((param) => js[param.name]).toList()),
|
| - new jsAst.ArrayInitializer.from(argNames)]));
|
| + [js['this'],
|
| + js[namer.CURRENT_ISOLATE][createInvocationMirror]([
|
| + js.string(methodName),
|
| + js.string(internalName),
|
| + type,
|
| + new jsAst.ArrayInitializer.from(
|
| + parameters.map((param) => js[param.name]).toList()),
|
| + new jsAst.ArrayInitializer.from(argNames)])]);
|
| + parameters = backend.isInterceptedName(selector.name)
|
| + ? ([new jsAst.Parameter('\$receiver')]..addAll(parameters))
|
| + : parameters;
|
| return js.fun(parameters, js.return_(expression));
|
| }
|
|
|
| @@ -2183,7 +2165,6 @@
|
| }
|
|
|
| void emitGetInterceptorMethod(CodeBuffer buffer,
|
| - String objectName,
|
| String key,
|
| Collection<ClassElement> classes) {
|
| jsAst.Statement buildReturnInterceptor(ClassElement cls) {
|
| @@ -2235,10 +2216,11 @@
|
| else if (cls == backend.jsNullClass) hasNull = true;
|
| else if (cls == backend.jsNumberClass) hasNumber = true;
|
| else if (cls == backend.jsStringClass) hasString = true;
|
| - else throw 'Internal error: $cls';
|
| + else {
|
| + assert(cls == compiler.objectClass);
|
| + }
|
| }
|
| if (hasDouble) {
|
| - assert(!hasNumber);
|
| hasNumber = true;
|
| }
|
| if (hasInt) hasNumber = true;
|
| @@ -2291,7 +2273,7 @@
|
| if (hasArray) {
|
| block.statements.add(buildInterceptorCheck(backend.jsArrayClass));
|
| }
|
| - block.statements.add(js.return_(js[objectName]['prototype']));
|
| + block.statements.add(js.return_(receiver));
|
|
|
| buffer.add(jsAst.prettyPrint(
|
| js[isolateProperties][key].assign(js.fun(['receiver'], block)),
|
| @@ -2303,13 +2285,10 @@
|
| * Emit all versions of the [:getInterceptor:] method.
|
| */
|
| void emitGetInterceptorMethods(CodeBuffer buffer) {
|
| - // If no class needs to be intercepted, just return.
|
| - if (backend.objectInterceptorClass == null) return;
|
| - String objectName = namer.isolateAccess(backend.objectInterceptorClass);
|
| var specializedGetInterceptors = backend.specializedGetInterceptors;
|
| for (String name in specializedGetInterceptors.keys.toList()..sort()) {
|
| Collection<ClassElement> classes = specializedGetInterceptors[name];
|
| - emitGetInterceptorMethod(buffer, objectName, name, classes);
|
| + emitGetInterceptorMethod(buffer, name, classes);
|
| }
|
| }
|
|
|
| @@ -2535,7 +2514,7 @@
|
| for (String name in names) {
|
| Selector selector = backend.oneShotInterceptors[name];
|
| Set<ClassElement> classes =
|
| - backend.getInterceptedClassesOn(selector);
|
| + backend.getInterceptedClassesOn(selector.name);
|
| String getInterceptorName =
|
| namer.getInterceptorName(backend.getInterceptorMethod, classes);
|
|
|
| @@ -2577,6 +2556,32 @@
|
| }
|
| }
|
|
|
| + /**
|
| + * If [:invokeOn:] has been compiled, emit all the possible selector names
|
| + * that are intercepted into the [:interceptedNames:] top-level
|
| + * variable. The implementation of [:invokeOn:] will use it to
|
| + * determine whether it should call the method with an extra
|
| + * parameter.
|
| + */
|
| + void emitInterceptedNames(CodeBuffer buffer) {
|
| + if (!compiler.enabledInvokeOn) return;
|
| + String name = backend.namer.getName(backend.interceptedNames);
|
| + jsAst.PropertyAccess property = js[isolateProperties][name];
|
| +
|
| + int index = 0;
|
| + List<jsAst.ArrayElement> elements = backend.usedInterceptors.map(
|
| + (Selector selector) {
|
| + jsAst.Literal str = js.string(namer.invocationName(selector));
|
| + return new jsAst.ArrayElement(index++, str);
|
| + }).toList();
|
| + jsAst.ArrayInitializer array = new jsAst.ArrayInitializer(
|
| + backend.usedInterceptors.length,
|
| + elements);
|
| +
|
| + buffer.add(jsAst.prettyPrint(property.assign(array), compiler));
|
| + buffer.add(N);
|
| + }
|
| +
|
| void emitInitFunction(CodeBuffer buffer) {
|
| jsAst.Fun fun = js.fun([], [
|
| js['$isolateProperties = {}'],
|
| @@ -2678,6 +2683,7 @@
|
| // constants to be set up.
|
| emitStaticNonFinalFieldInitializations(mainBuffer);
|
| emitOneShotInterceptors(mainBuffer);
|
| + emitInterceptedNames(mainBuffer);
|
| emitGetInterceptorMethods(mainBuffer);
|
| emitLazilyInitializedStaticFields(mainBuffer);
|
|
|
|
|