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

Side by Side Diff: lib/runtime/dart/_types.js

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

Powered by Google App Engine
This is Rietveld 408576698