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

Side by Side Diff: tool/input_sdk/private/types.dart

Issue 1530563003: Generate all runtime files from dart. (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 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
« no previous file with comments | « tool/input_sdk/private/runtime.dart ('k') | tool/input_sdk/private/utils.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2015, 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 /// This library defines the representation of runtime types.
6
7 library dart._types;
8
9 import 'dart:_foreign_helper' show JS, JsName;
10
11 import 'dart:_classes' show getGenericClass, getGenericArgs, getMixins, getImple ments;
12 import 'dart:_rtti' show LazyTagged, read;
13 import 'dart:_utils' as utils;
14
15 @JsName('assert')
16 final assert_ = JS('', '${utils.assert_}');
17 final getOwnPropertyNames = JS('', 'Object.getOwnPropertyNames');
18
19 ///
20 /// Types in dart are represented at runtime as follows.
21 /// - Normal nominal types, produced from classes, are represented
22 /// at runtime by the JS class of which they are an instance.
23 /// If the type is the result of instantiating a generic class,
24 /// then the "classes" module manages the association between the
25 /// instantiated class and the original class declaration
26 /// and the type arguments with which it was instantiated. This
27 /// assocation can be queried via the "classes" module".
28 ///
29 /// - All other types are represented as instances of class TypeRep,
30 /// defined in this module.
31 /// - Dynamic, Void, and Bottom are singleton instances of sentinal
32 /// classes.
33 /// - Function types are instances of subclasses of AbstractFunctionType.
34 ///
35 /// Function types are represented in one of two ways:
36 /// - As an instance of FunctionType. These are eagerly computed.
37 /// - As an instance of TypeDef. The TypeDef representation lazily
38 /// computes an instance of FunctionType, and delegates to that instance.
39 ///
40 /// All types satisfy the following interface:
41 /// get String name;
42 /// String toString();
43 ///
44 ///
45 final TypeRep = JS('', '''
46 class TypeRep extends $LazyTagged(() => $Type) {
47 get name() {return this.toString();}
48 }
49 ''');
50
51 final Dynamic = JS('', '''
52 class Dynamic extends $TypeRep {
53 toString() { return "dynamic"; }
54 }
55 ''');
56 @JsName('dynamic')
57 final dynamicR = JS('', 'new $Dynamic()');
58
59 final Void = JS('', '''
60 class Void extends $TypeRep {
61 toString() { return "void"; }
62 }
63 ''');
64
65 @JsName('void')
66 final voidR = JS('', 'new $Void()');
67
68 final Bottom = JS('', '''
69 class Bottom extends $TypeRep {
70 toString() { return "bottom"; }
71 }
72 ''');
73 final bottom = JS('', 'new $Bottom()');
74
75 final JSObject = JS('', '''
76 class JSObject extends $TypeRep {
77 toString() { return "NativeJavaScriptObject"; }
78 }
79 ''');
80 final jsobject = JS('', 'new $JSObject()');
81
82 final AbstractFunctionType = JS('', '''
83 class AbstractFunctionType extends $TypeRep {
84 constructor() {
85 super();
86 this._stringValue = null;
87 }
88
89 toString() { return this.name; }
90
91 get name() {
92 if (this._stringValue) return this._stringValue;
93
94 let buffer = '(';
95 for (let i = 0; i < this.args.length; ++i) {
96 if (i > 0) {
97 buffer += ', ';
98 }
99 buffer += $typeName(this.args[i]);
100 }
101 if (this.optionals.length > 0) {
102 if (this.args.length > 0) buffer += ', ';
103 buffer += '[';
104 for (let i = 0; i < this.optionals.length; ++i) {
105 if (i > 0) {
106 buffer += ', ';
107 }
108 buffer += $typeName(this.optionals[i]);
109 }
110 buffer += ']';
111 } else if (Object.keys(this.named).length > 0) {
112 if (this.args.length > 0) buffer += ', ';
113 buffer += '{';
114 let names = $getOwnPropertyNames(this.named).sort();
115 for (let i = 0; i < names.length; ++i) {
116 if (i > 0) {
117 buffer += ', ';
118 }
119 buffer += names[i] + ': ' + $typeName(this.named[names[i]]);
120 }
121 buffer += '}';
122 }
123
124 buffer += ') -> ' + $typeName(this.returnType);
125 this._stringValue = buffer;
126 return buffer;
127 }
128 }
129 ''');
130
131 final FunctionType = JS('', '''
132 class FunctionType extends $AbstractFunctionType {
133 /**
134 * Construct a function type. There are two arrow constructors,
135 * distinguished by the "definite" flag.
136 *
137 * The fuzzy arrow (definite is false) treats any arguments
138 * of type dynamic as having type bottom, and will always be
139 * called with a dynamic invoke.
140 *
141 * The definite arrow (definite is true) leaves arguments unchanged.
142 *
143 * We eagerly canonize the argument types to avoid having to deal with
144 * this logic in multiple places.
145 *
146 * TODO(leafp): Figure out how to present this to the user. How
147 * should these be printed out?
148 */
149 constructor(definite, returnType, args, optionals, named) {
150 super();
151 if (!returnType || args.indexOf(void 0) >= 0) {
152 throw new Error('Found undefined return type or arg type!');
153 }
154 this.definite = definite;
155 this.returnType = returnType;
156 this.args = args;
157 this.optionals = optionals;
158 this.named = named;
159
160 // TODO(vsm): This is just parameter metadata for now.
161 this.metadata = [];
162 function process(array, metadata) {
163 var result = [];
164 for (var i = 0; i < array.length; ++i) {
165 var arg = array[i];
166 if (arg instanceof Array) {
167 metadata.push(arg.slice(1));
168 result.push(arg[0]);
169 } else {
170 metadata.push([]);
171 result.push(arg);
172 }
173 }
174 return result;
175 }
176 this.args = process(this.args, this.metadata);
177 this.optionals = process(this.optionals, this.metadata);
178 // TODO(vsm): Add named arguments.
179 this._canonize();
180 }
181 _canonize() {
182 if (this.definite) return;
183
184 function replace(a) {
185 return (a == $dynamicR) ? $bottom : a;
186 }
187
188 this.args = this.args.map(replace);
189
190 if (this.optionals.length > 0) {
191 this.optionals = this.optionals.map(replace);
192 }
193
194 if (Object.keys(this.named).length > 0) {
195 let r = {};
196 for (let name of $getOwnPropertyNames(this.named)) {
197 r[name] = replace(this.named[name]);
198 }
199 this.named = r;
200 }
201 }
202 }
203 ''');
204
205 final Typedef = JS('', '''
206 class Typedef extends $AbstractFunctionType {
207 constructor(name, closure) {
208 super();
209 this._name = name;
210 this._closure = closure;
211 this._functionType = null;
212 }
213
214 get definite() {
215 return this._functionType.definite;
216 }
217
218 get name() {
219 return this._name;
220 }
221
222 get functionType() {
223 if (!this._functionType) {
224 this._functionType = this._closure();
225 }
226 return this._functionType;
227 }
228
229 get returnType() {
230 return this.functionType.returnType;
231 }
232
233 get args() {
234 return this.functionType.args;
235 }
236
237 get optionals() {
238 return this.functionType.optionals;
239 }
240
241 get named() {
242 return this.functionType.named;
243 }
244
245 get metadata() {
246 return this.functionType.metadata;
247 }
248 }
249 ''');
250
251 _functionType(definite, returnType, args, extra) => JS('', '''(() => {
252 // TODO(vsm): Cache / memomize?
253 let optionals;
254 let named;
255 if ($extra === void 0) {
256 optionals = [];
257 named = {};
258 } else if ($extra instanceof Array) {
259 optionals = $extra;
260 named = {};
261 } else {
262 optionals = [];
263 named = $extra;
264 }
265 return new $FunctionType($definite, $returnType, $args, optionals, named);
266 })()''');
267
268 ///
269 /// Create a "fuzzy" function type. If any arguments are dynamic
270 /// they will be replaced with bottom.
271 ///
272 functionType(returnType, args, extra) => JS('', '''(() => {
273 return _functionType(false, $returnType, $args, $extra);
274 })()''');
275
276 ///
277 /// Create a definite function type. No substitution of dynamic for
278 /// bottom occurs.
279 ///
280 definiteFunctionType(returnType, args, extra) => JS('', '''(() => {
281 return _functionType(true, $returnType, $args, $extra);
282 })()''');
283
284 typedef(name, closure) => JS('', '''(() => {
285 return new $Typedef($name, $closure);
286 })()''');
287
288 isDartType(type) => JS('', '''(() => {
289 return $read($type) === $Type;
290 })()''');
291
292 typeName(type) => JS('', '''(() => {
293 // Non-instance types
294 if ($type instanceof $TypeRep) return $type.toString();
295 // Instance types
296 let tag = $read($type);
297 if (tag === $Type) {
298 let name = $type.name;
299 let args = $getGenericArgs($type);
300 if (args) {
301 name += '<';
302 for (let i = 0; i < args.length; ++i) {
303 if (i > 0) name += ', ';
304 name += $typeName(args[i]);
305 }
306 name += '>';
307 }
308 return name;
309 }
310 if (tag) return "Not a type: " + tag.name;
311 return "JSObject<" + $type.name + ">";
312 })()''');
313
314 isFunctionType(type) => JS('', '''(() => {
315 return $type instanceof $AbstractFunctionType || $type == $Function;
316 })()''');
317
318 isFunctionSubType(ft1, ft2) => JS('', '''(() => {
319 if ($ft2 == $Function) {
320 return true;
321 }
322
323 let ret1 = $ft1.returnType;
324 let ret2 = $ft2.returnType;
325
326 if (!$isSubtype_(ret1, ret2)) {
327 // Covariant return types
328 // Note, void (which can only appear as a return type) is effectively
329 // treated as dynamic. If the base return type is void, we allow any
330 // subtype return type.
331 // E.g., we allow:
332 // () -> int <: () -> void
333 if (ret2 != $voidR) {
334 return false;
335 }
336 }
337
338 let args1 = $ft1.args;
339 let args2 = $ft2.args;
340
341 if (args1.length > args2.length) {
342 return false;
343 }
344
345 for (let i = 0; i < args1.length; ++i) {
346 if (!$isSubtype_(args2[i], args1[i])) {
347 return false;
348 }
349 }
350
351 let optionals1 = $ft1.optionals;
352 let optionals2 = $ft2.optionals;
353
354 if (args1.length + optionals1.length < args2.length + optionals2.length) {
355 return false;
356 }
357
358 let j = 0;
359 for (let i = args1.length; i < args2.length; ++i, ++j) {
360 if (!$isSubtype_(args2[i], optionals1[j])) {
361 return false;
362 }
363 }
364
365 for (let i = 0; i < optionals2.length; ++i, ++j) {
366 if (!$isSubtype_(optionals2[i], optionals1[j])) {
367 return false;
368 }
369 }
370
371 let named1 = $ft1.named;
372 let named2 = $ft2.named;
373
374 let names = $getOwnPropertyNames(named2);
375 for (let i = 0; i < names.length; ++i) {
376 let name = names[i];
377 let n1 = named1[name];
378 let n2 = named2[name];
379 if (n1 === void 0) {
380 return false;
381 }
382 if (!$isSubtype_(n2, n1)) {
383 return false;
384 }
385 }
386
387 return true;
388 })()''');
389
390 ///
391 /// Computes the canonical type.
392 /// This maps JS types onto their corresponding Dart Type.
393 ///
394 // TODO(jmesserly): lots more needs to be done here.
395 canonicalType(t) => JS('', '''(() => {
396 if ($t === Object) return $Object;
397 if ($t === Function) return $Function;
398 if ($t === Array) return $List;
399
400 // We shouldn't normally get here with these types, unless something strange
401 // happens like subclassing Number in JS and passing it to Dart.
402 if ($t === String) return $String;
403 if ($t === Number) return $double;
404 if ($t === Boolean) return $bool;
405 return $t;
406 })()''');
407
408 final subtypeMap = JS('', 'new Map()');
409 isSubtype(t1, t2) => JS('', '''(() => {
410 // See if we already know the answer
411 // TODO(jmesserly): general purpose memoize function?
412 let map = $subtypeMap.get($t1);
413 let result;
414 if (map) {
415 result = map.get($t2);
416 if (result !== void 0) return result;
417 } else {
418 $subtypeMap.set($t1, map = new Map());
419 }
420 result = $isSubtype_($t1, $t2);
421 map.set($t2, result);
422 return result;
423 })()''');
424
425 _isBottom(type) => JS('', '''(() => {
426 return $type == $bottom;
427 })()''');
428
429 _isTop(type) => JS('', '''(() => {
430 return $type == $Object || ($type == $dynamicR);
431 })()''');
432
433 isSubtype_(t1, t2) => JS('', '''(() => {
434 $t1 = $canonicalType($t1);
435 $t2 = $canonicalType($t2);
436 if ($t1 == $t2) return true;
437
438 // Trivially true.
439 if ($_isTop($t2) || $_isBottom($t1)) {
440 return true;
441 }
442
443 // Trivially false.
444 if ($_isTop($t1) || $_isBottom($t2)) {
445 return false;
446 }
447
448 // "Traditional" name-based subtype check.
449 if ($isClassSubType($t1, $t2)) {
450 return true;
451 }
452
453 // Function subtyping.
454 // TODO(vsm): Handle Objects with call methods. Those are functions
455 // even if they do not *nominally* subtype core.Function.
456 if ($isFunctionType($t1) &&
457 $isFunctionType($t2)) {
458 return $isFunctionSubType($t1, $t2);
459 }
460 return false;
461 })()''');
462
463 isClassSubType(t1, t2) => JS('', '''(() => {
464 // We support Dart's covariant generics with the caveat that we do not
465 // substitute bottom for dynamic in subtyping rules.
466 // I.e., given T1, ..., Tn where at least one Ti != dynamic we disallow:
467 // - S !<: S<T1, ..., Tn>
468 // - S<dynamic, ..., dynamic> !<: S<T1, ..., Tn>
469 $t1 = $canonicalType($t1);
470 $assert_($t2 == $canonicalType($t2));
471 if ($t1 == $t2) return true;
472
473 if ($t1 == $Object) return false;
474
475 // If t1 is a JS Object, we may not hit core.Object.
476 if ($t1 == null) return $t2 == $Object || $t2 == $dynamicR;
477
478 // Check if t1 and t2 have the same raw type. If so, check covariance on
479 // type parameters.
480 let raw1 = $getGenericClass($t1);
481 let raw2 = $getGenericClass($t2);
482 if (raw1 != null && raw1 == raw2) {
483 let typeArguments1 = $getGenericArgs($t1);
484 let typeArguments2 = $getGenericArgs($t2);
485 let length = typeArguments1.length;
486 if (typeArguments2.length == 0) {
487 // t2 is the raw form of t1
488 return true;
489 } else if (length == 0) {
490 // t1 is raw, but t2 is not
491 return false;
492 }
493 $assert_(length == typeArguments2.length);
494 for (let i = 0; i < length; ++i) {
495 if (!$isSubtype(typeArguments1[i], typeArguments2[i])) {
496 return false;
497 }
498 }
499 return true;
500 }
501
502 // Check superclass.
503 if ($isClassSubType($t1.__proto__, $t2)) return true;
504
505 // Check mixins.
506 let mixins = $getMixins($t1);
507 if (mixins) {
508 for (let m1 of mixins) {
509 // TODO(jmesserly): remove the != null check once we can load core libs.
510 if (m1 != null && $isClassSubType(m1, $t2)) return true;
511 }
512 }
513
514 // Check interfaces.
515 let getInterfaces = $getImplements($t1);
516 if (getInterfaces) {
517 for (let i1 of getInterfaces()) {
518 // TODO(jmesserly): remove the != null check once we can load core libs.
519 if (i1 != null && $isClassSubType(i1, $t2)) return true;
520 }
521 }
522
523 return false;
524 })()''');
525
526 // TODO(jmesserly): this isn't currently used, but it could be if we want
527 // `obj is NonGroundType<T,S>` to be rejected at runtime instead of compile
528 // time.
529 isGroundType(type) => JS('', '''(() => {
530 // TODO(vsm): Cache this if we start using it at runtime.
531
532 if ($type instanceof $AbstractFunctionType) {
533 if (!$_isTop($type.returnType)) return false;
534 for (let i = 0; i < $type.args.length; ++i) {
535 if (!$_isBottom($type.args[i])) return false;
536 }
537 for (let i = 0; i < $type.optionals.length; ++i) {
538 if (!$_isBottom($type.optionals[i])) return false;
539 }
540 let names = $getOwnPropertyNames($type.named);
541 for (let i = 0; i < names.length; ++i) {
542 if (!$_isBottom($type.named[names[i]])) return false;
543 }
544 return true;
545 }
546
547 let typeArgs = $getGenericArgs($type);
548 if (!typeArgs) return true;
549 for (let t of typeArgs) {
550 if (t != $Object && t != $dynamicR) return false;
551 }
552 return true;
553 })()''');
OLDNEW
« no previous file with comments | « tool/input_sdk/private/runtime.dart ('k') | tool/input_sdk/private/utils.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698