| Index: lib/compiler/implementation/native_handler.dart
|
| ===================================================================
|
| --- lib/compiler/implementation/native_handler.dart (revision 6616)
|
| +++ lib/compiler/implementation/native_handler.dart (working copy)
|
| @@ -147,7 +147,7 @@
|
| listener.endSend(token);
|
| token = token.next;
|
| // If this native method is just redirecting to another method,
|
| - // we add a return node to match the SSA builder expactations.
|
| + // we add a return node to match the SSA builder expectations.
|
| if (nativeRedirectionRegExp.hasMatch(str.dartString.slowToString())) {
|
| listener.endReturnStatement(true, begin, token);
|
| } else {
|
| @@ -256,91 +256,23 @@
|
| inputs.add(input);
|
| arguments.add('#');
|
| });
|
| - String foreignParameters = Strings.join(arguments, ',');
|
|
|
| - String dartMethodName;
|
| + String foreignParameters = Strings.join(arguments, ',');
|
| String nativeMethodCall;
|
| -
|
| if (element.kind == ElementKind.FUNCTION) {
|
| - dartMethodName = builder.compiler.namer.instanceMethodName(
|
| - element.getLibrary(), element.name, parameters.parameterCount);
|
| nativeMethodCall = '$receiver$nativeMethodName($foreignParameters)';
|
| } else if (element.kind == ElementKind.GETTER) {
|
| - dartMethodName = builder.compiler.namer.getterName(
|
| - element.getLibrary(), element.name);
|
| nativeMethodCall = '$receiver$nativeMethodName';
|
| } else if (element.kind == ElementKind.SETTER) {
|
| - dartMethodName = builder.compiler.namer.setterName(
|
| - element.getLibrary(), element.name);
|
| nativeMethodCall = '$receiver$nativeMethodName = $foreignParameters';
|
| } else {
|
| builder.compiler.internalError('unexpected kind: "${element.kind}"',
|
| element: element);
|
| }
|
|
|
| - HInstruction thenInstruction;
|
| - void visitThen() {
|
| - DartString jsCode = new DartString.literal(nativeMethodCall);
|
| - thenInstruction =
|
| - new HForeign(jsCode, const LiteralDartString('Object'), inputs);
|
| - builder.add(thenInstruction);
|
| - }
|
| -
|
| - bool isNativeLiteral = false;
|
| - bool isOverridden = false;
|
| - NativeEmitter nativeEmitter = builder.compiler.emitter.nativeEmitter;
|
| - if (element.enclosingElement.kind == ElementKind.CLASS) {
|
| - ClassElement classElement = element.enclosingElement;
|
| - String nativeName = classElement.nativeName.slowToString();
|
| - isNativeLiteral = nativeEmitter.isNativeLiteral(nativeName);
|
| - isOverridden = isOverriddenMethod(element, classElement, nativeEmitter);
|
| - }
|
| - if (!element.isInstanceMember() || isNativeLiteral || !isOverridden) {
|
| - // We generate a direct call to the native method.
|
| - visitThen();
|
| - builder.stack.add(thenInstruction);
|
| - } else {
|
| - // Record that this method is overridden. In case of optional
|
| - // arguments, the emitter will generate stubs to handle them,
|
| - // and needs to know if the method is overridden.
|
| - nativeEmitter.overriddenMethods.add(element);
|
| -
|
| - // If the method is an instance method that is overridden, we
|
| - // generate the following code:
|
| - // function(params) {
|
| - // return Object.getPrototypeOf(this).hasOwnProperty(dartMethodName))
|
| - // ? this.methodName(params)
|
| - // : Object.prototype.methodName.call(this, params);
|
| - // }
|
| - //
|
| - // The property check at the beginning is to make sure we won't
|
| - // call the method from the super class, in case the prototype of
|
| - // 'this' does not have the method yet.
|
| - HInstruction elseInstruction;
|
| - void visitElse() {
|
| - String params = arguments.isEmpty() ? '' : ', $foreignParameters';
|
| - DartString jsCode = new DartString.literal(
|
| - 'Object.prototype.$dartMethodName.call(#$params)');
|
| - elseInstruction =
|
| - new HForeign(jsCode, const LiteralDartString('Object'), inputs);
|
| - builder.add(elseInstruction);
|
| - }
|
| -
|
| - HConstant constant = builder.graph.addConstantString(
|
| - new DartString.literal('$dartMethodName'));
|
| - DartString jsCode = new DartString.literal(
|
| - 'Object.getPrototypeOf(#).hasOwnProperty(#)');
|
| - builder.push(new HForeign(
|
| - jsCode, const LiteralDartString('Object'),
|
| - <HInstruction>[builder.localsHandler.readThis(), constant]));
|
| -
|
| - builder.handleIf(visitThen, visitElse);
|
| -
|
| - HPhi phi = new HPhi.manyInputs(
|
| - null, <HInstruction>[thenInstruction, elseInstruction]);
|
| - builder.current.addPhi(phi);
|
| - builder.stack.add(phi);
|
| - }
|
| + DartString jsCode = new DartString.literal(nativeMethodCall);
|
| + builder.push(
|
| + new HForeign(jsCode, const LiteralDartString('Object'), inputs));
|
| } else {
|
| // This is JS code written in a Dart file with the construct
|
| // native """ ... """;. It does not work well with mangling,
|
| @@ -366,3 +298,48 @@
|
| <HInstruction>[]));
|
| }
|
| }
|
| +
|
| +void generateMethodWithPrototypeCheckForElement(Compiler compiler,
|
| + StringBuffer buffer,
|
| + FunctionElement element,
|
| + String code,
|
| + String parameters) {
|
| + String methodName;
|
| + Namer namer = compiler.namer;
|
| + if (element.kind == ElementKind.FUNCTION) {
|
| + FunctionParameters parameters = element.computeParameters(compiler);
|
| + methodName = namer.instanceMethodName(
|
| + element.getLibrary(), element.name, parameters.parameterCount);
|
| + } 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 {
|
| + compiler.internalError('unexpected kind: "${element.kind}"',
|
| + element: element);
|
| + }
|
| +
|
| + generateMethodWithPrototypeCheck(
|
| + compiler, buffer, methodName, code, parameters);
|
| +}
|
| +
|
| +
|
| +// 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.
|
| +void generateMethodWithPrototypeCheck(Compiler compiler,
|
| + StringBuffer buffer,
|
| + String methodName,
|
| + String code,
|
| + String parameters) {
|
| + buffer.add(" if (Object.getPrototypeOf(this).hasOwnProperty");
|
| + buffer.add("('$methodName')) {\n");
|
| + buffer.add(" $code");
|
| + buffer.add(" } else {\n");
|
| + buffer.add(" return Object.prototype.$methodName.call(this");
|
| + buffer.add(parameters == '' ? '' : ', $parameters');
|
| + buffer.add(");\n");
|
| + buffer.add(" }\n");
|
| +}
|
|
|