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

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

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

Powered by Google App Engine
This is Rietveld 408576698