Index: sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart |
=================================================================== |
--- sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart (revision 14840) |
+++ sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart (working copy) |
@@ -44,7 +44,20 @@ |
well as in the generated code. */ |
String isolateProperties; |
String classesCollector; |
+ |
+ /** |
+ * A cache of closures that are used to closurize instance methods. |
+ * A closure is dynamically bound to the instance used when |
+ * closurized. |
+ */ |
final Map<int, String> boundClosureCache; |
+ |
+ /** |
+ * A cache of closures that are used to closurize instance methods |
+ * of interceptors. These closures are dynamically bound to the |
+ * interceptor instance, and the actual receiver of the method. |
+ */ |
+ final Map<int, String> interceptorClosureCache; |
Set<ClassElement> checkedClasses; |
final bool generateSourceMap; |
@@ -54,6 +67,7 @@ |
mainBuffer = new CodeBuffer(), |
this.namer = namer, |
boundClosureCache = new Map<int, String>(), |
+ interceptorClosureCache = new Map<int, String>(), |
constantEmitter = new ConstantEmitter(compiler, namer), |
super(compiler) { |
nativeEmitter = new NativeEmitter(this); |
@@ -617,7 +631,7 @@ |
bool needsLeadingComma) { |
assert(invariant(classElement, classElement.isDeclaration)); |
bool needsComma = needsLeadingComma; |
- void defineInstanceMember(String name, CodeBuffer memberBuffer) { |
+ void defineInstanceMember(String name, StringBuffer memberBuffer) { |
if (needsComma) buffer.add(','); |
needsComma = true; |
buffer.add('\n'); |
@@ -625,6 +639,13 @@ |
buffer.add(memberBuffer); |
} |
+ JavaScriptBackend backend = compiler.backend; |
+ if (classElement == backend.objectInterceptorClass) { |
+ emitInterceptorMethods(defineInstanceMember); |
+ // The ObjectInterceptor does not have any instance methods. |
+ return; |
+ } |
+ |
classElement.implementation.forEachMember( |
(ClassElement enclosing, Element member) { |
assert(invariant(classElement, member.isDeclaration)); |
@@ -855,6 +876,39 @@ |
buffer.add('\n};\n\n'); |
} |
+ void emitInterceptorMethods( |
+ void defineInstanceMember(String name, StringBuffer memberBuffer)) { |
+ JavaScriptBackend backend = compiler.backend; |
+ // Emit forwarders for the ObjectInterceptor class. We need to |
+ // emit all possible sends on intercepted methods. |
+ for (Selector selector in backend.usedInterceptors) { |
+ String name; |
+ String comma = ''; |
+ String parameters = ''; |
+ if (selector.isGetter()) { |
+ name = backend.namer.getterName(selector.library, selector.name); |
+ } else if (selector.isSetter()) { |
+ name = backend.namer.setterName(selector.library, selector.name); |
+ } else { |
+ assert(selector.isCall()); |
+ name = backend.namer.instanceMethodInvocationName( |
+ selector.library, selector.name, selector); |
+ if (selector.argumentCount > 0) { |
+ comma = ', '; |
+ int i = 0; |
+ for (; i < selector.argumentCount - 1; i++) { |
+ parameters = '${parameters}a$i, '; |
+ } |
+ parameters = '${parameters}a$i'; |
+ } |
+ } |
+ StringBuffer body = new StringBuffer( |
+ "function(receiver$comma$parameters) {" |
+ " return receiver.$name($parameters); }"); |
+ defineInstanceMember(name, body); |
+ } |
+ } |
+ |
/** |
* Generate "is tests" for [cls]: itself, and the "is tests" for the |
* classes it implements. We don't need to add the "is tests" of the |
@@ -1016,11 +1070,13 @@ |
} |
void emitBoundClosureClassHeader(String mangledName, |
- String superName, |
- CodeBuffer buffer) { |
+ String superName, |
+ String extraArgument, |
+ CodeBuffer buffer) { |
+ extraArgument = extraArgument.isEmpty ? '' : ", '$extraArgument'"; |
buffer.add(""" |
$classesCollector.$mangledName = {'': |
-['self', 'target'], |
+['self'$extraArgument, 'target'], |
'super': '$superName', |
"""); |
} |
@@ -1057,8 +1113,27 @@ |
bool hasOptionalParameters = member.optionalParameterCount(compiler) != 0; |
int parameterCount = member.parameterCount(compiler); |
+ Map<int, String> cache; |
+ String extraArg; |
+ String extraArgWithThis; |
+ String extraArgWithoutComma; |
+ // Methods on foreign classes take an extra parameter, which is |
+ // the actual receiver of the call. |
+ JavaScriptBackend backend = compiler.backend; |
+ if (backend.isInterceptorClass(member.getEnclosingClass())) { |
+ cache = interceptorClosureCache; |
+ extraArg = 'receiver, '; |
+ extraArgWithThis = 'this.receiver, '; |
+ extraArgWithoutComma = 'receiver'; |
+ } else { |
+ cache = boundClosureCache; |
+ extraArg = ''; |
+ extraArgWithoutComma = ''; |
+ extraArgWithThis = ''; |
+ } |
+ |
String closureClass = |
- hasOptionalParameters ? null : boundClosureCache[parameterCount]; |
+ hasOptionalParameters ? null : cache[parameterCount]; |
if (closureClass == null) { |
// Either the class was not cached yet, or there are optional parameters. |
// Create a new closure class. |
@@ -1071,14 +1146,15 @@ |
// Define the constructor with a name so that Object.toString can |
// find the class name of the closure class. |
- emitBoundClosureClassHeader(mangledName, superName, boundClosureBuffer); |
+ emitBoundClosureClassHeader( |
+ mangledName, superName, extraArgWithoutComma, boundClosureBuffer); |
// Now add the methods on the closure class. The instance method does not |
// have the correct name. Since [addParameterStubs] use the name to create |
// its stubs we simply create a fake element with the correct name. |
// Note: the callElement will not have any enclosingElement. |
FunctionElement callElement = |
new ClosureInvocationElement(Namer.CLOSURE_INVOCATION_NAME, member); |
- |
+ |
String invocationName = namer.instanceMethodName(callElement); |
List<String> arguments = new List<String>(parameterCount); |
for (int i = 0; i < parameterCount; i++) { |
@@ -1087,7 +1163,8 @@ |
String joinedArgs = Strings.join(arguments, ", "); |
boundClosureBuffer.add( |
"$invocationName: function($joinedArgs) {"); |
- boundClosureBuffer.add(" return this.self[this.target]($joinedArgs);"); |
+ boundClosureBuffer.add( |
+ " return this.self[this.target]($extraArgWithThis$joinedArgs);"); |
boundClosureBuffer.add(" }"); |
addParameterStubs(callElement, (String stubName, CodeBuffer memberValue) { |
boundClosureBuffer.add(',\n $stubName: $memberValue'); |
@@ -1098,7 +1175,7 @@ |
// Cache it. |
if (!hasOptionalParameters) { |
- boundClosureCache[parameterCount] = closureClass; |
+ cache[parameterCount] = closureClass; |
} |
} |
@@ -1106,8 +1183,8 @@ |
String getterName = namer.getterName(member.getLibrary(), member.name); |
String targetName = namer.instanceMethodName(member); |
CodeBuffer getterBuffer = new CodeBuffer(); |
- getterBuffer.add( |
- "function() { return new $closureClass(this, '$targetName'); }"); |
+ getterBuffer.add("function($extraArgWithoutComma) " |
+ "{ return new $closureClass(this, $extraArg'$targetName'); }"); |
defineInstanceMember(getterName, getterBuffer); |
} |