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

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

Issue 1076883003: Initial support for runtime function types and type checking (Closed) Base URL: https://github.com/dart-lang/dev_compiler.git@master
Patch Set: Address comments Created 5 years, 8 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
« no previous file with comments | « lib/runtime/dart/isolate.js ('k') | lib/src/codegen/js_codegen.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 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 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 var dart, _js_helper; 5 var dart, _js_helper;
6 (function (dart) { 6 (function (dart) {
7 'use strict'; 7 'use strict';
8 8
9 let defineProperty = Object.defineProperty; 9 let defineProperty = Object.defineProperty;
10 let getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; 10 let getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after
119 case "boolean": 119 case "boolean":
120 return core.bool; 120 return core.bool;
121 case "string": 121 case "string":
122 return core.String; 122 return core.String;
123 case "symbol": 123 case "symbol":
124 return Symbol; 124 return Symbol;
125 } 125 }
126 // Undefined is handled above. For historical reasons, 126 // Undefined is handled above. For historical reasons,
127 // typeof null == "object" in JS. 127 // typeof null == "object" in JS.
128 if (obj === null) return core.Null; 128 if (obj === null) return core.Null;
129 return obj.constructor; 129 // TODO(vsm): Should we treat Dart and JS objects differently here?
130 // E.g., we can check if obj instanceof core.Object to differentiate.
131 var result = obj[dart.runtimeType];
132 if (result) return result;
133 result = obj.constructor;
134 if (result == Function) {
135 return getFunctionType(obj);
136 }
137 return result;
130 } 138 }
131 dart.getRuntimeType = getRuntimeType; 139 dart.getRuntimeType = getRuntimeType;
132 140
133 function instanceOf(obj, type) { 141 function instanceOf(obj, type) {
134 return isSubtype(getRuntimeType(obj), type); 142 return isSubtype(getRuntimeType(obj), type);
135 } 143 }
136 dart.is = instanceOf; 144 dart.is = instanceOf;
137 145
138 /** 146 /**
139 * Computes the canonical type. 147 * Computes the canonical type.
(...skipping 23 matching lines...) Expand all
163 result = map.get(t2); 171 result = map.get(t2);
164 if (result !== void 0) return result; 172 if (result !== void 0) return result;
165 } else { 173 } else {
166 subtypeMap.set(t1, map = new Map()); 174 subtypeMap.set(t1, map = new Map());
167 } 175 }
168 map.set(t2, result = isSubtype_(t1, t2)); 176 map.set(t2, result = isSubtype_(t1, t2));
169 return result; 177 return result;
170 } 178 }
171 dart.isSubtype = isSubtype; 179 dart.isSubtype = isSubtype;
172 180
173 function isSubtype_(t1, t2) { 181 function _isBottom(type, dynamicIsBottom) {
182 return (type == dart.dynamic && dynamicIsBottom) || type == dart.bottom;
183 }
184
185 function _isTop(type, dynamicIsBottom) {
186 return type == core.Object || (type == dart.dynamic && !dynamicIsBottom);
187 }
188
189 function isSubtype_(t1, t2, opt_dynamicIsBottom) {
190 let dynamicIsBottom =
191 opt_dynamicIsBottom === void 0 ? false : opt_dynamicIsBottom;
192
174 t1 = canonicalType(t1); 193 t1 = canonicalType(t1);
175 t2 = canonicalType(t2); 194 t2 = canonicalType(t2);
176 if (t1 == t2) return true; 195 if (t1 == t2) return true;
177 196
178 // In Dart, dynamic is effectively both top and bottom. 197 // In Dart, dynamic is effectively both top and bottom.
179 // Here, we treat dynamic as top - the base type of everything. 198 // Here, we treat dynamic as one or the other depending on context,
180 if (t1 == dart.dynamic) return false; 199 // but not both.
181 if (t2 == dart.dynamic) return true;
182 200
183 if (t2 == core.Object) return true; 201 // Trivially true.
184 if (t1 == core.Object) return false; 202 if (_isTop(t2, dynamicIsBottom) || _isBottom(t1, dynamicIsBottom)) {
203 return true;
204 }
205
206 // Trivially false.
207 if (_isTop(t1, dynamicIsBottom) || _isBottom(t2, dynamicIsBottom)) {
208 return false;
209 }
185 210
186 // "Traditional" name-based subtype check. 211 // "Traditional" name-based subtype check.
187 if (isClassSubType(t1, t2)) { 212 if (isClassSubType(t1, t2)) {
188 return true; 213 return true;
189 } 214 }
190 215
191 // Function subtyping. 216 // Function subtyping.
192 // TODO(jmesserly): implement this properly. 217 // TODO(vsm): Handle Objects with call methods. Those are functions
193 if (isClassSubType(t1, core.Function) && 218 // even if they do not *nominally* subtype core.Function.
194 isClassSubType(t2, core.Function)) { 219 if (isFunctionType(t1) &&
195 return true; 220 isFunctionType(t2)) {
221 return isFunctionSubType(t1, t2);
196 } 222 }
197 return false; 223 return false;
198 } 224 }
199 225
200 function safeGetOwnProperty(obj, name) { 226 function safeGetOwnProperty(obj, name) {
201 var desc = getOwnPropertyDescriptor(obj, name); 227 var desc = getOwnPropertyDescriptor(obj, name);
202 if (desc) return desc.value; 228 if (desc) return desc.value;
203 } 229 }
204 230
205 function isClassSubType(t1, t2) { 231 function isClassSubType(t1, t2) {
206 // We support Dart's covariant generics with the caveat that we do not 232 // We support Dart's covariant generics with the caveat that we do not
207 // substitute bottom for dynamic in subtyping rules. 233 // substitute bottom for dynamic in subtyping rules.
208 // I.e., given T1, ..., Tn where at least one Ti != dynamic we disallow: 234 // I.e., given T1, ..., Tn where at least one Ti != dynamic we disallow:
209 // - S !<: S<T1, ..., Tn> 235 // - S !<: S<T1, ..., Tn>
210 // - S<dynamic, ..., dynamic> !<: S<T1, ..., Tn> 236 // - S<dynamic, ..., dynamic> !<: S<T1, ..., Tn>
211 if (t1 == t2) return true; 237 if (t1 == t2) return true;
212 238
213 if (t1 == core.Object) return false; 239 if (t1 == core.Object) return false;
214 240
241 // If t1 is a JS Object, we may not hit core.Object.
242 if (t1 == null) return t2 == core.Object || t2 == dart.dynamic;
243
215 // Check if t1 and t2 have the same raw type. If so, check covariance on 244 // Check if t1 and t2 have the same raw type. If so, check covariance on
216 // type parameters. 245 // type parameters.
217 let raw1 = safeGetOwnProperty(t1, dart.originalDeclaration); 246 let raw1 = safeGetOwnProperty(t1, dart.originalDeclaration);
218 let raw2 = safeGetOwnProperty(t2, dart.originalDeclaration); 247 let raw2 = safeGetOwnProperty(t2, dart.originalDeclaration);
219 if (raw1 != null && raw1 == raw2) { 248 if (raw1 != null && raw1 == raw2) {
220 let typeArguments1 = safeGetOwnProperty(t1, dart.typeArguments); 249 let typeArguments1 = safeGetOwnProperty(t1, dart.typeArguments);
221 let typeArguments2 = safeGetOwnProperty(t2, dart.typeArguments); 250 let typeArguments2 = safeGetOwnProperty(t2, dart.typeArguments);
222 let length = typeArguments1.length; 251 let length = typeArguments1.length;
223 if (typeArguments2.length == 0) { 252 if (typeArguments2.length == 0) {
224 // t2 is the raw form of t1 253 // t2 is the raw form of t1
(...skipping 28 matching lines...) Expand all
253 if (getInterfaces) { 282 if (getInterfaces) {
254 for (let i1 of getInterfaces()) { 283 for (let i1 of getInterfaces()) {
255 // TODO(jmesserly): remove the != null check once we can load core libs. 284 // TODO(jmesserly): remove the != null check once we can load core libs.
256 if (i1 != null && isClassSubType(i1, t2)) return true; 285 if (i1 != null && isClassSubType(i1, t2)) return true;
257 } 286 }
258 } 287 }
259 288
260 return false; 289 return false;
261 } 290 }
262 291
263 function closureWrap(obj, type) {
264 // TODO(vsm): Remove this once we handle in the checker.
265 return obj;
266 }
267 dart.closureWrap = closureWrap;
268
269
270 // TODO(jmesserly): this isn't currently used, but it could be if we want 292 // TODO(jmesserly): this isn't currently used, but it could be if we want
271 // `obj is NonGroundType<T,S>` to be rejected at runtime instead of compile 293 // `obj is NonGroundType<T,S>` to be rejected at runtime instead of compile
272 // time. Also TODO: update this to handle functions. 294 // time.
273 function isGroundType(type) { 295 function isGroundType(type) {
296 // TODO(vsm): Cache this if we start using it at runtime.
297
298 if (type instanceof AbstractFunctionType) {
299 if (!_isTop(type.returnType, false)) return false;
300 for (var i = 0; i < type.args.length; ++i) {
301 if (!_isBottom(type.args[i], true)) return false;
302 }
303 for (var i = 0; i < type.optionals.length; ++i) {
304 if (!_isBottom(type.optionals[i], true)) return false;
305 }
306 var names = Object.getOwnPropertyNames(type.named);
307 for (var i = 0; i < names.length; ++i) {
308 if (!_isBottom(type.named[names[i]], true)) return false;
309 }
310 return true;
311 }
312
274 let typeArgs = safeGetOwnProperty(type, dart.typeArguments); 313 let typeArgs = safeGetOwnProperty(type, dart.typeArguments);
275 if (!typeArgs) return true; 314 if (!typeArgs) return true;
276 for (let t of typeArgs) { 315 for (let t of typeArgs) {
277 if (t != core.Object && t != dart.dynamic) return false; 316 if (t != core.Object && t != dart.dynamic) return false;
278 } 317 }
279 return true; 318 return true;
280 } 319 }
281 dart.isGroundType = isGroundType; 320 dart.isGroundType = isGroundType;
282 321
283 function arity(f) { 322 function arity(f) {
(...skipping 10 matching lines...) Expand all
294 } 333 }
295 dart.equals = equals; 334 dart.equals = equals;
296 335
297 /** Checks that `x` is not null or undefined. */ 336 /** Checks that `x` is not null or undefined. */
298 function notNull(x) { 337 function notNull(x) {
299 if (x == null) throw 'expected not-null value'; 338 if (x == null) throw 'expected not-null value';
300 return x; 339 return x;
301 } 340 }
302 dart.notNull = notNull; 341 dart.notNull = notNull;
303 342
343 function _typeName(type) {
344 var name = type.name;
345 if (!name) throw 'Unexpected type: ' + type;
346 return name;
347 }
348
349 class AbstractFunctionType {
350 constructor() {
351 this._stringValue = null;
352 }
353
354 get name() {
355 if (this._stringValue) return this._stringValue;
356
357 var buffer = '(';
358 for (var i = 0; i < this.args.length; ++i) {
359 if (i > 0) {
360 buffer += ', ';
361 }
362 buffer += _typeName(this.args[i]);
363 }
364 if (this.optionals.length > 0) {
365 if (this.args.length > 0) buffer += ', ';
366 buffer += '[';
367 for (var i = 0; i < this.optionals.length; ++i) {
368 if (i > 0) {
369 buffer += ', ';
370 }
371 buffer += _typeName(this.optionals[i]);
372 }
373 buffer += ']';
374 } else if (this.named.length > 0) {
375 if (this.args.length > 0) buffer += ', ';
376 buffer += '{';
377 let names = Object.getOwnPropertyNames(this.named).sort();
378 for (var i = 0; i < names.length; ++i) {
379 if (i > 0) {
380 buffer += ', ';
381 }
382 buffer += names[i] + ': ' + _typeName(this.named[names[i]]);
383 }
384 buffer += '}';
385 }
386
387 buffer += ') -> ' + _typeName(this.returnType);
388 this._stringValue = buffer;
389 return buffer;
390 }
391 }
392
393 class FunctionType extends AbstractFunctionType {
394 constructor(returnType, args, optionals, named) {
395 super();
396 this.returnType = returnType;
397 this.args = args;
398 this.optionals = optionals;
399 this.named = named;
400 }
401 }
402
403 function functionType(returnType, args, extra) {
404 // TODO(vsm): Cache / memomize?
405 var optionals;
406 var named;
407 if (extra === void 0) {
408 optionals = [];
409 named = {};
410 } else if (extra instanceof Array) {
411 optionals = extra;
412 named = {};
413 } else {
414 optionals = [];
415 named = extra;
416 }
417 return new FunctionType(returnType, args, optionals, named);
418 }
419 dart.functionType = functionType;
420
421 class Typedef extends AbstractFunctionType {
422 constructor(name, closure) {
423 super();
424 this._name = name;
425 this._closure = closure;
426 this._functionType = null;
427 }
428
429 get name() {
430 return this._name;
431 }
432
433 get functionType() {
434 if (!this._functionType) {
435 this._functionType = this._closure();
436 }
437 return this._functionType;
438 }
439
440 get returnType() {
441 return this.functionType.returnType;
442 }
443
444 get args() {
445 return this.functionType.args;
446 }
447
448 get optionals() {
449 return this.functionType.optionals;
450 }
451
452 get named() {
453 return this.functionType.named;
454 }
455 }
456
457 function typedef(name, closure) {
458 return new Typedef(name, closure);
459 }
460 dart.typedef = typedef;
461
462 function isFunctionType(type) {
463 return isClassSubType(type, core.Function) || type instanceof AbstractFuncti onType;
464 }
465
466 function getFunctionType(obj) {
467 // TODO(vsm): Encode this properly on the function for Dart-generated code.
468 var args = Array.apply(null, new Array(obj.length)).map(function(){return co re.Object});
469 return functionType(dart.bottom, args);
470 }
471
472 function isFunctionSubType(ft1, ft2) {
473 if (ft2 == core.Function) {
474 return true;
475 }
476
477 let ret1 = ft1.returnType;
478 let ret2 = ft2.returnType;
479
480 if (!isSubtype_(ret1, ret2)) {
481 // Covariant return types
482 // Note, void (which can only appear as a return type) is effectively
483 // treated as dynamic. If the base return type is void, we allow any
484 // subtype return type.
485 // E.g., we allow:
486 // () -> int <: () -> void
487 if (ret2 != dart.void) {
488 return false;
489 }
490 }
491
492 let args1 = ft1.args;
493 let args2 = ft2.args;
494
495 if (args1.length > args2.length) {
496 return false;
497 }
498
499 for (var i = 0; i < args1.length; ++i) {
500 if (!isSubtype_(args2[i], args1[i], true)) {
501 return false;
502 }
503 }
504
505 let optionals1 = ft1.optionals;
506 let optionals2 = ft2.optionals;
507
508 if (args1.length + optionals1.length < args2.length + optionals2.length) {
509 return false;
510 }
511
512 var j = 0;
513 for (var i = args1.length; i < args2.length; ++i, ++j) {
514 if (!isSubtype_(args2[i], optionals1[j], true)) {
515 return false;
516 }
517 }
518
519 for (var i = 0; i < optionals2.length; ++i, ++j) {
520 if (!isSubtype_(optionals2[i], optionals1[j], true)) {
521 return false;
522 }
523 }
524
525 let named1 = ft1.named;
526 let named2 = ft2.named;
527
528 let names = Object.getOwnPropertyNames(named2);
529 for (var i = 0; i < names.length; ++i) {
530 let name = names[i];
531 let n1 = named1[name]
532 let n2 = named2[name];
533 if (n1 === void 0) {
534 return false;
535 }
536 if (!isSubtype_(n2, n1, true)) {
537 return false;
538 }
539 }
540
541 return true;
542 }
543
304 /** 544 /**
305 * Defines a lazy property. 545 * Defines a lazy property.
306 * After initial get or set, it will replace itself with a value property. 546 * After initial get or set, it will replace itself with a value property.
307 */ 547 */
308 // TODO(jmesserly): is this the best implementation for JS engines? 548 // TODO(jmesserly): is this the best implementation for JS engines?
309 // TODO(jmesserly): reusing descriptor objects has been shown to improve 549 // TODO(jmesserly): reusing descriptor objects has been shown to improve
310 // performance in other projects (e.g. webcomponents.js ShadowDOM polyfill). 550 // performance in other projects (e.g. webcomponents.js ShadowDOM polyfill).
311 function defineLazyProperty(to, name, desc) { 551 function defineLazyProperty(to, name, desc) {
312 let init = desc.get; 552 let init = desc.get;
313 let writable = !!desc.set; 553 let writable = !!desc.set;
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after
501 } 741 }
502 return value; 742 return value;
503 } 743 }
504 return makeGenericType; 744 return makeGenericType;
505 } 745 }
506 dart.generic = generic; 746 dart.generic = generic;
507 747
508 // TODO(jmesserly): right now this is a sentinel. It should be a type object 748 // TODO(jmesserly): right now this is a sentinel. It should be a type object
509 // of some sort, assuming we keep around `dynamic` at runtime. 749 // of some sort, assuming we keep around `dynamic` at runtime.
510 dart.dynamic = { toString() { return 'dynamic'; } }; 750 dart.dynamic = { toString() { return 'dynamic'; } };
751 dart.void = { toString() { return 'void'; } };
752 dart.bottom = { toString() { return 'bottom'; } };
753
754 // TODO(vsm): How should we encode the runtime type?
755 dart.runtimeType = Symbol('runtimeType');
511 756
512 dart.JsSymbol = Symbol; 757 dart.JsSymbol = Symbol;
513 758
514 // TODO(jmesserly): hack to bootstrap the SDK 759 // TODO(jmesserly): hack to bootstrap the SDK
515 _js_helper = _js_helper || {}; 760 _js_helper = _js_helper || {};
516 _js_helper.checkNum = notNull; 761 _js_helper.checkNum = notNull;
517 762
518 })(dart || (dart = {})); 763 })(dart || (dart = {}));
OLDNEW
« no previous file with comments | « lib/runtime/dart/isolate.js ('k') | lib/src/codegen/js_codegen.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698