| Index: frog/leg/native_handler.dart
|
| ===================================================================
|
| --- frog/leg/native_handler.dart (revision 5925)
|
| +++ frog/leg/native_handler.dart (working copy)
|
| @@ -1,351 +0,0 @@
|
| -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -
|
| -#library('native');
|
| -#import('../../lib/uri/uri.dart');
|
| -#import('leg.dart');
|
| -#import('elements/elements.dart');
|
| -#import('scanner/scannerlib.dart');
|
| -#import('ssa/ssa.dart');
|
| -#import('tree/tree.dart');
|
| -#import('util/util.dart');
|
| -
|
| -void processNativeClasses(Compiler compiler,
|
| - Collection<LibraryElement> libraries) {
|
| - for (LibraryElement library in libraries) {
|
| - processNativeClassesInLibrary(compiler, library);
|
| - }
|
| -}
|
| -
|
| -void addSubtypes(ClassElement cls,
|
| - NativeEmitter emitter) {
|
| - for (Type type in cls.allSupertypes) {
|
| - List<Element> subtypes = emitter.subtypes.putIfAbsent(
|
| - type.element,
|
| - () => <ClassElement>[]);
|
| - subtypes.add(cls);
|
| - }
|
| -}
|
| -
|
| -void processNativeClassesInLibrary(Compiler compiler,
|
| - LibraryElement library) {
|
| - bool hasNativeClass = false;
|
| - for (Link<Element> link = library.topLevelElements;
|
| - !link.isEmpty(); link = link.tail) {
|
| - Element element = link.head;
|
| - if (element.kind == ElementKind.CLASS) {
|
| - ClassElement classElement = element;
|
| - if (classElement.isNative()) {
|
| - hasNativeClass = true;
|
| - compiler.registerInstantiatedClass(classElement);
|
| - // Also parse the node to know all its methods because
|
| - // otherwise it will only be parsed if there is a call to
|
| - // one of its constructor.
|
| - classElement.parseNode(compiler);
|
| - // Resolve to setup the inheritance.
|
| - classElement.ensureResolved(compiler);
|
| - // Add the information that this class is a subtype of
|
| - // its supertypes. The code emitter and the ssa builder use that
|
| - // information.
|
| - NativeEmitter emitter = compiler.emitter.nativeEmitter;
|
| - addSubtypes(classElement, emitter);
|
| - }
|
| - }
|
| - }
|
| - if (hasNativeClass) {
|
| - compiler.registerStaticUse(compiler.findHelper(
|
| - const SourceString('dynamicFunction')));
|
| - compiler.registerStaticUse(compiler.findHelper(
|
| - const SourceString('dynamicSetMetadata')));
|
| - compiler.registerStaticUse(compiler.findHelper(
|
| - const SourceString('defineProperty')));
|
| - compiler.registerStaticUse(compiler.findHelper(
|
| - const SourceString('toStringForNativeObject')));
|
| - }
|
| -}
|
| -
|
| -void maybeEnableNative(Compiler compiler,
|
| - LibraryElement library,
|
| - Uri uri) {
|
| - String libraryName = uri.toString();
|
| - if (library.script.name.contains('dart/frog/tests/native/src')
|
| - || libraryName == 'dart:dom'
|
| - || libraryName == 'dart:isolate'
|
| - || libraryName == 'dart:html') {
|
| - library.define(new ForeignElement(
|
| - const SourceString('native'), library), compiler);
|
| - library.canUseNative = true;
|
| - }
|
| -
|
| - // Additionaly, if this is a test, we allow access to foreign functions.
|
| - if (library.script.name.contains('dart/frog/tests/native/src')) {
|
| - library.define(compiler.findHelper(const SourceString('JS')), compiler);
|
| - }
|
| -}
|
| -
|
| -void checkAllowedLibrary(ElementListener listener, Token token) {
|
| - LibraryElement currentLibrary = listener.compilationUnitElement.getLibrary();
|
| - if (!currentLibrary.canUseNative) {
|
| - listener.recoverableError("Unexpected token", token: token);
|
| - }
|
| -}
|
| -
|
| -Token handleNativeBlockToSkip(Listener listener, Token token) {
|
| - checkAllowedLibrary(listener, token);
|
| - token = token.next;
|
| - if (token.kind === STRING_TOKEN) {
|
| - token = token.next;
|
| - }
|
| - if (token.stringValue === '{') {
|
| - BeginGroupToken beginGroupToken = token;
|
| - token = beginGroupToken.endGroup;
|
| - }
|
| - return token;
|
| -}
|
| -
|
| -Token handleNativeClassBodyToSkip(Listener listener, Token token) {
|
| - checkAllowedLibrary(listener, token);
|
| - listener.handleIdentifier(token);
|
| - token = token.next;
|
| - if (token.kind !== STRING_TOKEN) {
|
| - return listener.unexpected(token);
|
| - }
|
| - token = token.next;
|
| - if (token.stringValue !== '{') {
|
| - return listener.unexpected(token);
|
| - }
|
| - BeginGroupToken beginGroupToken = token;
|
| - token = beginGroupToken.endGroup;
|
| - return token;
|
| -}
|
| -
|
| -Token handleNativeClassBody(Listener listener, Token token) {
|
| - checkAllowedLibrary(listener, token);
|
| - token = token.next;
|
| - if (token.kind !== STRING_TOKEN) {
|
| - listener.unexpected(token);
|
| - } else {
|
| - token = token.next;
|
| - }
|
| - return token;
|
| -}
|
| -
|
| -Token handleNativeFunctionBody(ElementListener listener, Token token) {
|
| - checkAllowedLibrary(listener, token);
|
| - Token begin = token;
|
| - listener.beginExpressionStatement(token);
|
| - listener.handleIdentifier(token);
|
| - token = token.next;
|
| - if (token.kind === STRING_TOKEN) {
|
| - listener.beginLiteralString(token);
|
| - listener.endLiteralString(0);
|
| - listener.pushNode(new NodeList.singleton(listener.popNode()));
|
| - listener.endSend(token);
|
| - token = token.next;
|
| - listener.endExpressionStatement(token);
|
| - } else {
|
| - listener.pushNode(new NodeList.empty());
|
| - listener.endSend(token);
|
| - listener.endReturnStatement(true, begin, token);
|
| - }
|
| - listener.endFunctionBody(1, begin, token);
|
| - // TODO(ngeoffray): expect a ';'.
|
| - return token.next;
|
| -}
|
| -
|
| -SourceString checkForNativeClass(ElementListener listener) {
|
| - SourceString nativeName;
|
| - Node node = listener.nodes.head;
|
| - if (node != null
|
| - && node.asIdentifier() != null
|
| - && node.asIdentifier().source.stringValue == 'native') {
|
| - nativeName = node.asIdentifier().token.next.value;
|
| - listener.popNode();
|
| - }
|
| - return nativeName;
|
| -}
|
| -
|
| -bool isOverriddenMethod(FunctionElement element,
|
| - ClassElement cls,
|
| - NativeEmitter nativeEmitter) {
|
| - List<ClassElement> subtypes = nativeEmitter.subtypes[cls];
|
| - if (subtypes == null) return false;
|
| - for (ClassElement subtype in subtypes) {
|
| - if (subtype.lookupLocalMember(element.name) != null) return true;
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -void handleSsaNative(SsaBuilder builder, Send node) {
|
| - // Register NoSuchMethodException and captureStackTrace in the compiler
|
| - // because the dynamic dispatch for native classes may use them.
|
| - Compiler compiler = builder.compiler;
|
| - ClassElement cls = compiler.coreLibrary.find(
|
| - Compiler.NO_SUCH_METHOD_EXCEPTION);
|
| - cls.ensureResolved(compiler);
|
| - compiler.addToWorkList(cls.lookupConstructor(cls.name));
|
| - compiler.registerStaticUse(
|
| - compiler.findHelper(new SourceString('captureStackTrace')));
|
| -
|
| - FunctionElement element = builder.work.element;
|
| - element.setNative();
|
| - NativeEmitter nativeEmitter = compiler.emitter.nativeEmitter;
|
| - // If what we're compiling is a getter named 'typeName' and the native
|
| - // class is named 'DOMType', we generate a call to the typeNameOf
|
| - // function attached on the isolate.
|
| - // The DOM classes assume that their 'typeName' property, which is
|
| - // not a JS property on the DOM types, returns the type name.
|
| - if (element.name == const SourceString('typeName')
|
| - && element.isGetter()
|
| - && nativeEmitter.toNativeName(element.enclosingElement) == 'DOMType') {
|
| - DartString jsCode = new DartString.literal(
|
| - '${nativeEmitter.typeNameOfName}(#)');
|
| - List<HInstruction> inputs =
|
| - <HInstruction>[builder.localsHandler.readThis()];
|
| - builder.push(new HForeign(
|
| - jsCode, const LiteralDartString('String'), inputs));
|
| - return;
|
| - }
|
| -
|
| - HInstruction convertDartClosure(Element parameter) {
|
| - HInstruction local = builder.localsHandler.readLocal(parameter);
|
| - // TODO(ngeoffray): by better analyzing the function type and
|
| - // its formal parameters, we could just pass, eg closure.$call$0.
|
| - builder.push(new HStatic(builder.interceptors.getClosureConverter()));
|
| - List<HInstruction> callInputs = <HInstruction>[builder.pop(), local];
|
| - HInstruction closure = new HInvokeStatic(Selector.INVOCATION_1, callInputs);
|
| - builder.add(closure);
|
| - return closure;
|
| - }
|
| -
|
| - FunctionParameters parameters = element.computeParameters(builder.compiler);
|
| - if (node.arguments.isEmpty()) {
|
| - List<String> arguments = <String>[];
|
| - List<HInstruction> inputs = <HInstruction>[];
|
| - String receiver = '';
|
| - if (element.isInstanceMember()) {
|
| - receiver = '#.';
|
| - inputs.add(builder.localsHandler.readThis());
|
| - }
|
| - parameters.forEachParameter((Element parameter) {
|
| - Type type = parameter.computeType(compiler);
|
| - HInstruction input = builder.localsHandler.readLocal(parameter);
|
| - if (type is FunctionType) input = convertDartClosure(parameter);
|
| - inputs.add(input);
|
| - arguments.add('#');
|
| - });
|
| - String foreignParameters = Strings.join(arguments, ',');
|
| -
|
| - String dartMethodName;
|
| - String nativeMethodName = element.name.slowToString();
|
| - 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);
|
| - }
|
| -
|
| - } else if (!node.arguments.tail.isEmpty()) {
|
| - builder.compiler.cancel('More than one argument to native');
|
| - } else {
|
| - // This is JS code written in a Dart file with the construct
|
| - // native """ ... """;. It does not work well with mangling,
|
| - // but there should currently be no clash between leg mangling
|
| - // and the library where this construct is being used. This
|
| - // mangling problem will go away once we switch these libraries
|
| - // to use Leg's 'JS' function.
|
| - parameters.forEachParameter((Element parameter) {
|
| - Type type = parameter.computeType(compiler);
|
| - if (type is FunctionType) {
|
| - HInstruction jsClosure = convertDartClosure(parameter);
|
| - // Because the JS code references the argument name directly,
|
| - // we must keep the name and assign the JS closure to it.
|
| - builder.add(new HForeign(
|
| - new DartString.literal('${parameter.name.slowToString()} = #'),
|
| - const LiteralDartString('void'),
|
| - <HInstruction>[jsClosure]));
|
| - }
|
| - });
|
| - LiteralString jsCode = node.arguments.head;
|
| - builder.push(new HForeign(jsCode.dartString,
|
| - const LiteralDartString('Object'),
|
| - <HInstruction>[]));
|
| - }
|
| -}
|
|
|