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. | |
41 void registerElement(Element element) {} | 38 void registerElement(Element element) {} |
42 | 39 |
43 /// Notification of native field. Adds information from metadata attributes. | 40 /// Method is a member of a native class. |
44 void registerField(Element field) {} | 41 void registerMethod(Element method) {} |
45 | 42 |
46 /// Computes types instantiated due to getting a native field. | 43 /// Compute types instantiated due to getting a native field. |
47 void registerFieldLoad(Element field) {} | 44 void registerFieldLoad(Element field) {} |
48 | 45 |
49 /// Computes types instantiated due to setting a native field. | 46 /// Compute types instantiated due to setting a native field. |
50 void registerFieldStore(Element field) {} | 47 void registerFieldStore(Element field) {} |
51 | 48 |
52 /** | 49 /** |
53 * Handles JS-calls, which can be an instantiation point for types. | 50 * Handles JS-calls, which can be an instantiation point for types. |
54 * | 51 * |
55 * For example, the following code instantiates and returns native classes | 52 * For example, the following code instantiates and returns native classes |
56 * that are `_DOMWindowImpl` or a subtype. | 53 * that are `_DOMWindowImpl` or a subtype. |
57 * | 54 * |
58 * JS('_DOMWindowImpl', 'window') | 55 * JS('_DOMWindowImpl', 'window') |
59 * | 56 * |
(...skipping 29 matching lines...) Expand all Loading... |
89 final queue = new Queue(); | 86 final queue = new Queue(); |
90 bool flushing = false; | 87 bool flushing = false; |
91 | 88 |
92 | 89 |
93 final Enqueuer world; | 90 final Enqueuer world; |
94 final Compiler compiler; | 91 final Compiler compiler; |
95 final bool enableLiveTypeAnalysis; | 92 final bool enableLiveTypeAnalysis; |
96 | 93 |
97 ClassElement _annotationCreatesClass; | 94 ClassElement _annotationCreatesClass; |
98 ClassElement _annotationReturnsClass; | 95 ClassElement _annotationReturnsClass; |
99 ClassElement _annotationJsNameClass; | |
100 | 96 |
101 /// Subclasses of [NativeEnqueuerBase] are constructed by the backend. | 97 /// Subclasses of [NativeEnqueuerBase] are constructed by the backend. |
102 NativeEnqueuerBase(this.world, this.compiler, this.enableLiveTypeAnalysis); | 98 NativeEnqueuerBase(this.world, this.compiler, this.enableLiveTypeAnalysis); |
103 | 99 |
104 void processNativeClasses(Collection<LibraryElement> libraries) { | 100 void processNativeClasses(Collection<LibraryElement> libraries) { |
105 libraries.forEach(processNativeClassesInLibrary); | 101 libraries.forEach(processNativeClassesInLibrary); |
106 if (!enableLiveTypeAnalysis) { | 102 if (!enableLiveTypeAnalysis) { |
107 nativeClasses.forEach((c) => enqueueClass(c, 'forced')); | 103 nativeClasses.forEach((c) => enqueueClass(c, 'forced')); |
108 flushQueue(); | 104 flushQueue(); |
109 } | 105 } |
110 } | 106 } |
111 | 107 |
112 void processNativeClassesInLibrary(LibraryElement library) { | 108 void processNativeClassesInLibrary(LibraryElement library) { |
113 // Use implementation to ensure the inclusion of injected members. | 109 // Use implementation to ensure the inclusion of injected members. |
114 library.implementation.forEachLocalMember((Element element) { | 110 library.implementation.forEachLocalMember((Element element) { |
115 if (element.kind == ElementKind.CLASS) { | 111 if (element.kind == ElementKind.CLASS) { |
116 ClassElement classElement = element; | 112 ClassElement classElement = element; |
117 if (classElement.isNative()) { | 113 if (classElement.isNative()) { |
118 nativeClasses.add(classElement); | 114 nativeClasses.add(classElement); |
119 unusedClasses.add(classElement); | 115 unusedClasses.add(classElement); |
120 | 116 |
121 // Resolve class to ensure the class has valid inheritance info. | 117 // Resolve class to ensure the class has valid inheritance info. |
122 classElement.ensureResolved(compiler); | 118 classElement.ensureResolved(compiler); |
123 } | 119 } |
124 } | 120 } |
125 }); | 121 }); |
126 } | 122 } |
127 | 123 |
128 ClassElement get annotationCreatesClass { | 124 ClassElement get annotationCreatesClass { |
129 findAnnotationClasses(); | 125 if (_annotationCreatesClass == null) findAnnotationClasses(); |
130 return _annotationCreatesClass; | 126 return _annotationCreatesClass; |
131 } | 127 } |
132 | 128 |
133 ClassElement get annotationReturnsClass { | 129 ClassElement get annotationReturnsClass { |
134 findAnnotationClasses(); | 130 if (_annotationReturnsClass == null) findAnnotationClasses(); |
135 return _annotationReturnsClass; | 131 return _annotationReturnsClass; |
136 } | 132 } |
137 | 133 |
138 ClassElement get annotationJsNameClass { | |
139 findAnnotationClasses(); | |
140 return _annotationJsNameClass; | |
141 } | |
142 | |
143 void findAnnotationClasses() { | 134 void findAnnotationClasses() { |
144 if (_annotationCreatesClass != null) return; | |
145 ClassElement find(name) { | 135 ClassElement find(name) { |
146 Element e = compiler.findHelper(name); | 136 Element e = compiler.findHelper(name); |
147 if (e == null || e is! ClassElement) { | 137 if (e == null || e is! ClassElement) { |
148 compiler.cancel("Could not find implementation class '${name}'"); | 138 compiler.cancel("Could not find implementation class '${name}'"); |
149 } | 139 } |
150 return e; | 140 return e; |
151 } | 141 } |
152 _annotationCreatesClass = find(const SourceString('Creates')); | 142 _annotationCreatesClass = find(const SourceString('Creates')); |
153 _annotationReturnsClass = find(const SourceString('Returns')); | 143 _annotationReturnsClass = find(const SourceString('Returns')); |
154 _annotationJsNameClass = find(const SourceString('JSName')); | |
155 } | 144 } |
156 | 145 |
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 } | |
189 | 146 |
190 enqueueClass(ClassElement classElement, cause) { | 147 enqueueClass(ClassElement classElement, cause) { |
191 assert(unusedClasses.contains(classElement)); | 148 assert(unusedClasses.contains(classElement)); |
192 unusedClasses.remove(classElement); | 149 unusedClasses.remove(classElement); |
193 pendingClasses.add(classElement); | 150 pendingClasses.add(classElement); |
194 queue.add(() { processClass(classElement, cause); }); | 151 queue.add(() { processClass(classElement, cause); }); |
195 } | 152 } |
196 | 153 |
197 void flushQueue() { | 154 void flushQueue() { |
198 if (flushing) return; | 155 if (flushing) return; |
(...skipping 19 matching lines...) Expand all Loading... |
218 | 175 |
219 if (firstTime) { | 176 if (firstTime) { |
220 queue.add(onFirstNativeClass); | 177 queue.add(onFirstNativeClass); |
221 } | 178 } |
222 } | 179 } |
223 | 180 |
224 registerElement(Element element) { | 181 registerElement(Element element) { |
225 if (element.isFunction()) return registerMethod(element); | 182 if (element.isFunction()) return registerMethod(element); |
226 } | 183 } |
227 | 184 |
228 registerField(Element element) { | |
229 if (element.enclosingElement.isNative()) { | |
230 setNativeName(element); | |
231 } | |
232 } | |
233 | |
234 registerMethod(Element method) { | 185 registerMethod(Element method) { |
235 if (isNativeMethod(method)) { | 186 if (isNativeMethod(method)) { |
236 setNativeName(method); | |
237 processNativeBehavior( | 187 processNativeBehavior( |
238 NativeBehavior.ofMethod(method, compiler), | 188 NativeBehavior.ofMethod(method, compiler), |
239 method); | 189 method); |
240 flushQueue(); | 190 flushQueue(); |
241 } | 191 } |
242 } | 192 } |
243 | 193 |
244 /// Sets the native name of [element], either from an annotation, or | |
245 /// defaulting to the Dart name. | |
246 void setNativeName(Element element) { | |
247 String name = findJsNameFromAnnotation(element); | |
248 if (name == null) name = element.name.slowToString(); | |
249 element.setNative(name); | |
250 } | |
251 | |
252 bool isNativeMethod(Element element) { | 194 bool isNativeMethod(Element element) { |
253 if (!element.getLibrary().canUseNative) return false; | 195 if (!element.getLibrary().canUseNative) return false; |
254 // Native method? | 196 // Native method? |
255 return compiler.withCurrentElement(element, () { | 197 return compiler.withCurrentElement(element, () { |
256 Node node = element.parseNode(compiler); | 198 Node node = element.parseNode(compiler); |
257 if (node is! FunctionExpression) return false; | 199 if (node is! FunctionExpression) return false; |
258 node = node.body; | 200 node = node.body; |
259 Token token = node.getBeginToken(); | 201 Token token = node.getBeginToken(); |
260 if (identical(token.stringValue, 'native')) return true; | 202 if (identical(token.stringValue, 'native')) return true; |
261 return false; | 203 return false; |
(...skipping 455 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
717 listener.endLiteralString(0); | 659 listener.endLiteralString(0); |
718 token = token.next; | 660 token = token.next; |
719 } | 661 } |
720 listener.endReturnStatement(hasExpression, begin, token); | 662 listener.endReturnStatement(hasExpression, begin, token); |
721 // TODO(ngeoffray): expect a ';'. | 663 // TODO(ngeoffray): expect a ';'. |
722 // Currently there are method with both native marker and Dart body. | 664 // Currently there are method with both native marker and Dart body. |
723 return token.next; | 665 return token.next; |
724 } | 666 } |
725 | 667 |
726 SourceString checkForNativeClass(ElementListener listener) { | 668 SourceString checkForNativeClass(ElementListener listener) { |
727 SourceString nativeTagInfo; | 669 SourceString nativeName; |
728 Node node = listener.nodes.head; | 670 Node node = listener.nodes.head; |
729 if (node != null | 671 if (node != null |
730 && node.asIdentifier() != null | 672 && node.asIdentifier() != null |
731 && node.asIdentifier().source.stringValue == 'native') { | 673 && node.asIdentifier().source.stringValue == 'native') { |
732 nativeTagInfo = node.asIdentifier().token.next.value; | 674 nativeName = node.asIdentifier().token.next.value; |
733 listener.popNode(); | 675 listener.popNode(); |
734 } | 676 } |
735 return nativeTagInfo; | 677 return nativeName; |
736 } | 678 } |
737 | 679 |
738 bool isOverriddenMethod(FunctionElement element, | 680 bool isOverriddenMethod(FunctionElement element, |
739 ClassElement cls, | 681 ClassElement cls, |
740 NativeEmitter nativeEmitter) { | 682 NativeEmitter nativeEmitter) { |
741 List<ClassElement> subtypes = nativeEmitter.subtypes[cls]; | 683 List<ClassElement> subtypes = nativeEmitter.subtypes[cls]; |
742 if (subtypes == null) return false; | 684 if (subtypes == null) return false; |
743 for (ClassElement subtype in subtypes) { | 685 for (ClassElement subtype in subtypes) { |
744 if (subtype.lookupLocalMember(element.name) != null) return true; | 686 if (subtype.lookupLocalMember(element.name) != null) return true; |
745 } | 687 } |
746 return false; | 688 return false; |
747 } | 689 } |
748 | 690 |
749 final RegExp nativeRedirectionRegExp = new RegExp(r'^[a-zA-Z][a-zA-Z_$0-9]*$'); | 691 final RegExp nativeRedirectionRegExp = new RegExp(r'^[a-zA-Z][a-zA-Z_$0-9]*$'); |
750 | 692 |
751 void handleSsaNative(SsaBuilder builder, Expression nativeBody) { | 693 void handleSsaNative(SsaBuilder builder, Expression nativeBody) { |
752 Compiler compiler = builder.compiler; | 694 Compiler compiler = builder.compiler; |
753 FunctionElement element = builder.work.element; | 695 FunctionElement element = builder.work.element; |
| 696 element.setNative(); |
754 NativeEmitter nativeEmitter = builder.emitter.nativeEmitter; | 697 NativeEmitter nativeEmitter = builder.emitter.nativeEmitter; |
755 // If what we're compiling is a getter named 'typeName' and the native | 698 // If what we're compiling is a getter named 'typeName' and the native |
756 // class is named 'DOMType', we generate a call to the typeNameOf | 699 // class is named 'DOMType', we generate a call to the typeNameOf |
757 // function attached on the isolate. | 700 // function attached on the isolate. |
758 // The DOM classes assume that their 'typeName' property, which is | 701 // The DOM classes assume that their 'typeName' property, which is |
759 // not a JS property on the DOM types, returns the type name. | 702 // not a JS property on the DOM types, returns the type name. |
760 if (element.name == const SourceString('typeName') | 703 if (element.name == const SourceString('typeName') |
761 && element.isGetter() | 704 && element.isGetter() |
762 && nativeEmitter.toNativeName(element.getEnclosingClass()) == 'DOMType') { | 705 && nativeEmitter.toNativeName(element.getEnclosingClass()) == 'DOMType') { |
763 Element helper = | 706 Element helper = |
764 compiler.findHelper(const SourceString('getTypeNameOf')); | 707 compiler.findHelper(const SourceString('getTypeNameOf')); |
765 builder.pushInvokeHelper1(helper, builder.localsHandler.readThis()); | 708 builder.pushInvokeHelper1(helper, builder.localsHandler.readThis()); |
766 builder.close(new HReturn(builder.pop())).addSuccessor(builder.graph.exit); | 709 builder.close(new HReturn(builder.pop())).addSuccessor(builder.graph.exit); |
767 } | 710 } |
768 | 711 |
769 HInstruction convertDartClosure(Element parameter, FunctionType type) { | 712 HInstruction convertDartClosure(Element parameter, FunctionType type) { |
770 HInstruction local = builder.localsHandler.readLocal(parameter); | 713 HInstruction local = builder.localsHandler.readLocal(parameter); |
771 Constant arityConstant = | 714 Constant arityConstant = |
772 builder.constantSystem.createInt(type.computeArity()); | 715 builder.constantSystem.createInt(type.computeArity()); |
773 HInstruction arity = builder.graph.addConstant(arityConstant); | 716 HInstruction arity = builder.graph.addConstant(arityConstant); |
774 // TODO(ngeoffray): For static methods, we could pass a method with a | 717 // TODO(ngeoffray): For static methods, we could pass a method with a |
775 // defined arity. | 718 // defined arity. |
776 Element helper = builder.interceptors.getClosureConverter(); | 719 Element helper = builder.interceptors.getClosureConverter(); |
777 builder.pushInvokeHelper2(helper, local, arity); | 720 builder.pushInvokeHelper2(helper, local, arity); |
778 HInstruction closure = builder.pop(); | 721 HInstruction closure = builder.pop(); |
779 return closure; | 722 return closure; |
780 } | 723 } |
781 | 724 |
782 // Check which pattern this native method follows: | 725 // Check which pattern this native method follows: |
783 // 1) foo() native; | 726 // 1) foo() native; hasBody = false, isRedirecting = false |
784 // hasBody = false, isRedirecting = false | 727 // 2) foo() native "bar"; hasBody = false, isRedirecting = true |
785 // 2) foo() native "bar"; | 728 // 3) foo() native "return 42"; hasBody = true, isRedirecting = false |
786 // hasBody = false, isRedirecting = true, no longer supported. | |
787 // 3) foo() native "return 42"; | |
788 // hasBody = true, isRedirecting = false | |
789 bool hasBody = false; | 729 bool hasBody = false; |
790 String nativeMethodName = element.nativeName(); | 730 bool isRedirecting = false; |
| 731 String nativeMethodName = element.name.slowToString(); |
791 if (nativeBody != null) { | 732 if (nativeBody != null) { |
792 LiteralString jsCode = nativeBody.asLiteralString(); | 733 LiteralString jsCode = nativeBody.asLiteralString(); |
793 String str = jsCode.dartString.slowToString(); | 734 String str = jsCode.dartString.slowToString(); |
794 if (nativeRedirectionRegExp.hasMatch(str)) { | 735 if (nativeRedirectionRegExp.hasMatch(str)) { |
795 compiler.cancel("Deprecated syntax, use @JSName('name') instead.", | 736 nativeMethodName = str; |
796 node: nativeBody); | 737 isRedirecting = true; |
| 738 nativeEmitter.addRedirectingMethod(element, nativeMethodName); |
| 739 } else { |
| 740 hasBody = true; |
797 } | 741 } |
798 hasBody = true; | |
799 } | 742 } |
800 | 743 |
801 if (!hasBody) { | 744 if (!hasBody) { |
802 nativeEmitter.nativeMethods.add(element); | 745 nativeEmitter.nativeMethods.add(element); |
803 } | 746 } |
804 | 747 |
805 FunctionSignature parameters = element.computeSignature(builder.compiler); | 748 FunctionSignature parameters = element.computeSignature(builder.compiler); |
806 if (!hasBody) { | 749 if (!hasBody) { |
807 List<String> arguments = <String>[]; | 750 List<String> arguments = <String>[]; |
808 List<HInstruction> inputs = <HInstruction>[]; | 751 List<HInstruction> inputs = <HInstruction>[]; |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
902 String parameters) { | 845 String parameters) { |
903 buffer.add(" if (Object.getPrototypeOf(this).hasOwnProperty"); | 846 buffer.add(" if (Object.getPrototypeOf(this).hasOwnProperty"); |
904 buffer.add("('$methodName')) {\n"); | 847 buffer.add("('$methodName')) {\n"); |
905 buffer.add(" $code"); | 848 buffer.add(" $code"); |
906 buffer.add(" } else {\n"); | 849 buffer.add(" } else {\n"); |
907 buffer.add(" return Object.prototype.$methodName.call(this"); | 850 buffer.add(" return Object.prototype.$methodName.call(this"); |
908 buffer.add(parameters == '' ? '' : ', $parameters'); | 851 buffer.add(parameters == '' ? '' : ', $parameters'); |
909 buffer.add(");\n"); | 852 buffer.add(");\n"); |
910 buffer.add(" }\n"); | 853 buffer.add(" }\n"); |
911 } | 854 } |
OLD | NEW |