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

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

Issue 1182653002: Refactor runtime into libraries, better type reps (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Add missing file Created 5 years, 6 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 loader.library('dart/types', null, /* Imports */[
9 ], /* Lazy Imports */[
10 'dart/classes',
11 'dart/core',
12 'dart/rtti'
13 ], function(exports, classes, core, rtti) {
14 'use strict';
15
16 const assert = js_utils.assert;
17 const copyProperties = js_utils.copyProperties;
18 const getOwnPropertyNames = js_utils.getOwnPropertyNames;
19 const safeGetOwnProperty = js_utils.safeGetOwnProperty;
20 const throwRuntimeError = js_utils.throwRuntimeError;
21
22 class TypeRep extends rtti.LazyTagged(() => core.Type) {
23 get name() {return this.toString();}
24 }
25
26 class Dynamic extends TypeRep {
Jennifer Messerly 2015/06/12 17:32:25 awesome!
27 toString() { return "dynamic"; }
28 }
29 let dynamicR = new Dynamic();
30 exports.dynamic = dynamicR;
31
32 class Void extends TypeRep {
33 toString() { return "void"; }
34 }
35
36 let voidR = new Void();
37 exports.void = voidR;
38
39 class Bottom extends TypeRep {
40 toString() { return "bottom"; }
41 };
42 let bottomR = new Bottom();
43 exports.bottom = bottomR;
44
45 class AbstractFunctionType extends TypeRep {
46 constructor() {
47 super();
48 this._stringValue = null;
49 }
50
51 toString() { return this.name; }
52
53 get name() {
54 if (this._stringValue) return this._stringValue;
55
56 let buffer = '(';
57 for (let i = 0; i < this.args.length; ++i) {
58 if (i > 0) {
59 buffer += ', ';
60 }
61 buffer += typeToString(this.args[i]);
62 }
63 if (this.optionals.length > 0) {
64 if (this.args.length > 0) buffer += ', ';
65 buffer += '[';
66 for (let i = 0; i < this.optionals.length; ++i) {
67 if (i > 0) {
68 buffer += ', ';
69 }
70 buffer += typeToString(this.optionals[i]);
71 }
72 buffer += ']';
73 } else if (Object.keys(this.named).length > 0) {
74 if (this.args.length > 0) buffer += ', ';
75 buffer += '{';
76 let names = getOwnPropertyNames(this.named).sort();
77 for (let i = 0; i < names.length; ++i) {
78 if (i > 0) {
79 buffer += ', ';
80 }
81 buffer += names[i] + ': ' + typeToString(this.named[names[i]]);
82 }
83 buffer += '}';
84 }
85
86 buffer += ') -> ' + typeToString(this.returnType);
87 this._stringValue = buffer;
88 return buffer;
89 }
90 }
91
92 class FunctionType extends AbstractFunctionType {
93 constructor(returnType, args, optionals, named) {
94 super();
95 this.returnType = returnType;
96 this.args = args;
97 this.optionals = optionals;
98 this.named = named;
99 }
100 }
101
102 class Typedef extends AbstractFunctionType {
103 constructor(name, closure) {
104 super();
105 this._name = name;
106 this._closure = closure;
107 this._functionType = null;
108 }
109
110 get name() {
111 return this._name;
112 }
113
114 get functionType() {
115 if (!this._functionType) {
116 this._functionType = this._closure();
117 }
118 return this._functionType;
119 }
120
121 get returnType() {
122 return this.functionType.returnType;
123 }
124
125 get args() {
126 return this.functionType.args;
127 }
128
129 get optionals() {
130 return this.functionType.optionals;
131 }
132
133 get named() {
134 return this.functionType.named;
135 }
136 }
137
138 function functionType(returnType, args, extra) {
139 // TODO(vsm): Cache / memomize?
140 let optionals;
141 let named;
142 if (extra === void 0) {
143 optionals = [];
144 named = {};
145 } else if (extra instanceof Array) {
146 optionals = extra;
147 named = {};
148 } else {
149 optionals = [];
150 named = extra;
151 }
152 return new FunctionType(returnType, args, optionals, named);
153 }
154 exports.functionType = functionType;
155
156 function typedef(name, closure) {
157 return new Typedef(name, closure);
158 }
159 exports.typedef = typedef;
160
161 function isDartType(type) {
162 return rtti.read(type) === core.Type;
163 }
164 exports.isDartType = isDartType;
165
166 function typeToString(type) {
167 // Non-instance types
168 if (type instanceof TypeRep) return type.toString();
169 // Instance types
170 let tag = rtti.read(type);
171 if (tag === core.Type) {
172 let name = type.name;
173 let args = classes.getGenericArgs(type);
174 if (args) {
175 name += '<';
176 for (let i = 0; i < args.length; ++i) {
177 if (i > 0) name += ', ';
178 name += typeToString(args[i]);
179 }
180 name += '>';
181 }
182 return name;
183 }
184 if (tag) return "Not a type: " + tag.name;
185 return "JSObject<" + type.name + ">";
186 }
187 exports.typeToString = typeToString;
188
189 function isFunctionType(type) {
190 return type instanceof AbstractFunctionType || type == core.Function;
191 }
192
193 function isFunctionSubType(ft1, ft2) {
194 if (ft2 == core.Function) {
195 return true;
196 }
197
198 let ret1 = ft1.returnType;
199 let ret2 = ft2.returnType;
200
201 if (!isSubtype_(ret1, ret2)) {
202 // Covariant return types
203 // Note, void (which can only appear as a return type) is effectively
204 // treated as dynamic. If the base return type is void, we allow any
205 // subtype return type.
206 // E.g., we allow:
207 // () -> int <: () -> void
208 if (ret2 != voidR) {
209 return false;
210 }
211 }
212
213 let args1 = ft1.args;
214 let args2 = ft2.args;
215
216 if (args1.length > args2.length) {
217 return false;
218 }
219
220 for (let i = 0; i < args1.length; ++i) {
221 if (!isSubtype_(args2[i], args1[i], true)) {
222 return false;
223 }
224 }
225
226 let optionals1 = ft1.optionals;
227 let optionals2 = ft2.optionals;
228
229 if (args1.length + optionals1.length < args2.length + optionals2.length) {
230 return false;
231 }
232
233 let j = 0;
234 for (let i = args1.length; i < args2.length; ++i, ++j) {
235 if (!isSubtype_(args2[i], optionals1[j], true)) {
236 return false;
237 }
238 }
239
240 for (let i = 0; i < optionals2.length; ++i, ++j) {
241 if (!isSubtype_(optionals2[i], optionals1[j], true)) {
242 return false;
243 }
244 }
245
246 let named1 = ft1.named;
247 let named2 = ft2.named;
248
249 let names = getOwnPropertyNames(named2);
250 for (let i = 0; i < names.length; ++i) {
251 let name = names[i];
252 let n1 = named1[name];
253 let n2 = named2[name];
254 if (n1 === void 0) {
255 return false;
256 }
257 if (!isSubtype_(n2, n1, true)) {
258 return false;
259 }
260 }
261
262 return true;
263 }
264
265 /**
266 * Computes the canonical type.
267 * This maps JS types onto their corresponding Dart Type.
268 */
269 // TODO(jmesserly): lots more needs to be done here.
270 function canonicalType(t) {
271 if (t === Object) return core.Object;
272 if (t === Function) return core.Function;
273 if (t === Array) return core.List;
274
275 // We shouldn't normally get here with these types, unless something strange
276 // happens like subclassing Number in JS and passing it to Dart.
277 if (t === String) return core.String;
278 if (t === Number) return core.double;
279 if (t === Boolean) return core.bool;
280 return t;
281 }
282
283 const subtypeMap = new Map();
284 function isSubtype(t1, t2) {
285 // See if we already know the answer
286 // TODO(jmesserly): general purpose memoize function?
287 let map = subtypeMap.get(t1);
288 let result;
289 if (map) {
290 result = map.get(t2);
291 if (result !== void 0) return result;
292 } else {
293 subtypeMap.set(t1, map = new Map());
294 }
295 result = isSubtype_(t1, t2)
296 map.set(t2, result);
297 return result;
298 }
299 exports.isSubtype = isSubtype;
300
301 function _isBottom(type, dynamicIsBottom) {
302 return (type == dynamicR && dynamicIsBottom) || type == bottomR;
303 }
304
305 function _isTop(type, dynamicIsBottom) {
306 return type == core.Object || (type == dynamicR && !dynamicIsBottom);
307 }
308
309 function isSubtype_(t1, t2, opt_dynamicIsBottom) {
310 let dynamicIsBottom =
311 opt_dynamicIsBottom === void 0 ? false : opt_dynamicIsBottom;
312
313 t1 = canonicalType(t1);
314 t2 = canonicalType(t2);
315 if (t1 == t2) return true;
316
317 // In Dart, dynamic is effectively both top and bottom.
318 // Here, we treat dynamic as one or the other depending on context,
319 // but not both.
320
321 // Trivially true.
322 if (_isTop(t2, dynamicIsBottom) || _isBottom(t1, dynamicIsBottom)) {
323 return true;
324 }
325
326 // Trivially false.
327 if (_isTop(t1, dynamicIsBottom) || _isBottom(t2, dynamicIsBottom)) {
328 return false;
329 }
330
331 // "Traditional" name-based subtype check.
332 if (isClassSubType(t1, t2)) {
333 return true;
334 }
335
336 // Function subtyping.
337 // TODO(vsm): Handle Objects with call methods. Those are functions
338 // even if they do not *nominally* subtype core.Function.
339 if (isFunctionType(t1) &&
340 isFunctionType(t2)) {
341 return isFunctionSubType(t1, t2);
342 }
343 return false;
344 }
345
346 function isClassSubType(t1, t2) {
347 // We support Dart's covariant generics with the caveat that we do not
348 // substitute bottom for dynamic in subtyping rules.
349 // I.e., given T1, ..., Tn where at least one Ti != dynamic we disallow:
350 // - S !<: S<T1, ..., Tn>
351 // - S<dynamic, ..., dynamic> !<: S<T1, ..., Tn>
352 t1 = canonicalType(t1);
353 assert(t2 == canonicalType(t2));
354 if (t1 == t2) return true;
355
356 if (t1 == core.Object) return false;
357
358 // If t1 is a JS Object, we may not hit core.Object.
359 if (t1 == null) return t2 == core.Object || t2 == dynamicR;
360
361 // Check if t1 and t2 have the same raw type. If so, check covariance on
362 // type parameters.
363 let raw1 = classes.getGenericClass(t1);
364 let raw2 = classes.getGenericClass(t2);
365 if (raw1 != null && raw1 == raw2) {
366 let typeArguments1 = classes.getGenericArgs(t1);
367 let typeArguments2 = classes.getGenericArgs(t2);
368 let length = typeArguments1.length;
369 if (typeArguments2.length == 0) {
370 // t2 is the raw form of t1
371 return true;
372 } else if (length == 0) {
373 // t1 is raw, but t2 is not
374 return false;
375 }
376 assert(length == typeArguments2.length);
377 for (let i = 0; i < length; ++i) {
378 if (!isSubtype(typeArguments1[i], typeArguments2[i])) {
379 return false;
380 }
381 }
382 return true;
383 }
384
385 // Check superclass.
386 if (isClassSubType(t1.__proto__, t2)) return true;
387
388 // Check mixins.
389 let mixins = classes.getMixins(t1);
390 if (mixins) {
391 for (let m1 of mixins) {
392 // TODO(jmesserly): remove the != null check once we can load core libs.
393 if (m1 != null && isClassSubType(m1, t2)) return true;
394 }
395 }
396
397 // Check interfaces.
398 let getInterfaces = classes.getImplements(t1);
399 if (getInterfaces) {
400 for (let i1 of getInterfaces()) {
401 // TODO(jmesserly): remove the != null check once we can load core libs.
402 if (i1 != null && isClassSubType(i1, t2)) return true;
403 }
404 }
405
406 return false;
407 }
408
409 // TODO(jmesserly): this isn't currently used, but it could be if we want
410 // `obj is NonGroundType<T,S>` to be rejected at runtime instead of compile
411 // time.
412 function isGroundType(type) {
413 // TODO(vsm): Cache this if we start using it at runtime.
414
415 if (type instanceof AbstractFunctionType) {
416 if (!_isTop(type.returnType, false)) return false;
417 for (let i = 0; i < type.args.length; ++i) {
418 if (!_isBottom(type.args[i], true)) return false;
419 }
420 for (let i = 0; i < type.optionals.length; ++i) {
421 if (!_isBottom(type.optionals[i], true)) return false;
422 }
423 let names = getOwnPropertyNames(type.named);
424 for (let i = 0; i < names.length; ++i) {
425 if (!_isBottom(type.named[names[i]], true)) return false;
426 }
427 return true;
428 }
429
430 let typeArgs = classes.getGenericArgs(type);
431 if (!typeArgs) return true;
432 for (let t of typeArgs) {
433 if (t != core.Object && t != dynamicR) return false;
434 }
435 return true;
436 }
437 exports.isGroundType = isGroundType;
438
439 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698