Index: sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart |
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart b/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart |
index c2542ef768db514920b37b0537583c57d667b6bb..38c9a5f608fc9c68968085c0e465a5975102b613 100644 |
--- a/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart |
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart |
@@ -206,34 +206,47 @@ function(cls, desc) { |
return result == null ? const<ClassElement>[] : result; |
} |
- void potentiallyConvertDartClosuresToJs(CodeBuffer code, |
+ void potentiallyConvertDartClosuresToJs(List<js.Statement> statements, |
FunctionElement member, |
- List<String> argumentsBuffer) { |
+ List<js.Parameter> stubParameters) { |
FunctionSignature parameters = member.computeSignature(compiler); |
Element converter = |
compiler.findHelper(const SourceString('convertDartClosureToJS')); |
String closureConverter = backend.namer.isolateAccess(converter); |
+ Set<String> stubParameterNames = new Set<String>.from( |
+ stubParameters.map((param) => param.name)); |
ngeoffray
2012/11/30 09:01:56
I would to this by hand to keep the order and then
|
parameters.forEachParameter((Element parameter) { |
String name = parameter.name.slowToString(); |
- // If [name] is not in [argumentsBuffer], then the parameter is |
- // an optional parameter that was not provided for that stub. |
- if (argumentsBuffer.indexOf(name) == -1) return; |
- DartType type = parameter.computeType(compiler).unalias(compiler); |
- if (type is FunctionType) { |
- // The parameter type is a function type either directly or through |
- // typedef(s). |
- int arity = type.computeArity(); |
- code.add(' $name = $closureConverter($name, $arity);\n'); |
+ // If [name] is not in [stubParameters], then the parameter is an optional |
+ // parameter that was not provided for this stub. |
+ for (js.Parameter stubParameter in stubParameters) { |
+ if (stubParameter.name == name) { |
+ DartType type = parameter.computeType(compiler).unalias(compiler); |
+ if (type is FunctionType) { |
+ // The parameter type is a function type either directly or through |
+ // typedef(s). |
+ int arity = type.computeArity(); |
+ |
+ statements.add( |
+ new js.ExpressionStatement( |
+ new js.Assignment( |
+ new js.VariableUse(name), |
+ new js.VariableUse(closureConverter) |
+ .callWith([new js.VariableUse(name), |
+ new js.LiteralNumber('$arity')])))); |
+ break; |
+ } |
+ } |
} |
}); |
} |
- String generateParameterStub(Element member, |
- String invocationName, |
- String stubParameters, |
- List<String> argumentsBuffer, |
- int indexOfLastOptionalArgumentInParameters, |
- CodeBuffer buffer) { |
+ List<js.Statement> generateParameterStubStatements( |
+ Element member, |
+ String invocationName, |
+ List<js.Parameter> stubParameters, |
+ List<js.Expression> argumentsBuffer, |
+ int indexOfLastOptionalArgumentInParameters) { |
// The target JS function may check arguments.length so we need to |
// make sure not to pass any unspecified optional arguments to it. |
// For example, for the following Dart method: |
@@ -243,37 +256,92 @@ function(cls, desc) { |
// must be turned into a JS call to: |
// foo(null, y). |
- List<String> nativeArgumentsBuffer = argumentsBuffer.getRange( |
- 0, indexOfLastOptionalArgumentInParameters + 1); |
- |
ClassElement classElement = member.enclosingElement; |
- String nativeName = classElement.nativeName.slowToString(); |
- String nativeArguments = Strings.join(nativeArgumentsBuffer, ","); |
+ String nativeTagInfo = classElement.nativeName.slowToString(); |
- CodeBuffer code = new CodeBuffer(); |
- potentiallyConvertDartClosuresToJs(code, member, argumentsBuffer); |
+ List<js.Statement> statements = <js.Statement>[]; |
+ potentiallyConvertDartClosuresToJs(statements, member, stubParameters); |
+ |
+ String target; |
+ List<js.Expression> arguments; |
if (!nativeMethods.contains(member)) { |
- // When calling a method that has a native body, we call it |
- // with our calling conventions. |
- String arguments = Strings.join(argumentsBuffer, ","); |
- code.add(' return this.${backend.namer.getName(member)}($arguments)'); |
+ // When calling a method that has a native body, we call it with our |
+ // calling conventions. |
+ target = backend.namer.getName(member); |
+ arguments = argumentsBuffer; |
} else { |
- // When calling a JS method, we call it with the native name. |
- String name = redirectingMethods[member]; |
- if (name == null) name = member.name.slowToString(); |
- code.add(' return this.$name($nativeArguments);'); |
+ // When calling a JS method, we call it with the native name, and only the |
+ // arguments up until the last one provided. |
+ target = redirectingMethods[member]; |
+ if (target == null) target = member.name.slowToString(); |
+ arguments = argumentsBuffer.getRange( |
+ 0, indexOfLastOptionalArgumentInParameters + 1); |
} |
+ statements.add( |
+ new js.Return( |
+ new js.VariableUse('this').dot(target).callWith(arguments))); |
- if (isNativeLiteral(nativeName) || !overriddenMethods.contains(member)) { |
+ if (isNativeLiteral(nativeTagInfo) || !overriddenMethods.contains(member)) { |
// Call the method directly. |
- buffer.add(code.toString()); |
+ return statements; |
+ } else { |
+ return <js.Statement>[ |
+ generateMethodBodyWithPrototypeCheck( |
+ invocationName, new js.Block(statements), stubParameters)]; |
+ } |
+ } |
+ |
+ // If a method is overridden, we must check if the prototype of 'this' has the |
+ // method available. Otherwise, we may end up calling the method from the |
+ // super class. If the method is not available, we make a direct call to |
+ // Object.prototype.$methodName. This method will patch the prototype of |
+ // 'this' to the real method. |
+ js.Statement generateMethodBodyWithPrototypeCheck( |
+ String methodName, |
+ js.Statement body, |
+ List<js.Parameter> parameters) { |
+ return new js.If( |
+ new js.VariableUse('Object') |
+ .dot('getPrototypeOf') |
+ .callWith([new js.VariableUse('this')]) |
+ .dot('hasOwnProperty') |
+ .callWith([new js.LiteralString("'$methodName'")]), |
+ body, |
+ new js.Block( |
+ <js.Statement>[ |
+ new js.Return( |
+ new js.VariableUse('Object') |
+ .dot('prototype').dot(methodName).dot('call') |
+ .callWith( |
+ <js.Expression>[new js.VariableUse('this')] |
+ ..addAll(parameters.map((param) => |
+ new js.VariableUse(param.name))))) |
+ ])); |
+ } |
+ |
+ js.Block generateMethodBodyWithPrototypeCheckForElement( |
+ FunctionElement element, |
+ js.Block body, |
+ List<js.Parameter> parameters) { |
+ String methodName; |
+ Namer namer = backend.namer; |
+ if (element.kind == ElementKind.FUNCTION) { |
+ methodName = namer.instanceMethodName(element); |
+ } else if (element.kind == ElementKind.GETTER) { |
+ methodName = namer.getterName(element.getLibrary(), element.name); |
+ } else if (element.kind == ElementKind.SETTER) { |
+ methodName = namer.setterName(element.getLibrary(), element.name); |
} else { |
- native.generateMethodWithPrototypeCheck( |
- compiler, buffer, invocationName, code.toString(), stubParameters); |
+ compiler.internalError('unexpected kind: "${element.kind}"', |
+ element: element); |
} |
+ |
+ return new js.Block( |
+ [generateMethodBodyWithPrototypeCheck(methodName, body, parameters)]); |
} |
+ |
void emitDynamicDispatchMetadata() { |
if (classesWithDynamicDispatch.isEmpty) return; |
int length = classesWithDynamicDispatch.length; |