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

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

Issue 694353007: Move dart2js from sdk/lib/_internal/compiler to pkg/compiler (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month 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
(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 part of native;
6
7 /// This class is a temporary work-around until we get a more powerful DartType.
8 class SpecialType {
9 final String name;
10 const SpecialType._(this.name);
11
12 /// The type Object, but no subtypes:
13 static const JsObject = const SpecialType._('=Object');
14
15 int get hashCode => name.hashCode;
16 }
17
18 /**
19 * A summary of the behavior of a native element.
20 *
21 * Native code can return values of one type and cause native subtypes of
22 * another type to be instantiated. By default, we compute both from the
23 * declared type.
24 *
25 * A field might yield any native type that 'is' the field type.
26 *
27 * A method might create and return instances of native subclasses of its
28 * declared return type, and a callback argument may be called with instances of
29 * the callback parameter type (e.g. Event).
30 *
31 * If there is one or more `@Creates` annotations, the union of the named types
32 * replaces the inferred instantiated type, and the return type is ignored for
33 * the purpose of inferring instantiated types.
34 *
35 * @Creates('IDBCursor') // Created asynchronously.
36 * @Creates('IDBRequest') // Created synchronously (for return value).
37 * IDBRequest openCursor();
38 *
39 * If there is one or more `@Returns` annotations, the union of the named types
40 * replaces the declared return type.
41 *
42 * @Returns('IDBRequest')
43 * IDBRequest openCursor();
44 *
45 * Types in annotations are non-nullable, so include `@Returns('Null')` if
46 * `null` may be returned.
47 */
48 class NativeBehavior {
49
50 /// [DartType]s or [SpecialType]s returned or yielded by the native element.
51 final List typesReturned = [];
52
53 /// [DartType]s or [SpecialType]s instantiated by the native element.
54 final List typesInstantiated = [];
55
56 // If this behavior is for a JS expression, [codeTemplate] contains the
57 // parsed tree.
58 js.Template codeTemplate;
59
60 final SideEffects sideEffects = new SideEffects.empty();
61
62 static NativeBehavior NONE = new NativeBehavior();
63
64 /// Processes the type specification string of a call to JS and stores the
65 /// result in the [typesReturned] and [typesInstantiated].
66 ///
67 /// Two forms of the string is supported:
68 /// 1) A single type string of the form 'void', '', 'var' or 'T1|...|Tn'
69 /// which defines the types returned and for the later form also created by
70 /// the call to JS.
71 /// 2) A sequence of the form '<tag>:<type-string>;' where <tag> is either
72 /// 'returns' or 'creates' and where <type-string> is a type string like in
73 /// 1). The type string marked by 'returns' defines the types returned and
74 /// 'creates' defines the types created by the call to JS. Each tag kind
75 /// can only occur once in the sequence.
76 ///
77 /// [specString] is the specification string, [resolveType] resolves named
78 /// types into type values, [typesReturned] and [typesInstantiated] collects
79 /// the types defined by the specification string, and [objectType] and
80 /// [nullType] define the types for `Object` and `Null`, respectively. The
81 /// latter is used for the type strings of the form '' and 'var'.
82 // TODO(johnniwinther): Use ';' as a separator instead of a terminator.
83 static void processSpecString(
84 DiagnosticListener listener,
85 Spannable spannable,
86 String specString,
87 {dynamic resolveType(String typeString),
88 List typesReturned, List typesInstantiated,
89 objectType, nullType}) {
90
91 /// Resolve a type string of one of the three forms:
92 /// * 'void' - in which case [onVoid] is called,
93 /// * '' or 'var' - in which case [onVar] is called,
94 /// * 'T1|...|Tn' - in which case [onType] is called for each Ti.
95 void resolveTypesString(String typesString,
96 {onVoid(), onVar(), onType(type)}) {
97 // Various things that are not in fact types.
98 if (typesString == 'void') {
99 if (onVoid != null) {
100 onVoid();
101 }
102 return;
103 }
104 if (typesString == '' || typesString == 'var') {
105 if (onVar != null) {
106 onVar();
107 }
108 return;
109 }
110 for (final typeString in typesString.split('|')) {
111 onType(resolveType(typeString));
112 }
113 }
114
115 if (specString.contains(':')) {
116 /// Find and remove a substring of the form 'tag:<type-string>;' from
117 /// [specString].
118 String getTypesString(String tag) {
119 String marker = '$tag:';
120 int startPos = specString.indexOf(marker);
121 if (startPos == -1) return null;
122 int endPos = specString.indexOf(';', startPos);
123 if (endPos == -1) return null;
124 String typeString =
125 specString.substring(startPos + marker.length, endPos);
126 specString = '${specString.substring(0, startPos)}'
127 '${specString.substring(endPos + 1)}'.trim();
128 return typeString;
129 }
130
131 String returns = getTypesString('returns');
132 if (returns != null) {
133 resolveTypesString(returns, onVar: () {
134 typesReturned.add(objectType);
135 typesReturned.add(nullType);
136 }, onType: (type) {
137 typesReturned.add(type);
138 });
139 }
140
141 String creates = getTypesString('creates');
142 if (creates != null) {
143 resolveTypesString(creates, onVoid: () {
144 listener.internalError(spannable,
145 "Invalid type string 'creates:$creates'");
146 }, onVar: () {
147 listener.internalError(spannable,
148 "Invalid type string 'creates:$creates'");
149 }, onType: (type) {
150 typesInstantiated.add(type);
151 });
152 }
153
154 if (!specString.isEmpty) {
155 listener.internalError(spannable, "Invalid JS type string.");
156 }
157 } else {
158 resolveTypesString(specString, onVar: () {
159 typesReturned.add(objectType);
160 typesReturned.add(nullType);
161 }, onType: (type) {
162 typesInstantiated.add(type);
163 typesReturned.add(type);
164 });
165 }
166 }
167
168 static NativeBehavior ofJsCall(Send jsCall, Compiler compiler, resolver) {
169 // The first argument of a JS-call is a string encoding various attributes
170 // of the code.
171 //
172 // 'Type1|Type2'. A union type.
173 // '=Object'. A JavaScript Object, no subtype.
174
175 var argNodes = jsCall.arguments;
176 if (argNodes.isEmpty) {
177 compiler.internalError(jsCall, "JS expression has no type.");
178 }
179
180 var code = argNodes.tail.head;
181 if (code is !StringNode || code.isInterpolation) {
182 compiler.internalError(code, 'JS code must be a string literal.');
183 }
184
185 LiteralString specLiteral = argNodes.head.asLiteralString();
186 if (specLiteral == null) {
187 // TODO(sra): We could accept a type identifier? e.g. JS(bool, '1<2'). It
188 // is not very satisfactory because it does not work for void, dynamic.
189 compiler.internalError(argNodes.head, "Unexpected JS first argument.");
190 }
191
192 NativeBehavior behavior = new NativeBehavior();
193 behavior.codeTemplate =
194 js.js.parseForeignJS(code.dartString.slowToString());
195 new SideEffectsVisitor(behavior.sideEffects)
196 .visit(behavior.codeTemplate.ast);
197
198 String specString = specLiteral.dartString.slowToString();
199
200 resolveType(String typeString) {
201 return _parseType(
202 typeString,
203 compiler,
204 (name) => resolver.resolveTypeFromString(specLiteral, name),
205 jsCall);
206 }
207
208 processSpecString(compiler, jsCall,
209 specString,
210 resolveType: resolveType,
211 typesReturned: behavior.typesReturned,
212 typesInstantiated: behavior.typesInstantiated,
213 objectType: compiler.objectClass.computeType(compiler),
214 nullType: compiler.nullClass.computeType(compiler));
215
216 return behavior;
217 }
218
219 static NativeBehavior ofJsEmbeddedGlobalCall(Send jsGlobalCall,
220 Compiler compiler,
221 resolver) {
222 // The first argument of a JS-embedded global call is a string encoding
223 // the type of the code.
224 //
225 // 'Type1|Type2'. A union type.
226 // '=Object'. A JavaScript Object, no subtype.
227
228 Link<Node> argNodes = jsGlobalCall.arguments;
229 if (argNodes.isEmpty) {
230 compiler.internalError(jsGlobalCall,
231 "JS embedded global expression has no type.");
232 }
233
234 // We don't check the given name. That needs to be done at a later point.
235 // This is, because we want to allow non-literals as names.
236 if (argNodes.tail.isEmpty) {
237 compiler.internalError(jsGlobalCall, 'Embedded Global is missing name');
238 }
239
240 if (!argNodes.tail.tail.isEmpty) {
241 compiler.internalError(argNodes.tail.tail.head,
242 'Embedded Global has more than 2 arguments');
243 }
244
245 LiteralString specLiteral = argNodes.head.asLiteralString();
246 if (specLiteral == null) {
247 // TODO(sra): We could accept a type identifier? e.g. JS(bool, '1<2'). It
248 // is not very satisfactory because it does not work for void, dynamic.
249 compiler.internalError(argNodes.head, "Unexpected first argument.");
250 }
251
252 NativeBehavior behavior = new NativeBehavior();
253
254 String specString = specLiteral.dartString.slowToString();
255
256 resolveType(String typeString) {
257 return _parseType(
258 typeString,
259 compiler,
260 (name) => resolver.resolveTypeFromString(specLiteral, name),
261 jsGlobalCall);
262 }
263
264 processSpecString(compiler, jsGlobalCall,
265 specString,
266 resolveType: resolveType,
267 typesReturned: behavior.typesReturned,
268 typesInstantiated: behavior.typesInstantiated,
269 objectType: compiler.objectClass.computeType(compiler),
270 nullType: compiler.nullClass.computeType(compiler));
271
272 return behavior;
273 }
274
275 static NativeBehavior ofMethod(FunctionElement method, Compiler compiler) {
276 FunctionType type = method.computeType(compiler);
277 var behavior = new NativeBehavior();
278 behavior.typesReturned.add(type.returnType);
279 if (!type.returnType.isVoid) {
280 // Declared types are nullable.
281 behavior.typesReturned.add(compiler.nullClass.computeType(compiler));
282 }
283 behavior._capture(type, compiler);
284
285 // TODO(sra): Optional arguments are currently missing from the
286 // DartType. This should be fixed so the following work-around can be
287 // removed.
288 method.functionSignature.forEachOptionalParameter(
289 (ParameterElement parameter) {
290 behavior._escape(parameter.type, compiler);
291 });
292
293 behavior._overrideWithAnnotations(method, compiler);
294 return behavior;
295 }
296
297 static NativeBehavior ofFieldLoad(Element field, Compiler compiler) {
298 DartType type = field.computeType(compiler);
299 var behavior = new NativeBehavior();
300 behavior.typesReturned.add(type);
301 // Declared types are nullable.
302 behavior.typesReturned.add(compiler.nullClass.computeType(compiler));
303 behavior._capture(type, compiler);
304 behavior._overrideWithAnnotations(field, compiler);
305 return behavior;
306 }
307
308 static NativeBehavior ofFieldStore(Element field, Compiler compiler) {
309 DartType type = field.computeType(compiler);
310 var behavior = new NativeBehavior();
311 behavior._escape(type, compiler);
312 // We don't override the default behaviour - the annotations apply to
313 // loading the field.
314 return behavior;
315 }
316
317 void _overrideWithAnnotations(Element element, Compiler compiler) {
318 if (element.metadata.isEmpty) return;
319
320 DartType lookup(String name) {
321 Element e = element.buildScope().lookup(name);
322 if (e == null) return null;
323 if (e is! ClassElement) return null;
324 ClassElement cls = e;
325 cls.ensureResolved(compiler);
326 return cls.thisType;
327 }
328
329 NativeEnqueuer enqueuer = compiler.enqueuer.resolution.nativeEnqueuer;
330 var creates = _collect(element, compiler, enqueuer.annotationCreatesClass,
331 lookup);
332 var returns = _collect(element, compiler, enqueuer.annotationReturnsClass,
333 lookup);
334
335 if (creates != null) {
336 typesInstantiated..clear()..addAll(creates);
337 }
338 if (returns != null) {
339 typesReturned..clear()..addAll(returns);
340 }
341 }
342
343 /**
344 * Returns a list of type constraints from the annotations of
345 * [annotationClass].
346 * Returns `null` if no constraints.
347 */
348 static _collect(Element element, Compiler compiler, Element annotationClass,
349 lookup(str)) {
350 var types = null;
351 for (Link<MetadataAnnotation> link = element.metadata;
352 !link.isEmpty;
353 link = link.tail) {
354 MetadataAnnotation annotation = link.head.ensureResolved(compiler);
355 ConstantValue value = annotation.constant.value;
356 if (!value.isConstructedObject) continue;
357 ConstructedConstantValue constructedObject = value;
358 if (constructedObject.type.element != annotationClass) continue;
359
360 List<ConstantValue> fields = constructedObject.fields;
361 // TODO(sra): Better validation of the constant.
362 if (fields.length != 1 || !fields[0].isString) {
363 PartialMetadataAnnotation partial = annotation;
364 compiler.internalError(annotation,
365 'Annotations needs one string: ${partial.parseNode(compiler)}');
366 }
367 StringConstantValue specStringConstant = fields[0];
368 String specString = specStringConstant.toDartString().slowToString();
369 for (final typeString in specString.split('|')) {
370 var type = _parseType(typeString, compiler, lookup, annotation);
371 if (types == null) types = [];
372 types.add(type);
373 }
374 }
375 return types;
376 }
377
378 /// Models the behavior of having intances of [type] escape from Dart code
379 /// into native code.
380 void _escape(DartType type, Compiler compiler) {
381 type = type.unalias(compiler);
382 if (type is FunctionType) {
383 FunctionType functionType = type;
384 // A function might be called from native code, passing us novel
385 // parameters.
386 _escape(functionType.returnType, compiler);
387 for (DartType parameter in functionType.parameterTypes) {
388 _capture(parameter, compiler);
389 }
390 }
391 }
392
393 /// Models the behavior of Dart code receiving instances and methods of [type]
394 /// from native code. We usually start the analysis by capturing a native
395 /// method that has been used.
396 void _capture(DartType type, Compiler compiler) {
397 type = type.unalias(compiler);
398 if (type is FunctionType) {
399 FunctionType functionType = type;
400 _capture(functionType.returnType, compiler);
401 for (DartType parameter in functionType.parameterTypes) {
402 _escape(parameter, compiler);
403 }
404 } else {
405 typesInstantiated.add(type);
406 }
407 }
408
409 static _parseType(String typeString, Compiler compiler,
410 lookup(name), locationNodeOrElement) {
411 if (typeString == '=Object') return SpecialType.JsObject;
412 if (typeString == 'dynamic') {
413 return const DynamicType();
414 }
415 DartType type = lookup(typeString);
416 if (type != null) return type;
417
418 int index = typeString.indexOf('<');
419 if (index < 1) {
420 compiler.internalError(
421 _errorNode(locationNodeOrElement, compiler),
422 "Type '$typeString' not found.");
423 }
424 type = lookup(typeString.substring(0, index));
425 if (type != null) {
426 // TODO(sra): Parse type parameters.
427 return type;
428 }
429 compiler.internalError(
430 _errorNode(locationNodeOrElement, compiler),
431 "Type '$typeString' not found.");
432 }
433
434 static _errorNode(locationNodeOrElement, compiler) {
435 if (locationNodeOrElement is Node) return locationNodeOrElement;
436 return locationNodeOrElement.parseNode(compiler);
437 }
438 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698