Index: lib/runtime/dart/_operations.js |
diff --git a/lib/runtime/dart/_operations.js b/lib/runtime/dart/_operations.js |
index 082a0f4b2ed2f5e9c4782a61ca3bd194433a819b..d09154d6e3e455e0b0f85e4b8615803abda45b17 100644 |
--- a/lib/runtime/dart/_operations.js |
+++ b/lib/runtime/dart/_operations.js |
@@ -1,71 +1,41 @@ |
-// 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. |
- |
-/* This library defines runtime operations on objects used by the code |
- * generator. |
- */ |
dart_library.library('dart/_operations', null, /* Imports */[ |
-], /* Lazy Imports */[ |
- 'dart/_utils', |
- 'dart/async', |
- 'dart/collection', |
- 'dart/core', |
- 'dart/_js_helper', |
+ 'dart/_utils' |
+], /* Lazy imports */[ |
'dart/_classes', |
'dart/_errors', |
'dart/_rtti', |
- 'dart/_types' |
-], function(exports, dart_utils, async, collection, core, _js_helper, classes, errors, rtti, |
- types) { |
+ 'dart/_types', |
+ 'dart/core', |
+ 'dart/async', |
+ 'dart/collection', |
+ 'dart/_js_helper' |
+], function(exports, utils, classes, _errors, rtti, _types, core, async, collection, _js_helper) { |
'use strict'; |
- |
- const getOwnNamesAndSymbols = dart_utils.getOwnNamesAndSymbols; |
- const throwError = dart_utils.throwError; |
- |
+ const getOwnNamesAndSymbols = utils.getOwnNamesAndSymbols; |
const getOwnPropertyNames = Object.getOwnPropertyNames; |
const hasOwnProperty = Object.prototype.hasOwnProperty; |
- |
function _canonicalFieldName(obj, name, args, displayName) { |
name = classes.canonicalMember(obj, name); |
if (name) return name; |
- // TODO(jmesserly): in the future we might have types that "overlay" Dart |
- // methods while also exposing the full native API, e.g. dart:html vs |
- // dart:dom. To support that we'd need to fall back to the normal name |
- // if an extension method wasn't found. |
- errors.throwNoSuchMethod(obj, displayName, args); |
+ throwNoSuchMethod(obj, displayName, args); |
} |
- |
function dload(obj, field) { |
field = _canonicalFieldName(obj, field, [], field); |
if (classes.hasMethod(obj, field)) { |
return classes.bind(obj, field); |
} |
- // TODO(vsm): Implement NSM robustly. An 'in' check breaks on certain |
- // types. hasOwnProperty doesn't chase the proto chain. |
- // Also, do we want an NSM on regular JS objects? |
- // See: https://github.com/dart-lang/dev_compiler/issues/169 |
let result = obj[field]; |
return result; |
} |
- exports.dload = dload; |
- |
function dput(obj, field, value) { |
field = _canonicalFieldName(obj, field, [value], field); |
- // TODO(vsm): Implement NSM and type checks. |
- // See: https://github.com/dart-lang/dev_compiler/issues/170 |
obj[field] = value; |
return value; |
} |
- exports.dput = dput; |
- |
- |
- /// Check that a function of a given type can be applied to |
- /// actuals. |
function checkApply(type, actuals) { |
if (actuals.length < type.args.length) return false; |
let index = 0; |
- for(let i = 0; i < type.args.length; ++i) { |
+ for (let i = 0; i < type.args.length; ++i) { |
if (!instanceOfOrNull(actuals[i], type.args[i])) return false; |
++index; |
} |
@@ -73,39 +43,30 @@ dart_library.library('dart/_operations', null, /* Imports */[ |
let extras = actuals.length - type.args.length; |
if (type.optionals.length > 0) { |
if (extras > type.optionals.length) return false; |
- for(let i = 0, j=index; i < extras; ++i, ++j) { |
+ for (let i = 0, j = index; i < extras; ++i, ++j) { |
if (!instanceOfOrNull(actuals[j], type.optionals[i])) return false; |
} |
return true; |
} |
- // TODO(leafp): We can't tell when someone might be calling |
- // something expecting an optional argument with named arguments |
- |
if (extras != 1) return false; |
- // An empty named list means no named arguments |
if (getOwnPropertyNames(type.named).length == 0) return false; |
let opts = actuals[index]; |
let names = getOwnPropertyNames(opts); |
- // Type is something other than a map |
if (names.length == 0) return false; |
for (var name of names) { |
- if (!(hasOwnProperty.call(type.named, name))) { |
+ if (!hasOwnProperty.call(type.named, name)) { |
return false; |
} |
if (!instanceOfOrNull(opts[name], type.named[name])) return false; |
} |
return true; |
} |
- |
function throwNoSuchMethod(obj, name, args, opt_func) { |
if (obj === void 0) obj = opt_func; |
- errors.throwNoSuchMethod(obj, name, args); |
+ _errors.throwNoSuchMethod(obj, name, args); |
} |
- |
function checkAndCall(f, ftype, obj, args, name) { |
if (!(f instanceof Function)) { |
- // We're not a function (and hence not a method either) |
- // Grab the `call` method if it's not a function. |
if (f != null) { |
ftype = classes.getMethodType(f, 'call'); |
f = f.call; |
@@ -114,167 +75,91 @@ dart_library.library('dart/_operations', null, /* Imports */[ |
throwNoSuchMethod(obj, name, args); |
} |
} |
- // If f is a function, but not a method (no method type) |
- // then it should have been a function valued field, so |
- // get the type from the function. |
if (ftype === void 0) { |
ftype = rtti.read(f); |
} |
- |
if (!ftype) { |
- // TODO(leafp): Allow JS objects to go through? |
- // This includes the DOM. |
return f.apply(obj, args); |
} |
- |
if (checkApply(ftype, args)) { |
return f.apply(obj, args); |
} |
- |
- // TODO(leafp): throw a type error (rather than NSM) |
- // if the arity matches but the types are wrong. |
throwNoSuchMethod(obj, name, args, f); |
} |
- |
function dcall(f, ...args) { |
let ftype = rtti.read(f); |
return checkAndCall(f, ftype, void 0, args, 'call'); |
} |
- exports.dcall = dcall; |
- |
- /** Shared code for dsend, dindex, and dsetindex. */ |
function callMethod(obj, name, args, displayName) { |
let symbol = _canonicalFieldName(obj, name, args, displayName); |
let f = obj != null ? obj[symbol] : null; |
let ftype = classes.getMethodType(obj, name); |
return checkAndCall(f, ftype, obj, args, displayName); |
} |
- |
function dsend(obj, method, ...args) { |
return callMethod(obj, method, args, method); |
} |
- exports.dsend = dsend; |
- |
function dindex(obj, index) { |
return callMethod(obj, 'get', [index], '[]'); |
} |
- exports.dindex = dindex; |
- |
function dsetindex(obj, index, value) { |
callMethod(obj, 'set', [index, value], '[]='); |
return value; |
} |
- exports.dsetindex = dsetindex; |
- |
function _ignoreTypeFailure(actual, type) { |
- // TODO(vsm): Remove this hack ... |
- // This is primarily due to the lack of generic methods, |
- // but we need to triage all the errors. |
- let isSubtype = types.isSubtype; |
- if (isSubtype(type, core.Iterable) && isSubtype(actual, core.Iterable) || |
- isSubtype(type, async.Future) && isSubtype(actual, async.Future) || |
- isSubtype(type, core.Map) && isSubtype(actual, core.Map) || |
- isSubtype(type, core.Function) && isSubtype(actual, core.Function) || |
- isSubtype(type, async.Stream) && isSubtype(actual, async.Stream) || |
- isSubtype(type, async.StreamSubscription) && |
- isSubtype(actual, async.StreamSubscription)) { |
- console.warn('Ignoring cast fail from ' + types.typeName(actual) + |
- ' to ' + types.typeName(type)); |
+ let isSubtype = _types.isSubtype; |
+ if (isSubtype(type, core.Iterable) && isSubtype(actual, core.Iterable) || isSubtype(type, async.Future) && isSubtype(actual, async.Future) || isSubtype(type, core.Map) && isSubtype(actual, core.Map) || isSubtype(type, core.Function) && isSubtype(actual, core.Function) || isSubtype(type, async.Stream) && isSubtype(actual, async.Stream) || isSubtype(type, async.StreamSubscription) && isSubtype(actual, async.StreamSubscription)) { |
+ console.warn('Ignoring cast fail from ' + _types.typeName(actual) + ' to ' + _types.typeName(type)); |
return true; |
} |
return false; |
} |
- |
function strongInstanceOf(obj, type, ignoreFromWhiteList) { |
let actual = rtti.realRuntimeType(obj); |
- if (types.isSubtype(actual, type) || actual == types.jsobject) return true; |
+ if (_types.isSubtype(actual, type) || actual == _types.jsobject) return true; |
if (ignoreFromWhiteList == void 0) return false; |
- if (types.isGroundType(type)) return false; |
+ if (_types.isGroundType(type)) return false; |
if (_ignoreTypeFailure(actual, type)) return true; |
return false; |
} |
- exports.strongInstanceOf = strongInstanceOf; |
- |
function instanceOfOrNull(obj, type) { |
- if ((obj == null) || strongInstanceOf(obj, type, true)) return true; |
+ if (obj == null || strongInstanceOf(obj, type, true)) return true; |
return false; |
} |
- |
function instanceOf(obj, type) { |
if (strongInstanceOf(obj, type)) return true; |
- // TODO(#296): This is perhaps too eager to throw a StrongModeError? |
- // It will throw on <int>[] is List<String>. |
- // TODO(vsm): We can statically detect many cases where this |
- // check is unnecessary. |
- if (types.isGroundType(type)) return false; |
+ if (_types.isGroundType(type)) return false; |
let actual = rtti.realRuntimeType(obj); |
- dart_utils.throwStrongModeError('Strong mode is check failure: ' + |
- types.typeName(actual) + ' does not soundly subtype ' + |
- types.typeName(type)); |
+ utils.throwStrongModeError('Strong mode is check failure: ' + _types.typeName(actual) + ' does not soundly subtype ' + _types.typeName(type)); |
} |
- exports.instanceOf = instanceOf; |
- |
function cast(obj, type) { |
- // TODO(#296): This is perhaps too eager to throw a StrongModeError? |
- // TODO(vsm): handle non-nullable types |
if (instanceOfOrNull(obj, type)) return obj; |
let actual = rtti.realRuntimeType(obj); |
- if (types.isGroundType(type)) errors.throwCastError(actual, type); |
- |
+ if (_types.isGroundType(type)) _errors.throwCastError(actual, type); |
if (_ignoreTypeFailure(actual, type)) return obj; |
- |
- dart_utils.throwStrongModeError('Strong mode cast failure from ' + |
- types.typeName(actual) + ' to ' + types.typeName(type)); |
+ utils.throwStrongModeError('Strong mode cast failure from ' + _types.typeName(actual) + ' to ' + _types.typeName(type)); |
} |
- exports.cast = cast; |
- |
function asInt(obj) { |
if (obj == null) { |
return null; |
} |
if (Math.floor(obj) != obj) { |
- // Note: null will also be caught by this check |
- errors.throwCastError(rtti.realRuntimeType(obj), core.int); |
+ _errors.throwCastError(rtti.realRuntimeType(obj), core.int); |
} |
return obj; |
} |
- exports.asInt = asInt; |
- |
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 }; |
+ return {min: f.length, max: f.length}; |
} |
- exports.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; |
} |
- exports.equals = equals; |
- |
- /** Checks that `x` is not null or undefined. */ |
function notNull(x) { |
- if (x == null) errors.throwNullValueError(); |
+ if (x == null) _errors.throwNullValueError(); |
return x; |
} |
- exports.notNull = notNull; |
- |
- /** |
- * 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 = collection.LinkedHashMap.new(); |
if (Array.isArray(values)) { |
@@ -290,50 +175,28 @@ dart_library.library('dart/_operations', null, /* Imports */[ |
} |
return map; |
} |
- exports.map = map; |
- |
function assert(condition) { |
- if (!condition) errors.throwAssertionError(); |
+ if (!condition) _errors.throwAssertionError(); |
} |
- exports.assert = assert; |
- |
- let _stack = new WeakMap(); |
- function throw_(obj) { |
+ const _stack = new WeakMap(); |
+ function throw$(obj) { |
if (obj != null && (typeof obj == 'object' || typeof obj == 'function')) { |
- // TODO(jmesserly): couldn't we store the most recent stack in a single |
- // variable? There should only be one active stack trace. That would |
- // allow it to work for things like strings and numbers. |
_stack.set(obj, new Error()); |
} |
throw obj; |
} |
- exports.throw = throw_; |
- |
function getError(exception) { |
var stack = _stack.get(exception); |
return stack !== void 0 ? stack : exception; |
} |
- |
- // This is a utility function: it is only intended to be called from dev |
- // tools. |
function stackPrint(exception) { |
var error = getError(exception); |
console.log(error.stack ? error.stack : 'No stack trace for: ' + error); |
} |
- exports.stackPrint = stackPrint; |
- |
function stackTrace(exception) { |
var error = getError(exception); |
return _js_helper.getTraceFromException(error); |
} |
- exports.stackTrace = stackTrace; |
- |
- /** |
- * Implements a sequence of .? operations. |
- * |
- * Will call each successive callback, unless one returns null, which stops |
- * the sequence. |
- */ |
function nullSafe(obj, ...callbacks) { |
if (obj == null) return obj; |
for (const callback of callbacks) { |
@@ -342,27 +205,11 @@ dart_library.library('dart/_operations', null, /* Imports */[ |
} |
return obj; |
} |
- exports.nullSafe = nullSafe; |
- |
- let _value = Symbol('_value'); |
- /** |
- * Looks up a sequence of [keys] in [map], recursively, and |
- * returns the result. If the value is not found, [valueFn] will be called to |
- * add it. For example: |
- * |
- * let map = new Map(); |
- * putIfAbsent(map, [1, 2, 'hi ', 'there '], () => 'world'); |
- * |
- * ... will create a Map with a structure like: |
- * |
- * { 1: { 2: { 'hi ': { 'there ': 'world' } } } } |
- */ |
+ const _value = Symbol("_value"); |
function multiKeyPutIfAbsent(map, keys, valueFn) { |
for (let k of keys) { |
let value = map.get(k); |
if (!value) { |
- // TODO(jmesserly): most of these maps are very small (e.g. 1 item), |
- // so it may be worth optimizing for that. |
map.set(k, value = new Map()); |
} |
map = value; |
@@ -372,93 +219,96 @@ dart_library.library('dart/_operations', null, /* Imports */[ |
map.set(_value, value); |
return value; |
} |
- |
- /** The global constant table. */ |
const constants = new Map(); |
- |
- /** |
- * Canonicalize a constant object. |
- * |
- * Preconditions: |
- * - `obj` is an objects or array, not a primitive. |
- * - nested values of the object are themselves already canonicalized. |
- */ |
- function constant(obj) { |
+ function const$(obj) { |
let objectKey = [rtti.realRuntimeType(obj)]; |
- // TODO(jmesserly): there's no guarantee in JS that names/symbols are |
- // returned in the same order. |
- // |
- // We could probably get the same order if we're judicious about |
- // initializing fields in a consistent order across all const constructors. |
- // Alternatively we need a way to sort them to make consistent. |
- // |
- // Right now we use the (name,value) pairs in sequence, which prevents |
- // an object with incorrect field values being returned, but won't |
- // canonicalize correctly if key order is different. |
for (let name of getOwnNamesAndSymbols(obj)) { |
objectKey.push(name); |
objectKey.push(obj[name]); |
} |
return multiKeyPutIfAbsent(constants, objectKey, () => obj); |
} |
- exports.const = constant; |
- |
- |
- // The following are helpers for Object methods when the receiver |
- // may be null or primitive. These should only be generated by |
- // the compiler. |
function hashCode(obj) { |
if (obj == null) { |
return 0; |
} |
- // TODO(vsm): What should we do for primitives and non-Dart objects? |
switch (typeof obj) { |
- case "number": |
- case "boolean": |
- return obj & 0x1FFFFFFF; |
- case "string": |
- // TODO(vsm): Call the JSString hashCode? |
- return obj.length; |
+ case "number": |
+ case "boolean": |
+ { |
+ return obj & 0x1FFFFFFF; |
+ } |
+ case "string": |
+ { |
+ return obj.length; |
+ } |
} |
return obj.hashCode; |
} |
- exports.hashCode = hashCode; |
- |
function toString(obj) { |
if (obj == null) { |
return "null"; |
} |
return obj.toString(); |
} |
- exports.toString = toString; |
- |
function noSuchMethod(obj, invocation) { |
if (obj == null) { |
- errors.throwNoSuchMethod(obj, invocation.memberName, |
- invocation.positionalArguments, invocation.namedArguments); |
+ throwNoSuchMethod(obj, invocation.memberName, invocation.positionalArguments, invocation.namedArguments); |
} |
switch (typeof obj) { |
case "number": |
case "boolean": |
case "string": |
- errors.throwNoSuchMethod(obj, invocation.memberName, |
- invocation.positionalArguments, invocation.namedArguments); |
+ { |
+ throwNoSuchMethod(obj, invocation.memberName, invocation.positionalArguments, invocation.namedArguments); |
+ } |
} |
return obj.noSuchMethod(invocation); |
} |
- exports.noSuchMethod = noSuchMethod; |
- |
- class JsIterator { |
+ const JsIterator = class JsIterator { |
constructor(dartIterator) { |
this.dartIterator = dartIterator; |
} |
next() { |
let i = this.dartIterator; |
let done = !i.moveNext(); |
- return { done: done, value: done ? void 0 : i.current }; |
+ return {done: done, value: done ? void 0 : i.current}; |
} |
- } |
+ }; |
+ // Exports: |
+ exports.getOwnNamesAndSymbols = getOwnNamesAndSymbols; |
+ exports.getOwnPropertyNames = getOwnPropertyNames; |
+ exports.hasOwnProperty = hasOwnProperty; |
+ exports.dload = dload; |
+ exports.dput = dput; |
+ exports.checkApply = checkApply; |
+ exports.throwNoSuchMethod = throwNoSuchMethod; |
+ exports.checkAndCall = checkAndCall; |
+ exports.dcall = dcall; |
+ exports.callMethod = callMethod; |
+ exports.dsend = dsend; |
+ exports.dindex = dindex; |
+ exports.dsetindex = dsetindex; |
+ exports.strongInstanceOf = strongInstanceOf; |
+ exports.instanceOfOrNull = instanceOfOrNull; |
+ exports.instanceOf = instanceOf; |
+ exports.cast = cast; |
+ exports.asInt = asInt; |
+ exports.arity = arity; |
+ exports.equals = equals; |
+ exports.notNull = notNull; |
+ exports.map = map; |
+ exports.assert = assert; |
+ exports.throw = throw$; |
+ exports.getError = getError; |
+ exports.stackPrint = stackPrint; |
+ exports.stackTrace = stackTrace; |
+ exports.nullSafe = nullSafe; |
+ exports.multiKeyPutIfAbsent = multiKeyPutIfAbsent; |
+ exports.constants = constants; |
+ exports.const = const$; |
+ exports.hashCode = hashCode; |
+ exports.toString = toString; |
+ exports.noSuchMethod = noSuchMethod; |
exports.JsIterator = JsIterator; |
- |
- |
}); |