| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library native; | 5 library native; |
| 6 | 6 |
| 7 import 'dart:uri'; | 7 import 'dart:uri'; |
| 8 import 'dart2jslib.dart' hide SourceString; | 8 import 'dart2jslib.dart' hide SourceString; |
| 9 import 'elements/elements.dart'; | 9 import 'elements/elements.dart'; |
| 10 import 'js_backend/js_backend.dart'; | 10 import 'js_backend/js_backend.dart'; |
| (...skipping 17 matching lines...) Expand all Loading... |
| 28 } | 28 } |
| 29 | 29 |
| 30 | 30 |
| 31 /** | 31 /** |
| 32 * This could be an abstract class but we use it as a stub for the dart_backend. | 32 * This could be an abstract class but we use it as a stub for the dart_backend. |
| 33 */ | 33 */ |
| 34 class NativeEnqueuer { | 34 class NativeEnqueuer { |
| 35 /// Initial entry point to native enqueuer. | 35 /// Initial entry point to native enqueuer. |
| 36 void processNativeClasses(Collection<LibraryElement> libraries) {} | 36 void processNativeClasses(Collection<LibraryElement> libraries) {} |
| 37 | 37 |
| 38 /// Notification of a main Enqueuer worklist element. For methods, adds |
| 39 /// information from metadata attributes, and computes types instantiated due |
| 40 /// to calling the method. |
| 38 void registerElement(Element element) {} | 41 void registerElement(Element element) {} |
| 39 | 42 |
| 40 /// Method is a member of a native class. | 43 /// Notification of native field. Adds information from metadata attributes. |
| 41 void registerMethod(Element method) {} | 44 void registerField(Element field) {} |
| 42 | 45 |
| 43 /// Compute types instantiated due to getting a native field. | 46 /// Computes types instantiated due to getting a native field. |
| 44 void registerFieldLoad(Element field) {} | 47 void registerFieldLoad(Element field) {} |
| 45 | 48 |
| 46 /// Compute types instantiated due to setting a native field. | 49 /// Computes types instantiated due to setting a native field. |
| 47 void registerFieldStore(Element field) {} | 50 void registerFieldStore(Element field) {} |
| 48 | 51 |
| 49 /** | 52 /** |
| 50 * Handles JS-calls, which can be an instantiation point for types. | 53 * Handles JS-calls, which can be an instantiation point for types. |
| 51 * | 54 * |
| 52 * For example, the following code instantiates and returns native classes | 55 * For example, the following code instantiates and returns native classes |
| 53 * that are `_DOMWindowImpl` or a subtype. | 56 * that are `_DOMWindowImpl` or a subtype. |
| 54 * | 57 * |
| 55 * JS('_DOMWindowImpl', 'window') | 58 * JS('_DOMWindowImpl', 'window') |
| 56 * | 59 * |
| (...skipping 29 matching lines...) Expand all Loading... |
| 86 final queue = new Queue(); | 89 final queue = new Queue(); |
| 87 bool flushing = false; | 90 bool flushing = false; |
| 88 | 91 |
| 89 | 92 |
| 90 final Enqueuer world; | 93 final Enqueuer world; |
| 91 final Compiler compiler; | 94 final Compiler compiler; |
| 92 final bool enableLiveTypeAnalysis; | 95 final bool enableLiveTypeAnalysis; |
| 93 | 96 |
| 94 ClassElement _annotationCreatesClass; | 97 ClassElement _annotationCreatesClass; |
| 95 ClassElement _annotationReturnsClass; | 98 ClassElement _annotationReturnsClass; |
| 99 ClassElement _annotationJsNameClass; |
| 96 | 100 |
| 97 /// Subclasses of [NativeEnqueuerBase] are constructed by the backend. | 101 /// Subclasses of [NativeEnqueuerBase] are constructed by the backend. |
| 98 NativeEnqueuerBase(this.world, this.compiler, this.enableLiveTypeAnalysis); | 102 NativeEnqueuerBase(this.world, this.compiler, this.enableLiveTypeAnalysis); |
| 99 | 103 |
| 100 void processNativeClasses(Collection<LibraryElement> libraries) { | 104 void processNativeClasses(Collection<LibraryElement> libraries) { |
| 101 libraries.forEach(processNativeClassesInLibrary); | 105 libraries.forEach(processNativeClassesInLibrary); |
| 102 if (!enableLiveTypeAnalysis) { | 106 if (!enableLiveTypeAnalysis) { |
| 103 nativeClasses.forEach((c) => enqueueClass(c, 'forced')); | 107 nativeClasses.forEach((c) => enqueueClass(c, 'forced')); |
| 104 flushQueue(); | 108 flushQueue(); |
| 105 } | 109 } |
| 106 } | 110 } |
| 107 | 111 |
| 108 void processNativeClassesInLibrary(LibraryElement library) { | 112 void processNativeClassesInLibrary(LibraryElement library) { |
| 109 // Use implementation to ensure the inclusion of injected members. | 113 // Use implementation to ensure the inclusion of injected members. |
| 110 library.implementation.forEachLocalMember((Element element) { | 114 library.implementation.forEachLocalMember((Element element) { |
| 111 if (element.kind == ElementKind.CLASS) { | 115 if (element.kind == ElementKind.CLASS) { |
| 112 ClassElement classElement = element; | 116 ClassElement classElement = element; |
| 113 if (classElement.isNative()) { | 117 if (classElement.isNative()) { |
| 114 nativeClasses.add(classElement); | 118 nativeClasses.add(classElement); |
| 115 unusedClasses.add(classElement); | 119 unusedClasses.add(classElement); |
| 116 | 120 |
| 117 // Resolve class to ensure the class has valid inheritance info. | 121 // Resolve class to ensure the class has valid inheritance info. |
| 118 classElement.ensureResolved(compiler); | 122 classElement.ensureResolved(compiler); |
| 119 } | 123 } |
| 120 } | 124 } |
| 121 }); | 125 }); |
| 122 } | 126 } |
| 123 | 127 |
| 124 ClassElement get annotationCreatesClass { | 128 ClassElement get annotationCreatesClass { |
| 125 if (_annotationCreatesClass == null) findAnnotationClasses(); | 129 findAnnotationClasses(); |
| 126 return _annotationCreatesClass; | 130 return _annotationCreatesClass; |
| 127 } | 131 } |
| 128 | 132 |
| 129 ClassElement get annotationReturnsClass { | 133 ClassElement get annotationReturnsClass { |
| 130 if (_annotationReturnsClass == null) findAnnotationClasses(); | 134 findAnnotationClasses(); |
| 131 return _annotationReturnsClass; | 135 return _annotationReturnsClass; |
| 132 } | 136 } |
| 133 | 137 |
| 138 ClassElement get annotationJsNameClass { |
| 139 findAnnotationClasses(); |
| 140 return _annotationJsNameClass; |
| 141 } |
| 142 |
| 134 void findAnnotationClasses() { | 143 void findAnnotationClasses() { |
| 144 if (_annotationCreatesClass != null) return; |
| 135 ClassElement find(name) { | 145 ClassElement find(name) { |
| 136 Element e = compiler.findHelper(name); | 146 Element e = compiler.findHelper(name); |
| 137 if (e == null || e is! ClassElement) { | 147 if (e == null || e is! ClassElement) { |
| 138 compiler.cancel("Could not find implementation class '${name}'"); | 148 compiler.cancel("Could not find implementation class '${name}'"); |
| 139 } | 149 } |
| 140 return e; | 150 return e; |
| 141 } | 151 } |
| 142 _annotationCreatesClass = find(const SourceString('Creates')); | 152 _annotationCreatesClass = find(const SourceString('Creates')); |
| 143 _annotationReturnsClass = find(const SourceString('Returns')); | 153 _annotationReturnsClass = find(const SourceString('Returns')); |
| 154 _annotationJsNameClass = find(const SourceString('JSName')); |
| 144 } | 155 } |
| 145 | 156 |
| 157 /// Returns the JSName annotation string or `null` if no JSName annotation is |
| 158 /// present. |
| 159 String findJsNameFromAnnotation(Element element) { |
| 160 String name = null; |
| 161 ClassElement annotationClass = annotationJsNameClass; |
| 162 for (Link<MetadataAnnotation> link = element.metadata; |
| 163 !link.isEmpty; |
| 164 link = link.tail) { |
| 165 MetadataAnnotation annotation = link.head.ensureResolved(compiler); |
| 166 var value = annotation.value; |
| 167 if (value is! ConstructedConstant) continue; |
| 168 if (value.type is! InterfaceType) continue; |
| 169 if (!identical(value.type.element, annotationClass)) continue; |
| 170 |
| 171 var fields = value.fields; |
| 172 // TODO(sra): Better validation of the constant. |
| 173 if (fields.length != 1 || fields[0] is! StringConstant) { |
| 174 PartialMetadataAnnotation partial = annotation; |
| 175 compiler.cancel( |
| 176 'Annotations needs one string: ${partial.parseNode(compiler)}'); |
| 177 } |
| 178 String specString = fields[0].toDartString().slowToString(); |
| 179 if (name == null) { |
| 180 name = specString; |
| 181 } else { |
| 182 PartialMetadataAnnotation partial = annotation; |
| 183 compiler.cancel( |
| 184 'Too many JSName annotations: ${partial.parseNode(compiler)}'); |
| 185 } |
| 186 } |
| 187 return name; |
| 188 } |
| 146 | 189 |
| 147 enqueueClass(ClassElement classElement, cause) { | 190 enqueueClass(ClassElement classElement, cause) { |
| 148 assert(unusedClasses.contains(classElement)); | 191 assert(unusedClasses.contains(classElement)); |
| 149 unusedClasses.remove(classElement); | 192 unusedClasses.remove(classElement); |
| 150 pendingClasses.add(classElement); | 193 pendingClasses.add(classElement); |
| 151 queue.add(() { processClass(classElement, cause); }); | 194 queue.add(() { processClass(classElement, cause); }); |
| 152 } | 195 } |
| 153 | 196 |
| 154 void flushQueue() { | 197 void flushQueue() { |
| 155 if (flushing) return; | 198 if (flushing) return; |
| (...skipping 16 matching lines...) Expand all Loading... |
| 172 // Also parse the node to know all its methods because otherwise it will | 215 // Also parse the node to know all its methods because otherwise it will |
| 173 // only be parsed if there is a call to one of its constructors. | 216 // only be parsed if there is a call to one of its constructors. |
| 174 classElement.parseNode(compiler); | 217 classElement.parseNode(compiler); |
| 175 | 218 |
| 176 if (firstTime) { | 219 if (firstTime) { |
| 177 queue.add(onFirstNativeClass); | 220 queue.add(onFirstNativeClass); |
| 178 } | 221 } |
| 179 } | 222 } |
| 180 | 223 |
| 181 registerElement(Element element) { | 224 registerElement(Element element) { |
| 182 if (element.isFunction()) return registerMethod(element); | 225 if (element.isFunction() || element.isGetter() || element.isSetter()) { |
| 226 return registerMethod(element); |
| 227 } |
| 228 } |
| 229 |
| 230 registerField(Element element) { |
| 231 if (element.enclosingElement.isNative()) { |
| 232 setNativeName(element); |
| 233 } |
| 183 } | 234 } |
| 184 | 235 |
| 185 registerMethod(Element method) { | 236 registerMethod(Element method) { |
| 186 if (isNativeMethod(method)) { | 237 if (isNativeMethod(method)) { |
| 238 setNativeName(method); |
| 187 processNativeBehavior( | 239 processNativeBehavior( |
| 188 NativeBehavior.ofMethod(method, compiler), | 240 NativeBehavior.ofMethod(method, compiler), |
| 189 method); | 241 method); |
| 190 flushQueue(); | 242 flushQueue(); |
| 191 } | 243 } |
| 192 } | 244 } |
| 193 | 245 |
| 246 /// Sets the native name of [element], either from an annotation, or |
| 247 /// defaulting to the Dart name. |
| 248 void setNativeName(Element element) { |
| 249 String name = findJsNameFromAnnotation(element); |
| 250 if (name == null) name = element.name.slowToString(); |
| 251 element.setNative(name); |
| 252 } |
| 253 |
| 194 bool isNativeMethod(Element element) { | 254 bool isNativeMethod(Element element) { |
| 195 if (!element.getLibrary().canUseNative) return false; | 255 if (!element.getLibrary().canUseNative) return false; |
| 196 // Native method? | 256 // Native method? |
| 197 return compiler.withCurrentElement(element, () { | 257 return compiler.withCurrentElement(element, () { |
| 198 Node node = element.parseNode(compiler); | 258 Node node = element.parseNode(compiler); |
| 199 if (node is! FunctionExpression) return false; | 259 if (node is! FunctionExpression) return false; |
| 200 node = node.body; | 260 node = node.body; |
| 201 Token token = node.getBeginToken(); | 261 Token token = node.getBeginToken(); |
| 202 if (identical(token.stringValue, 'native')) return true; | 262 if (identical(token.stringValue, 'native')) return true; |
| 203 return false; | 263 return false; |
| (...skipping 456 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 660 listener.endLiteralString(0); | 720 listener.endLiteralString(0); |
| 661 token = token.next; | 721 token = token.next; |
| 662 } | 722 } |
| 663 listener.endReturnStatement(hasExpression, begin, token); | 723 listener.endReturnStatement(hasExpression, begin, token); |
| 664 // TODO(ngeoffray): expect a ';'. | 724 // TODO(ngeoffray): expect a ';'. |
| 665 // Currently there are method with both native marker and Dart body. | 725 // Currently there are method with both native marker and Dart body. |
| 666 return token.next; | 726 return token.next; |
| 667 } | 727 } |
| 668 | 728 |
| 669 SourceString checkForNativeClass(ElementListener listener) { | 729 SourceString checkForNativeClass(ElementListener listener) { |
| 670 SourceString nativeName; | 730 SourceString nativeTagInfo; |
| 671 Node node = listener.nodes.head; | 731 Node node = listener.nodes.head; |
| 672 if (node != null | 732 if (node != null |
| 673 && node.asIdentifier() != null | 733 && node.asIdentifier() != null |
| 674 && node.asIdentifier().source.stringValue == 'native') { | 734 && node.asIdentifier().source.stringValue == 'native') { |
| 675 nativeName = node.asIdentifier().token.next.value; | 735 nativeTagInfo = node.asIdentifier().token.next.value; |
| 676 listener.popNode(); | 736 listener.popNode(); |
| 677 } | 737 } |
| 678 return nativeName; | 738 return nativeTagInfo; |
| 679 } | 739 } |
| 680 | 740 |
| 681 bool isOverriddenMethod(FunctionElement element, | 741 bool isOverriddenMethod(FunctionElement element, |
| 682 ClassElement cls, | 742 ClassElement cls, |
| 683 NativeEmitter nativeEmitter) { | 743 NativeEmitter nativeEmitter) { |
| 684 List<ClassElement> subtypes = nativeEmitter.subtypes[cls]; | 744 List<ClassElement> subtypes = nativeEmitter.subtypes[cls]; |
| 685 if (subtypes == null) return false; | 745 if (subtypes == null) return false; |
| 686 for (ClassElement subtype in subtypes) { | 746 for (ClassElement subtype in subtypes) { |
| 687 if (subtype.lookupLocalMember(element.name) != null) return true; | 747 if (subtype.lookupLocalMember(element.name) != null) return true; |
| 688 } | 748 } |
| 689 return false; | 749 return false; |
| 690 } | 750 } |
| 691 | 751 |
| 692 final RegExp nativeRedirectionRegExp = new RegExp(r'^[a-zA-Z][a-zA-Z_$0-9]*$'); | 752 final RegExp nativeRedirectionRegExp = new RegExp(r'^[a-zA-Z][a-zA-Z_$0-9]*$'); |
| 693 | 753 |
| 694 void handleSsaNative(SsaBuilder builder, Expression nativeBody) { | 754 void handleSsaNative(SsaBuilder builder, Expression nativeBody) { |
| 695 Compiler compiler = builder.compiler; | 755 Compiler compiler = builder.compiler; |
| 696 FunctionElement element = builder.work.element; | 756 FunctionElement element = builder.work.element; |
| 697 element.setNative(); | |
| 698 NativeEmitter nativeEmitter = builder.emitter.nativeEmitter; | 757 NativeEmitter nativeEmitter = builder.emitter.nativeEmitter; |
| 699 // If what we're compiling is a getter named 'typeName' and the native | 758 // If what we're compiling is a getter named 'typeName' and the native |
| 700 // class is named 'DOMType', we generate a call to the typeNameOf | 759 // class is named 'DOMType', we generate a call to the typeNameOf |
| 701 // function attached on the isolate. | 760 // function attached on the isolate. |
| 702 // The DOM classes assume that their 'typeName' property, which is | 761 // The DOM classes assume that their 'typeName' property, which is |
| 703 // not a JS property on the DOM types, returns the type name. | 762 // not a JS property on the DOM types, returns the type name. |
| 704 if (element.name == const SourceString('typeName') | 763 if (element.name == const SourceString('typeName') |
| 705 && element.isGetter() | 764 && element.isGetter() |
| 706 && nativeEmitter.toNativeName(element.getEnclosingClass()) == 'DOMType') { | 765 && nativeEmitter.toNativeName(element.getEnclosingClass()) == 'DOMType') { |
| 707 Element helper = | 766 Element helper = |
| 708 compiler.findHelper(const SourceString('getTypeNameOf')); | 767 compiler.findHelper(const SourceString('getTypeNameOf')); |
| 709 builder.pushInvokeHelper1(helper, builder.localsHandler.readThis()); | 768 builder.pushInvokeHelper1(helper, builder.localsHandler.readThis()); |
| 710 builder.close(new HReturn(builder.pop())).addSuccessor(builder.graph.exit); | 769 builder.close(new HReturn(builder.pop())).addSuccessor(builder.graph.exit); |
| 711 } | 770 } |
| 712 | 771 |
| 713 HInstruction convertDartClosure(Element parameter, FunctionType type) { | 772 HInstruction convertDartClosure(Element parameter, FunctionType type) { |
| 714 HInstruction local = builder.localsHandler.readLocal(parameter); | 773 HInstruction local = builder.localsHandler.readLocal(parameter); |
| 715 Constant arityConstant = | 774 Constant arityConstant = |
| 716 builder.constantSystem.createInt(type.computeArity()); | 775 builder.constantSystem.createInt(type.computeArity()); |
| 717 HInstruction arity = builder.graph.addConstant(arityConstant); | 776 HInstruction arity = builder.graph.addConstant(arityConstant); |
| 718 // TODO(ngeoffray): For static methods, we could pass a method with a | 777 // TODO(ngeoffray): For static methods, we could pass a method with a |
| 719 // defined arity. | 778 // defined arity. |
| 720 Element helper = builder.interceptors.getClosureConverter(); | 779 Element helper = builder.interceptors.getClosureConverter(); |
| 721 builder.pushInvokeHelper2(helper, local, arity); | 780 builder.pushInvokeHelper2(helper, local, arity); |
| 722 HInstruction closure = builder.pop(); | 781 HInstruction closure = builder.pop(); |
| 723 return closure; | 782 return closure; |
| 724 } | 783 } |
| 725 | 784 |
| 726 // Check which pattern this native method follows: | 785 // Check which pattern this native method follows: |
| 727 // 1) foo() native; hasBody = false, isRedirecting = false | 786 // 1) foo() native; |
| 728 // 2) foo() native "bar"; hasBody = false, isRedirecting = true | 787 // hasBody = false, isRedirecting = false |
| 729 // 3) foo() native "return 42"; hasBody = true, isRedirecting = false | 788 // 2) foo() native "bar"; |
| 789 // hasBody = false, isRedirecting = true, no longer supported. |
| 790 // 3) foo() native "return 42"; |
| 791 // hasBody = true, isRedirecting = false |
| 730 bool hasBody = false; | 792 bool hasBody = false; |
| 731 bool isRedirecting = false; | 793 assert(element.isNative()); |
| 732 String nativeMethodName = element.name.slowToString(); | 794 String nativeMethodName = element.nativeName(); |
| 733 if (nativeBody != null) { | 795 if (nativeBody != null) { |
| 734 LiteralString jsCode = nativeBody.asLiteralString(); | 796 LiteralString jsCode = nativeBody.asLiteralString(); |
| 735 String str = jsCode.dartString.slowToString(); | 797 String str = jsCode.dartString.slowToString(); |
| 736 if (nativeRedirectionRegExp.hasMatch(str)) { | 798 if (nativeRedirectionRegExp.hasMatch(str)) { |
| 737 nativeMethodName = str; | 799 compiler.cancel("Deprecated syntax, use @JSName('name') instead.", |
| 738 isRedirecting = true; | 800 node: nativeBody); |
| 739 nativeEmitter.addRedirectingMethod(element, nativeMethodName); | |
| 740 } else { | |
| 741 hasBody = true; | |
| 742 } | 801 } |
| 802 hasBody = true; |
| 743 } | 803 } |
| 744 | 804 |
| 745 if (!hasBody) { | 805 if (!hasBody) { |
| 746 nativeEmitter.nativeMethods.add(element); | 806 nativeEmitter.nativeMethods.add(element); |
| 747 } | 807 } |
| 748 | 808 |
| 749 FunctionSignature parameters = element.computeSignature(builder.compiler); | 809 FunctionSignature parameters = element.computeSignature(builder.compiler); |
| 750 if (!hasBody) { | 810 if (!hasBody) { |
| 751 List<String> arguments = <String>[]; | 811 List<String> arguments = <String>[]; |
| 752 List<HInstruction> inputs = <HInstruction>[]; | 812 List<HInstruction> inputs = <HInstruction>[]; |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 802 builder.add(new HForeign( | 862 builder.add(new HForeign( |
| 803 new DartString.literal('${parameter.name.slowToString()} = #'), | 863 new DartString.literal('${parameter.name.slowToString()} = #'), |
| 804 const LiteralDartString('void'), | 864 const LiteralDartString('void'), |
| 805 <HInstruction>[jsClosure])); | 865 <HInstruction>[jsClosure])); |
| 806 } | 866 } |
| 807 }); | 867 }); |
| 808 LiteralString jsCode = nativeBody.asLiteralString(); | 868 LiteralString jsCode = nativeBody.asLiteralString(); |
| 809 builder.push(new HForeign.statement(jsCode.dartString, <HInstruction>[])); | 869 builder.push(new HForeign.statement(jsCode.dartString, <HInstruction>[])); |
| 810 } | 870 } |
| 811 } | 871 } |
| OLD | NEW |