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

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

Powered by Google App Engine
This is Rietveld 408576698