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

Side by Side Diff: pkg/compiler/lib/src/native/resolver.dart

Issue 2732793002: Add NativeDataResolver (Closed)
Patch Set: Cleanup. Created 3 years, 9 months 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
OLDNEW
(Empty)
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
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.
4
5 import 'package:front_end/src/fasta/scanner.dart' show StringToken, Token;
6
7 import '../common.dart';
8 import '../common/backend_api.dart';
9 import '../compiler.dart' show Compiler;
10 import '../constants/values.dart';
11 import '../elements/elements.dart'
12 show
13 ClassElement,
14 Element,
15 LibraryElement,
16 MemberElement,
17 MetadataAnnotation,
18 MethodElement;
19 import '../elements/modelx.dart' show FunctionElementX, MetadataAnnotationX;
20 import '../elements/resolution_types.dart' show ResolutionDartType;
21 import '../js_backend/js_backend.dart';
22 import '../js_backend/native_data.dart';
23 import '../patch_parser.dart';
24 import '../tree/tree.dart';
25 import 'behavior.dart';
26
27 abstract class NativeDataResolver {
28 /// Returns `true` if [element] is a JsInterop member.
29 bool isJsInteropMember(MemberElement element);
30
31 /// Computes whether [element] is native or JsInterop and, if so, registers
32 /// its [NativeBehavior]s to [registry].
33 void resolveNativeMember(MemberElement element, NativeRegistry registry);
34 }
35
36 class NativeDataResolverImpl implements NativeDataResolver {
37 static final RegExp _identifier = new RegExp(r'^[a-zA-Z_$][a-zA-Z0-9_$]*$');
38
39 final Compiler _compiler;
40
41 NativeDataResolverImpl(this._compiler);
42
43 JavaScriptBackend get _backend => _compiler.backend;
44 DiagnosticReporter get _reporter => _compiler.reporter;
45 NativeClassData get _nativeClassData => _backend.nativeClassData;
46 NativeDataBuilder get _nativeDataBuilder => _backend.nativeDataBuilder;
47
48 @override
49 bool isJsInteropMember(MemberElement element) {
50 // TODO(johnniwinther): Avoid computing this twice for external function;
51 // once from JavaScriptBackendTarget.resolveExternalFunction and once
52 // through JavaScriptBackendTarget.resolveNativeMember.
53 bool isJsInterop =
54 checkJsInteropMemberAnnotations(_compiler, element, _nativeDataBuilder);
55 if (!isJsInterop) {
56 if (element.enclosingClass != null) {
57 isJsInterop = _nativeClassData.isJsInteropClass(element.enclosingClass);
58 } else {
59 isJsInterop = _nativeClassData.isJsInteropLibrary(element.library);
60 }
61 }
62 return isJsInterop;
63 }
64
65 void resolveNativeMember(MemberElement element, NativeRegistry registry) {
66 bool isJsInterop = isJsInteropMember(element);
67 if (element.isFunction ||
68 element.isConstructor ||
69 element.isGetter ||
70 element.isSetter) {
71 bool isNative = _processMethodAnnotations(element);
72 if (isNative || isJsInterop) {
73 NativeBehavior behavior = NativeBehavior
74 .ofMethodElement(element, _compiler, isJsInterop: isJsInterop);
75 _nativeDataBuilder.setNativeMethodBehavior(element, behavior);
76 registry.registerNativeData(behavior);
77 }
78 } else if (element.isField) {
79 bool isNative = _processFieldAnnotations(element);
80 if (isNative || isJsInterop) {
81 NativeBehavior fieldLoadBehavior = NativeBehavior
82 .ofFieldElementLoad(element, _compiler, isJsInterop: isJsInterop);
83 NativeBehavior fieldStoreBehavior =
84 NativeBehavior.ofFieldElementStore(element, _compiler);
85 _nativeDataBuilder.setNativeFieldLoadBehavior(
86 element, fieldLoadBehavior);
87 _nativeDataBuilder.setNativeFieldStoreBehavior(
88 element, fieldStoreBehavior);
89
90 // TODO(sra): Process fields for storing separately.
91 // We have to handle both loading and storing to the field because we
92 // only get one look at each member and there might be a load or store
93 // we have not seen yet.
94 registry.registerNativeData(fieldLoadBehavior);
95 registry.registerNativeData(fieldStoreBehavior);
96 }
97 }
98 }
99
100 /// Process the potentially native [field]. Adds information from metadata
101 /// attributes. Returns `true` of [method] is native.
102 bool _processFieldAnnotations(Element element) {
103 if (_compiler.serialization.isDeserialized(element)) {
104 return false;
105 }
106 if (element.isInstanceMember &&
107 _backend.nativeClassData.isNativeClass(element.enclosingClass)) {
108 // Exclude non-instance (static) fields - they are not really native and
109 // are compiled as isolate globals. Access of a property of a constructor
110 // function or a non-method property in the prototype chain, must be coded
111 // using a JS-call.
112 _setNativeName(element);
113 return true;
114 }
115 return false;
116 }
117
118 /// Process the potentially native [method]. Adds information from metadata
119 /// attributes. Returns `true` of [method] is native.
120 bool _processMethodAnnotations(Element method) {
121 if (_compiler.serialization.isDeserialized(method)) {
122 return false;
123 }
124 if (_isNativeMethod(method)) {
125 if (method.isStatic) {
126 _setNativeNameForStaticMethod(method);
127 } else {
128 _setNativeName(method);
129 }
130 return true;
131 }
132 return false;
133 }
134
135 /// Sets the native name of [element], either from an annotation, or
136 /// defaulting to the Dart name.
137 void _setNativeName(MemberElement element) {
138 String name = _findJsNameFromAnnotation(element);
139 if (name == null) name = element.name;
140 _nativeDataBuilder.setNativeMemberName(element, name);
141 }
142
143 /// Sets the native name of the static native method [element], using the
144 /// following rules:
145 /// 1. If [element] has a @JSName annotation that is an identifier, qualify
146 /// that identifier to the @Native name of the enclosing class
147 /// 2. If [element] has a @JSName annotation that is not an identifier,
148 /// use the declared @JSName as the expression
149 /// 3. If [element] does not have a @JSName annotation, qualify the name of
150 /// the method with the @Native name of the enclosing class.
151 void _setNativeNameForStaticMethod(MethodElement element) {
152 String name = _findJsNameFromAnnotation(element);
153 if (name == null) name = element.name;
154 if (_isIdentifier(name)) {
155 List<String> nativeNames =
156 _nativeDataBuilder.getNativeTagsOfClassRaw(element.enclosingClass);
157 if (nativeNames.length != 1) {
158 _reporter.internalError(
159 element,
160 'Unable to determine a native name for the enclosing class, '
161 'options: $nativeNames');
162 }
163 _nativeDataBuilder.setNativeMemberName(
164 element, '${nativeNames[0]}.$name');
165 } else {
166 _nativeDataBuilder.setNativeMemberName(element, name);
167 }
168 }
169
170 bool _isIdentifier(String s) => _identifier.hasMatch(s);
171
172 bool _isNativeMethod(FunctionElementX element) {
173 if (!_backend.canLibraryUseNative(element.library)) return false;
174 // Native method?
175 return _reporter.withCurrentElement(element, () {
176 Node node = element.parseNode(_compiler.resolution.parsingContext);
177 if (node is! FunctionExpression) return false;
178 FunctionExpression functionExpression = node;
179 node = functionExpression.body;
180 Token token = node.getBeginToken();
181 if (identical(token.stringValue, 'native')) return true;
182 return false;
183 });
184 }
185
186 /// Returns the JSName annotation string or `null` if no JSName annotation is
187 /// present.
188 String _findJsNameFromAnnotation(Element element) {
189 String name = null;
190 ClassElement annotationClass = _backend.helpers.annotationJSNameClass;
191 for (MetadataAnnotation annotation in element.implementation.metadata) {
192 annotation.ensureResolved(_compiler.resolution);
193 ConstantValue value =
194 _compiler.constants.getConstantValue(annotation.constant);
195 if (!value.isConstructedObject) continue;
196 ConstructedConstantValue constructedObject = value;
197 if (constructedObject.type.element != annotationClass) continue;
198
199 Iterable<ConstantValue> fields = constructedObject.fields.values;
200 // TODO(sra): Better validation of the constant.
201 if (fields.length != 1 || fields.single is! StringConstantValue) {
202 _reporter.internalError(
203 annotation, 'Annotations needs one string: ${annotation}');
204 }
205 StringConstantValue specStringConstant = fields.single;
206 String specString = specStringConstant.toDartString().slowToString();
207 if (name == null) {
208 name = specString;
209 } else {
210 _reporter.internalError(
211 annotation, 'Too many JSName annotations: ${annotation}');
212 }
213 }
214 return name;
215 }
216 }
217
218 /// Check whether [cls] has a `@Native(...)` annotation, and if so, set its
219 /// native name from the annotation.
220 checkNativeAnnotation(Compiler compiler, ClassElement cls,
221 NativeClassDataBuilder nativeClassDataBuilder) {
222 EagerAnnotationHandler.checkAnnotation(
223 compiler, cls, new NativeAnnotationHandler(nativeClassDataBuilder));
224 }
225
226 /// Annotation handler for pre-resolution detection of `@Native(...)`
227 /// annotations.
228 class NativeAnnotationHandler extends EagerAnnotationHandler<String> {
229 final NativeClassDataBuilder _nativeClassDataBuilder;
230
231 NativeAnnotationHandler(this._nativeClassDataBuilder);
232
233 String getNativeAnnotation(MetadataAnnotationX annotation) {
234 if (annotation.beginToken != null &&
235 annotation.beginToken.next.value == 'Native') {
236 // Skipping '@', 'Native', and '('.
237 Token argument = annotation.beginToken.next.next.next;
238 if (argument is StringToken) {
239 return argument.value;
240 }
241 }
242 return null;
243 }
244
245 String apply(
246 Compiler compiler, Element element, MetadataAnnotation annotation) {
247 if (element.isClass) {
248 String native = getNativeAnnotation(annotation);
249 if (native != null) {
250 _nativeClassDataBuilder.setNativeClassTagInfo(element, native);
251 return native;
252 }
253 }
254 return null;
255 }
256
257 void validate(Compiler compiler, Element element,
258 MetadataAnnotation annotation, ConstantValue constant) {
259 ResolutionDartType annotationType =
260 constant.getType(compiler.commonElements);
261 if (annotationType.element !=
262 compiler.backend.helpers.nativeAnnotationClass) {
263 DiagnosticReporter reporter = compiler.reporter;
264 reporter.internalError(annotation, 'Invalid @Native(...) annotation.');
265 }
266 }
267 }
268
269 void checkJsInteropClassAnnotations(Compiler compiler, LibraryElement library,
270 NativeClassDataBuilder nativeClassDataBuilder) {
271 bool checkJsInteropAnnotation(Element element) {
272 return EagerAnnotationHandler.checkAnnotation(
273 compiler, element, const JsInteropAnnotationHandler());
274 }
275
276 if (checkJsInteropAnnotation(library)) {
277 nativeClassDataBuilder.markAsJsInteropLibrary(library);
278 }
279 library.forEachLocalMember((Element element) {
280 if (element.isClass) {
281 if (checkJsInteropAnnotation(element)) {
282 nativeClassDataBuilder.markAsJsInteropClass(element);
283 }
284 }
285 });
286 }
287
288 bool checkJsInteropMemberAnnotations(Compiler compiler, MemberElement element,
289 NativeDataBuilder nativeDataBuilder) {
290 bool isJsInterop = EagerAnnotationHandler.checkAnnotation(
291 compiler, element, const JsInteropAnnotationHandler());
292 if (isJsInterop) {
293 nativeDataBuilder.markAsJsInteropMember(element);
294 }
295 return isJsInterop;
296 }
297
298 /// Annotation handler for pre-resolution detection of `@JS(...)`
299 /// annotations.
300 class JsInteropAnnotationHandler implements EagerAnnotationHandler<bool> {
301 const JsInteropAnnotationHandler();
302
303 bool hasJsNameAnnotation(MetadataAnnotationX annotation) =>
304 annotation.beginToken != null && annotation.beginToken.next.value == 'JS';
305
306 bool apply(
307 Compiler compiler, Element element, MetadataAnnotation annotation) {
308 return hasJsNameAnnotation(annotation);
309 }
310
311 @override
312 void validate(Compiler compiler, Element element,
313 MetadataAnnotation annotation, ConstantValue constant) {
314 JavaScriptBackend backend = compiler.backend;
315 ResolutionDartType type = constant.getType(compiler.commonElements);
316 if (type.element != backend.helpers.jsAnnotationClass) {
317 compiler.reporter
318 .internalError(annotation, 'Invalid @JS(...) annotation.');
319 }
320 }
321
322 bool get defaultResult => false;
323 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698