| Index: lib/runtime/dart_runtime.js
|
| diff --git a/lib/runtime/dart_runtime.js b/lib/runtime/dart_runtime.js
|
| index c872085277f70e6397360292955531d10363caef..f91a9347cac41280ed7591adf0c7ef7c044e6d5e 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,157 @@ 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) {
|
| + //TODO(vsm): handle non-nullable types
|
| + if (obj == null) return obj;
|
| + 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) {
|
| + 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.
|
| + 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;
|
| +
|
| + // 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 +253,30 @@ var dart, _js_helper;
|
| }
|
| dart.closureWrap = closureWrap;
|
|
|
| +
|
| + // TODO(jmesserly): this isn't currently used, but it could be if we want
|
| + // `obj is NonGroundType<T,S>` to be rejected at runtime instead of compile
|
| + // time. Also TODO: update this to handle functions.
|
| 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 +296,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 +319,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 +335,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 +347,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 +361,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 +369,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 +378,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 +402,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 +432,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 +446,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');
|
|
|
| /** 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 +492,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;
|
|
|
|
|