Chromium Code Reviews| Index: lib/runtime/dart_runtime.js |
| diff --git a/lib/runtime/dart_runtime.js b/lib/runtime/dart_runtime.js |
| index c872085277f70e6397360292955531d10363caef..3e5c2d23bc853832ee43428dccbd90b7a0243e93 100644 |
| --- a/lib/runtime/dart_runtime.js |
| +++ b/lib/runtime/dart_runtime.js |
| @@ -6,25 +6,25 @@ var dart, _js_helper; |
| (function (dart) { |
| 'use strict'; |
| - var defineProperty = Object.defineProperty; |
| - var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; |
| - var getOwnPropertyNames = Object.getOwnPropertyNames; |
| + let defineProperty = Object.defineProperty; |
| + let getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; |
| + let getOwnPropertyNames = Object.getOwnPropertyNames; |
| // Adapted from Angular.js |
| - var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m; |
| - var FN_ARG_SPLIT = /,/; |
| - var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/; |
| - var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; |
| + let FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m; |
| + let FN_ARG_SPLIT = /,/; |
| + let FN_ARG = /^\s*(_?)(\S+?)\1\s*$/; |
| + let STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; |
| function formalParameterList(fn) { |
| - var fnText,argDecl; |
| - var args=[]; |
| + let fnText,argDecl; |
| + let args=[]; |
| fnText = fn.toString().replace(STRIP_COMMENTS, ''); |
| argDecl = fnText.match(FN_ARGS); |
| - var r = argDecl[1].split(FN_ARG_SPLIT); |
| - for(var a in r) { |
| - var arg = r[a]; |
| + let r = argDecl[1].split(FN_ARG_SPLIT); |
| + for(let a in r) { |
| + let arg = r[a]; |
| arg.replace(FN_ARG, function(all, underscore, name){ |
| args.push(name); |
| }); |
| @@ -54,12 +54,12 @@ var dart, _js_helper; |
| throwNoSuchMethod(obj, method, args); |
| } |
| } |
| - var formals = formalParameterList(f); |
| + let formals = formalParameterList(f); |
| // TODO(vsm): Type check args! We need to encode sufficient type info on f. |
| if (formals.length < args.length) { |
| throwNoSuchMethod(obj, name, args, f); |
| } else if (formals.length > args.length) { |
| - for (var i = args.length; i < formals.length; ++i) { |
| + for (let i = args.length; i < formals.length; ++i) { |
| if (formals[i].indexOf("opt$") != 0) { |
| throwNoSuchMethod(obj, name, args, f); |
| } |
| @@ -69,13 +69,13 @@ var dart, _js_helper; |
| } |
| function dinvokef(f/*, ...args*/) { |
| - var args = Array.prototype.slice.call(arguments, 1); |
| + let args = Array.prototype.slice.call(arguments, 1); |
| return checkAndCall(f, void 0, args, 'call'); |
| } |
| dart.dinvokef = dinvokef; |
| function dinvoke(obj, method/*, ...args*/) { |
| - var args = Array.prototype.slice.call(arguments, 2); |
| + let args = Array.prototype.slice.call(arguments, 2); |
| return checkAndCall(obj[method], obj, args, method); |
| } |
| dart.dinvoke = dinvoke; |
| @@ -95,19 +95,156 @@ var dart, _js_helper; |
| } |
| dart.dbinary = dbinary; |
| - function as_(obj, type) { |
| - // TODO(vsm): Implement. |
| - // if (obj == null || is(obj, type)) return obj; |
| - // throw new core.CastError(); |
| - return obj; |
| + function cast(obj, type) { |
| + if (obj == null) return obj; |
|
vsm
2015/04/01 15:38:00
Can you add a TODO(vsm) here about handling non-nu
Jennifer Messerly
2015/04/01 15:57:32
sure thing :)
|
| + let actual = getRuntimeType(obj); |
| + if (isSubtype(actual, type)) return obj; |
| + throw new _js_helper.CastErrorImplementation(actual, type); |
| } |
| - dart.as = as_; |
| + dart.as = cast; |
| - function is(obj, type) { |
| - // TODO(vsm): Implement. |
| - throw new core.UnimplementedError(); |
| + /** |
| + * Returns the runtime type of obj. This is the same as `obj.runtimeType` |
| + * but will not call an overridden getter. |
| + * |
| + * Currently this will return null for non-Dart objects. |
| + */ |
| + function getRuntimeType(obj) { |
| + switch (typeof obj) { |
| + case "undefined": |
| + return core.Null; |
| + case "number": |
| + return Math.floor(obj) == obj ? core.int : core.double; |
| + case "boolean": |
| + return core.bool; |
| + case "string": |
| + return core.String; |
| + case "symbol": |
| + return Symbol; |
| + } |
| + // Undefined is handled above. For historical reasons, |
| + // typeof null == "object" in JS. |
| + if (obj === null) return core.Null; |
| + return obj.constructor; |
| + } |
| + dart.getRuntimeType = getRuntimeType; |
| + |
| + function instanceOf(obj, type) { |
|
vsm
2015/04/01 15:38:00
This needs a test for null:
if (obj == null) {
Jennifer Messerly
2015/04/01 15:57:32
yeah, I saw that code, but it seems like null is a
vsm
2015/04/01 16:29:40
The problem is the opposite case:
isSubtype(core
Jennifer Messerly
2015/04/01 16:41:22
chatted about this & added a test case. I think we
|
| + return isSubtype(getRuntimeType(obj), type); |
| + } |
| + dart.is = instanceOf; |
| + |
| + /** |
| + * Computes the canonical type. |
| + * This maps JS types onto their corresponding Dart Type. |
| + */ |
| + // TODO(jmesserly): lots more needs to be done here. |
| + function canonicalType(t) { |
| + if (t === Object) return core.Object; |
| + if (t === Function) return core.Function; |
| + if (t === Array) return core.List; |
| + |
| + // We shouldn't normally get here with these types, unless something strange |
| + // happens like subclassing Number in JS and passing it to Dart. |
| + if (t === String) return core.String; |
| + if (t === Number) return core.double; |
| + if (t === Boolean) return core.bool; |
| + return t; |
| + } |
| + |
| + let subtypeMap = new Map(); |
| + function isSubtype(t1, t2) { |
| + // See if we already know the answer |
| + // TODO(jmesserly): general purpose memoize function? |
| + let map = subtypeMap.get(t1); |
| + let result; |
| + if (map) { |
| + result = map.get(t2); |
| + if (result !== void 0) return result; |
| + } else { |
| + subtypeMap.set(t1, map = new Map()); |
| + } |
| + map.set(t2, result = isSubtype_(t1, t2)); |
| + return result; |
| + } |
| + dart.isSubtype = isSubtype; |
| + |
| + function isSubtype_(t1, t2) { |
| + t1 = canonicalType(t1); |
| + t2 = canonicalType(t2); |
| + if (t1 == t2) return true; |
| + |
| + // In Dart, dynamic is effectively both top and bottom. |
| + // Here, we treat dynamic as top - the base type of everything. |
|
vsm
2015/04/01 15:38:00
We'll need to update as per recent CL. Both dynam
Jennifer Messerly
2015/04/01 15:57:32
sounds good. happy to update either now or after y
|
| + if (t1 == dart.dynamic) return false; |
| + if (t2 == dart.dynamic) return true; |
| + |
| + if (t2 == core.Object) return true; |
| + if (t1 == core.Object) return false; |
| + |
| + // "Traditional" name-based subtype check. |
| + if (isClassSubType(t1, t2)) { |
| + return true; |
| + } |
| + |
| + // Function subtyping. |
| + // TODO(jmesserly): implement. |
| + return false; |
| + } |
| + |
| + function safeGetOwnProperty(obj, name) { |
| + var desc = getOwnPropertyDescriptor(obj, name); |
| + if (desc) return desc.value; |
| + } |
| + |
| + function isClassSubType(t1, t2) { |
| + // We support Dart's covariant generics with the caveat that we do not |
| + // substitute bottom for dynamic in subtyping rules. |
| + // I.e., given T1, ..., Tn where at least one Ti != dynamic we disallow: |
| + // - S !<: S<T1, ..., Tn> |
| + // - S<dynamic, ..., dynamic> !<: S<T1, ..., Tn> |
| + if (t1 == t2) return true; |
| + |
| + if (t1 == core.Object) return false; |
| + |
| + // Check if t1 and t2 have the same raw type. If so, check covariance on |
| + // type parameters. |
| + let raw1 = safeGetOwnProperty(t1, dart.originalDeclaration); |
| + let raw2 = safeGetOwnProperty(t2, dart.originalDeclaration); |
| + if (raw1 != null && raw1 == raw2) { |
| + let typeArguments1 = safeGetOwnProperty(t1, dart.typeArguments); |
| + let typeArguments2 = safeGetOwnProperty(t2, dart.typeArguments); |
| + let length = typeArguments1.length; |
| + if (typeArguments2.length == 0) { |
| + // t2 is the raw form of t1 |
| + return true; |
| + } else if (length == 0) { |
| + // t1 is raw, but t2 is not |
| + return false; |
| + } |
| + assert(length == typeArguments2.length); |
| + for (let i = 0; i < length; ++i) { |
| + if (!isSubtype(typeArguments1[i], typeArguments2[i])) { |
| + return false; |
| + } |
| + } |
| + return true; |
| + } |
| + |
| + // Check superclass. |
| + if (isClassSubType(t1.__proto__, t2)) return true; |
|
vsm
2015/04/01 15:37:59
This won't cover mixins, right? The application o
Jennifer Messerly
2015/04/01 15:57:32
doh! Good catch. It seems like the Dart code might
vsm
2015/04/01 16:29:40
Yeah, updating my CL too. :-)
|
| + |
| + // Check interfaces. |
| + let getInterfaces = safeGetOwnProperty(t1, dart.implements); |
| + if (getInterfaces) { |
| + for (let i1 of getInterfaces()) { |
| + // TODO(jmesserly): remove the != null check once we can load core libs. |
| + if (i1 != null && isClassSubType(i1, t2)) return true; |
| + } |
| + } |
| + |
| + return false; |
| } |
| - dart.is = is; |
| function closureWrap(obj, type) { |
| // TODO(vsm): Remove this once we handle in the checker. |
| @@ -115,21 +252,27 @@ var dart, _js_helper; |
| } |
| dart.closureWrap = closureWrap; |
| + // TODO(jmesserly): do we need this function? |
|
vsm
2015/04/01 15:38:00
We may want to keep this around if we want to reje
Jennifer Messerly
2015/04/01 15:57:32
sounds good. Updated the comment
|
| function isGroundType(type) { |
| - // TODO(vsm): Implement. |
| - throw new core.UnimplementedError(); |
| + let typeArgs = safeGetOwnProperty(type, dart.typeArguments); |
| + if (!typeArgs) return true; |
| + for (let t of typeArgs) { |
| + if (t != core.Object && t != dart.dynamic) return false; |
| + } |
| + return true; |
| } |
| dart.isGroundType = isGroundType; |
| function arity(f) { |
| - // TODO(vsm): Implement. |
| - throw new core.UnimplementedError(); |
| + // TODO(jmesserly): need to parse optional params. |
| + // In ES6, length is the number of required arguments. |
| + return { min: f.length, max: f.length }; |
| } |
| dart.arity = arity; |
| function equals(x, y) { |
| if (x == null || y == null) return x == y; |
| - var eq = x['==']; |
| + let eq = x['==']; |
| return eq ? eq.call(x, y) : x == y; |
| } |
| dart.equals = equals; |
| @@ -149,19 +292,19 @@ var dart, _js_helper; |
| // TODO(jmesserly): reusing descriptor objects has been shown to improve |
| // performance in other projects (e.g. webcomponents.js ShadowDOM polyfill). |
| function defineLazyProperty(to, name, desc) { |
| - var init = desc.get; |
| - var writable = !!desc.set; |
| + let init = desc.get; |
| + let writable = !!desc.set; |
| function lazySetter(value) { |
| defineProperty(to, name, { value: value, writable: writable }); |
| } |
| function lazyGetter() { |
| // Clear the init function to detect circular initialization. |
| - var f = init; |
| + let f = init; |
| if (f === null) throw 'circular initialization for field ' + name; |
| init = null; |
| // Compute and store the value. |
| - var value = f(); |
| + let value = f(); |
| lazySetter(value); |
| return value; |
| } |
| @@ -172,9 +315,9 @@ var dart, _js_helper; |
| } |
| function defineLazy(to, from) { |
| - var names = getOwnPropertyNames(from); |
| - for (var i = 0; i < names.length; i++) { |
| - var name = names[i]; |
| + let names = getOwnPropertyNames(from); |
| + for (let i = 0; i < names.length; i++) { |
| + let name = names[i]; |
| defineLazyProperty(to, name, getOwnPropertyDescriptor(from, name)); |
| } |
| } |
| @@ -188,9 +331,9 @@ var dart, _js_helper; |
| * This operation is commonly called `mixin` in JS. |
| */ |
| function copyProperties(to, from) { |
| - var names = getOwnPropertyNames(from); |
| - for (var i = 0; i < names.length; i++) { |
| - var name = names[i]; |
| + let names = getOwnPropertyNames(from); |
| + for (let i = 0; i < names.length; i++) { |
| + let name = names[i]; |
| defineProperty(to, name, getOwnPropertyDescriptor(from, name)); |
| } |
| return to; |
| @@ -200,7 +343,7 @@ var dart, _js_helper; |
| /** The Symbol for storing type arguments on a specialized generic type. */ |
| dart.mixins = Symbol('mixins'); |
| - dart.implements = Symbol('implements') |
| + dart.implements = Symbol('implements'); |
| /** |
| * Returns a new type that mixes members from base and all mixins. |
| @@ -214,7 +357,7 @@ var dart, _js_helper; |
| function mixin(base/*, ...mixins*/) { |
| // Create an initializer for the mixin, so when derived constructor calls |
| // super, we can correctly initialize base and mixins. |
| - var mixins = Array.prototype.slice.call(arguments, 1); |
| + let mixins = Array.prototype.slice.call(arguments, 1); |
| // Create a class that will hold all of the mixin methods. |
| class Mixin extends base { |
| @@ -222,8 +365,8 @@ var dart, _js_helper; |
| [base.name](/*...args*/) { |
| // Run mixin initializers. They cannot have arguments. |
| // Run them backwards so most-derived mixin is initialized first. |
| - for (var i = mixins.length - 1; i >= 0; i--) { |
| - var mixin = mixins[i]; |
| + for (let i = mixins.length - 1; i >= 0; i--) { |
| + let mixin = mixins[i]; |
| mixin.prototype[mixin.name].call(this); |
| } |
| // Run base initializer. |
| @@ -231,8 +374,8 @@ var dart, _js_helper; |
| } |
| } |
| // Copy each mixin's methods, with later ones overwriting earlier entries. |
| - for (var i = 0; i < mixins.length; i++) { |
| - copyProperties(Mixin.prototype, mixins[i].prototype); |
| + for (let m of mixins) { |
| + copyProperties(Mixin.prototype, m.prototype); |
| } |
| // Save mixins for reflection |
| Mixin[dart.mixins] = mixins; |
| @@ -255,19 +398,16 @@ var dart, _js_helper; |
| */ |
| // TODO(jmesserly): this could be faster |
| function map(values) { |
| - var map = new collection.LinkedHashMap(); |
| + let map = new collection.LinkedHashMap(); |
| if (Array.isArray(values)) { |
| - for (var i = 0, end = values.length - 1; i < end; i += 2) { |
| - var key = values[i]; |
| - var value = values[i + 1]; |
| + for (let i = 0, end = values.length - 1; i < end; i += 2) { |
| + let key = values[i]; |
| + let value = values[i + 1]; |
| map.set(key, value); |
| } |
| } else if (typeof values === 'object') { |
| - var keys = Object.getOwnPropertyNames(values); |
| - for (var i = 0; i < keys.length; i++) { |
| - var key = keys[i]; |
| - var value = values[key]; |
| - map.set(key, value); |
| + for (let key of Object.getOwnPropertyNames(values)) { |
| + map.set(key, values[key]); |
| } |
| } |
| return map; |
| @@ -288,9 +428,9 @@ var dart, _js_helper; |
| * function with the same name. For example `new SomeClass.name(args)`. |
| */ |
| function defineNamedConstructor(clazz, name) { |
| - var proto = clazz.prototype; |
| - var initMethod = proto[name]; |
| - var ctor = function() { return initMethod.apply(this, arguments); } |
| + let proto = clazz.prototype; |
| + let initMethod = proto[name]; |
| + let ctor = function() { return initMethod.apply(this, arguments); } |
| ctor.prototype = proto; |
| clazz[name] = ctor; |
| } |
| @@ -302,33 +442,37 @@ var dart, _js_helper; |
| dart.stackTrace = stackTrace; |
| /** The Symbol for storing type arguments on a specialized generic type. */ |
| - dart.typeSignature = Symbol('typeSignature'); |
| + dart.typeArguments = Symbol('typeArguments'); |
| + dart.originalDeclaration = Symbol('originalDeclaration'); |
|
Jennifer Messerly
2015/04/01 00:32:35
better name suggestions?
|
| /** Memoize a generic type constructor function. */ |
| function generic(typeConstructor) { |
| - var length = typeConstructor.length; |
| - if (length < 1) throw 'must have at least one generic type argument'; |
| + let length = typeConstructor.length; |
| + if (length < 1) throw Error('must have at least one generic type argument'); |
| - var resultMap = new Map(); |
| + let resultMap = new Map(); |
| function makeGenericType(/*...arguments*/) { |
| if (arguments.length != length && arguments.length != 0) { |
| - throw 'requires ' + length + ' or 0 type arguments'; |
| + throw Error('requires ' + length + ' or 0 type arguments'); |
| } |
| - |
| - var value = resultMap; |
| - for (var i = 0; i < length; i++) { |
| - var arg = arguments[i]; |
| - if (arg === void 0) arg = dart.dynamic; |
| - |
| - var map = value; |
| + let args = Array.prototype.slice.call(arguments); |
| + while (args.length < length) args.push(dart.dynamic); |
| + |
| + let value = resultMap; |
| + for (let i = 0; i < length; i++) { |
| + let arg = args[i]; |
| + if (arg == null) { |
| + throw Error('type arguments should not be null: ' + typeConstructor); |
| + } |
| + let map = value; |
| value = map.get(arg); |
| if (value === void 0) { |
| if (i + 1 == length) { |
| - value = typeConstructor.apply(null, arguments); |
| + value = typeConstructor.apply(null, args); |
| // Save the type constructor and arguments for reflection. |
| if (value) { |
| - var args = Array.prototype.slice.call(arguments); |
| - value[dart.typeSignature] = [makeGenericType].concat(args); |
| + value[dart.typeArguments] = args; |
| + value[dart.originalDeclaration] = makeGenericType; |
| } |
| } else { |
| value = new Map(); |
| @@ -344,7 +488,7 @@ var dart, _js_helper; |
| // TODO(jmesserly): right now this is a sentinel. It should be a type object |
| // of some sort, assuming we keep around `dynamic` at runtime. |
| - dart.dynamic = Object.create(null); |
| + dart.dynamic = { toString() { return 'dynamic'; } }; |
| dart.JsSymbol = Symbol; |