Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1370)

Side by Side Diff: sdk/lib/_internal/compiler/implementation/native_handler.dart

Issue 11419250: Add @JSName annotation for native fields and methods - second try. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698