a4cbcc0c07
or: how creating a SplayTreeSet<String> leads to fixing a bunch of stuff R=vsm@google.com Review URL: https://codereview.chromium.org/1058653002
519 lines
16 KiB
JavaScript
519 lines
16 KiB
JavaScript
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
var dart, _js_helper;
|
|
(function (dart) {
|
|
'use strict';
|
|
|
|
let defineProperty = Object.defineProperty;
|
|
let getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
|
|
let getOwnPropertyNames = Object.getOwnPropertyNames;
|
|
|
|
// Adapted from Angular.js
|
|
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) {
|
|
let fnText,argDecl;
|
|
let args=[];
|
|
fnText = fn.toString().replace(STRIP_COMMENTS, '');
|
|
argDecl = fnText.match(FN_ARGS);
|
|
|
|
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);
|
|
});
|
|
}
|
|
return args;
|
|
}
|
|
|
|
function dload(obj, field) {
|
|
if (!(field in obj)) {
|
|
throw new core.NoSuchMethodError(obj, field);
|
|
}
|
|
return obj[field];
|
|
}
|
|
dart.dload = dload;
|
|
|
|
// TODO(jmesserly): this should call noSuchMethod, not throw.
|
|
function throwNoSuchMethod(obj, name, args, opt_func) {
|
|
if (obj === void 0) obj = opt_func;
|
|
throw new core.NoSuchMethodError(obj, name, args);
|
|
}
|
|
|
|
function checkAndCall(f, obj, args, name) {
|
|
if (!(f instanceof Function)) {
|
|
// Grab the `call` method if it's not a function.
|
|
if (f !== null) f = f.call;
|
|
if (!(f instanceof Function)) {
|
|
throwNoSuchMethod(obj, method, args);
|
|
}
|
|
}
|
|
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 (let i = args.length; i < formals.length; ++i) {
|
|
if (formals[i].indexOf("opt$") != 0) {
|
|
throwNoSuchMethod(obj, name, args, f);
|
|
}
|
|
}
|
|
}
|
|
return f.apply(obj, args);
|
|
}
|
|
|
|
function dinvokef(f/*, ...args*/) {
|
|
let args = Array.prototype.slice.call(arguments, 1);
|
|
return checkAndCall(f, void 0, args, 'call');
|
|
}
|
|
dart.dinvokef = dinvokef;
|
|
|
|
function dinvoke(obj, method/*, ...args*/) {
|
|
let args = Array.prototype.slice.call(arguments, 2);
|
|
return checkAndCall(obj[method], obj, args, method);
|
|
}
|
|
dart.dinvoke = dinvoke;
|
|
|
|
function dindex(obj, index) {
|
|
return checkAndCall(obj.get, obj, [index], '[]');
|
|
}
|
|
dart.dindex = dindex;
|
|
|
|
function dsetindex(obj, index, value) {
|
|
return checkAndCall(obj.set, obj, [index, value], '[]=');
|
|
}
|
|
dart.dsetindex = dindex;
|
|
|
|
function dbinary(left, op, right) {
|
|
return checkAndCall(left[op], left, [right], op);
|
|
}
|
|
dart.dbinary = dbinary;
|
|
|
|
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 = cast;
|
|
|
|
/**
|
|
* 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 this properly.
|
|
if (isClassSubType(t1, core.Function) &&
|
|
isClassSubType(t2, core.Function)) {
|
|
return true;
|
|
}
|
|
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 mixins.
|
|
let mixins = safeGetOwnProperty(t1, dart.mixins);
|
|
if (mixins) {
|
|
for (let m1 of mixins) {
|
|
// TODO(jmesserly): remove the != null check once we can load core libs.
|
|
if (m1 != null && isClassSubType(m1, 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;
|
|
}
|
|
|
|
function closureWrap(obj, type) {
|
|
// TODO(vsm): Remove this once we handle in the checker.
|
|
return obj;
|
|
}
|
|
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) {
|
|
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(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;
|
|
let eq = x['=='];
|
|
return eq ? eq.call(x, y) : x == y;
|
|
}
|
|
dart.equals = equals;
|
|
|
|
/** Checks that `x` is not null or undefined. */
|
|
function notNull(x) {
|
|
if (x == null) throw 'expected not-null value';
|
|
return x;
|
|
}
|
|
dart.notNull = notNull;
|
|
|
|
/**
|
|
* Defines a lazy property.
|
|
* After initial get or set, it will replace itself with a value property.
|
|
*/
|
|
// TODO(jmesserly): is this the best implementation for JS engines?
|
|
// 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) {
|
|
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.
|
|
let f = init;
|
|
if (f === null) throw 'circular initialization for field ' + name;
|
|
init = null;
|
|
|
|
// Compute and store the value.
|
|
let value = f();
|
|
lazySetter(value);
|
|
return value;
|
|
}
|
|
desc.get = lazyGetter;
|
|
desc.configurable = true;
|
|
if (writable) desc.set = lazySetter;
|
|
defineProperty(to, name, desc);
|
|
}
|
|
|
|
function defineLazy(to, from) {
|
|
let names = getOwnPropertyNames(from);
|
|
for (let i = 0; i < names.length; i++) {
|
|
let name = names[i];
|
|
defineLazyProperty(to, name, getOwnPropertyDescriptor(from, name));
|
|
}
|
|
}
|
|
// TODO(jmesserly): these are identical, but this makes it easier to grep for.
|
|
dart.defineLazyClass = defineLazy;
|
|
dart.defineLazyProperties = defineLazy;
|
|
dart.defineLazyClassGeneric = defineLazyProperty;
|
|
|
|
/**
|
|
* Copy properties from source to destination object.
|
|
* This operation is commonly called `mixin` in JS.
|
|
*/
|
|
function copyProperties(to, from) {
|
|
let names = getOwnPropertyNames(from);
|
|
for (let i = 0; i < names.length; i++) {
|
|
let name = names[i];
|
|
defineProperty(to, name, getOwnPropertyDescriptor(from, name));
|
|
}
|
|
return to;
|
|
}
|
|
dart.copyProperties = copyProperties;
|
|
|
|
|
|
/** The Symbol for storing type arguments on a specialized generic type. */
|
|
dart.mixins = Symbol('mixins');
|
|
dart.implements = Symbol('implements');
|
|
|
|
/**
|
|
* Returns a new type that mixes members from base and all mixins.
|
|
*
|
|
* Each mixin applies in sequence, with further to the right ones overriding
|
|
* previous entries.
|
|
*
|
|
* For each mixin, we only take its own properties, not anything from its
|
|
* superclass (prototype).
|
|
*/
|
|
function mixin(base/*, ...mixins*/) {
|
|
// Create an initializer for the mixin, so when derived constructor calls
|
|
// super, we can correctly initialize base and mixins.
|
|
let mixins = Array.prototype.slice.call(arguments, 1);
|
|
|
|
// Create a class that will hold all of the mixin methods.
|
|
class Mixin extends base {
|
|
// Initializer method: run mixin initializers, then the base.
|
|
[base.name](/*...args*/) {
|
|
// Run mixin initializers. They cannot have arguments.
|
|
// Run them backwards so most-derived mixin is initialized first.
|
|
for (let i = mixins.length - 1; i >= 0; i--) {
|
|
let mixin = mixins[i];
|
|
let init = mixin.prototype[mixin.name];
|
|
if (init) init.call(this);
|
|
}
|
|
// Run base initializer.
|
|
let init = base.prototype[base.name];
|
|
if (init) init.apply(this, arguments);
|
|
}
|
|
}
|
|
// Copy each mixin's methods, with later ones overwriting earlier entries.
|
|
for (let m of mixins) {
|
|
copyProperties(Mixin.prototype, m.prototype);
|
|
}
|
|
// Save mixins for reflection
|
|
Mixin[dart.mixins] = mixins;
|
|
return Mixin;
|
|
}
|
|
dart.mixin = mixin;
|
|
|
|
/**
|
|
* Creates a dart:collection LinkedHashMap.
|
|
*
|
|
* For a map with string keys an object literal can be used, for example
|
|
* `map({'hi': 1, 'there': 2})`.
|
|
*
|
|
* Otherwise an array should be used, for example `map([1, 2, 3, 4])` will
|
|
* create a map with keys [1, 3] and values [2, 4]. Each key-value pair
|
|
* should be adjacent entries in the array.
|
|
*
|
|
* For a map with no keys the function can be called with no arguments, for
|
|
* example `map()`.
|
|
*/
|
|
// TODO(jmesserly): this could be faster
|
|
function map(values) {
|
|
let map = new collection.LinkedHashMap();
|
|
if (Array.isArray(values)) {
|
|
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') {
|
|
for (let key of Object.getOwnPropertyNames(values)) {
|
|
map.set(key, values[key]);
|
|
}
|
|
}
|
|
return map;
|
|
}
|
|
dart.map = map;
|
|
|
|
function assert(condition) {
|
|
// TODO(jmesserly): throw assertion error.
|
|
if (!condition) throw 'assertion failed';
|
|
}
|
|
dart.assert = assert;
|
|
|
|
function throw_(obj) { throw obj; }
|
|
dart.throw_ = throw_;
|
|
|
|
/**
|
|
* Given a class and an initializer method name, creates a constructor
|
|
* function with the same name. For example `new SomeClass.name(args)`.
|
|
*/
|
|
function defineNamedConstructor(clazz, name) {
|
|
let proto = clazz.prototype;
|
|
let initMethod = proto[name];
|
|
let ctor = function() { return initMethod.apply(this, arguments); }
|
|
ctor.prototype = proto;
|
|
clazz[name] = ctor;
|
|
}
|
|
dart.defineNamedConstructor = defineNamedConstructor;
|
|
|
|
function stackTrace(exception) {
|
|
throw new core.UnimplementedError();
|
|
}
|
|
dart.stackTrace = stackTrace;
|
|
|
|
/** The Symbol for storing type arguments on a specialized generic type. */
|
|
dart.typeArguments = Symbol('typeArguments');
|
|
dart.originalDeclaration = Symbol('originalDeclaration');
|
|
|
|
/** Memoize a generic type constructor function. */
|
|
function generic(typeConstructor) {
|
|
let length = typeConstructor.length;
|
|
if (length < 1) throw Error('must have at least one generic type argument');
|
|
|
|
let resultMap = new Map();
|
|
function makeGenericType(/*...arguments*/) {
|
|
if (arguments.length != length && arguments.length != 0) {
|
|
throw Error('requires ' + length + ' or 0 type arguments');
|
|
}
|
|
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, args);
|
|
// Save the type constructor and arguments for reflection.
|
|
if (value) {
|
|
value[dart.typeArguments] = args;
|
|
value[dart.originalDeclaration] = makeGenericType;
|
|
}
|
|
} else {
|
|
value = new Map();
|
|
}
|
|
map.set(arg, value);
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
return makeGenericType;
|
|
}
|
|
dart.generic = generic;
|
|
|
|
// 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 = { toString() { return 'dynamic'; } };
|
|
|
|
dart.JsSymbol = Symbol;
|
|
|
|
// TODO(jmesserly): hack to bootstrap the SDK
|
|
_js_helper = _js_helper || {};
|
|
_js_helper.checkNum = notNull;
|
|
|
|
})(dart || (dart = {}));
|