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

Unified Diff: lib/runtime/operations.js

Issue 1182653002: Refactor runtime into libraries, better type reps (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Add missing file Created 5 years, 6 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 side-by-side diff with in-line comments
Download patch
Index: lib/runtime/operations.js
diff --git a/lib/runtime/operations.js b/lib/runtime/operations.js
new file mode 100644
index 0000000000000000000000000000000000000000..8959596d2813293e79d46b1a11adbc4ef846a78a
--- /dev/null
+++ b/lib/runtime/operations.js
@@ -0,0 +1,394 @@
+// 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.
+ */
+loader.library('dart/operations', null, /* Imports */[
+], /* Lazy Imports */[
+ 'dart/async',
+ 'dart/classes',
+ 'dart/core',
+ 'dart/errors',
+ 'dart/rtti',
+ 'dart/types'
+], function(exports, async, classes, core, errors, rtti, types) {
+ 'use strict';
+
+ const getOwnNamesAndSymbols = js_utils.getOwnNamesAndSymbols;
+ const getOwnPropertyNames = js_utils.getOwnPropertyNames;
+ const hasOwnProperty = js_utils.hasOwnProperty;
+ const slice = js_utils.slice;
+ const throwRuntimeError = js_utils.throwRuntimeError;
+
+ 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);
+ }
+
+ 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];
+
+ // TODO(vsm): Check this more robustly.
+ if (typeof result == "function" && !hasOwnProperty.call(obj, field)) {
+ // This appears to be a method tearoff. Bind this.
+ return result.bind(obj);
+ }
+ 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;
+ }
+ 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) {
+ if (!instanceOfOrNull(actuals[i], type.args[i])) return false;
+ ++index;
+ }
+ if (actuals.length == type.args.length) return true;
+ 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) {
+ 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 (name of names) {
+ 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);
+ }
+
+ 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;
+ }
+ if (!(f instanceof Function)) {
+ 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 args = slice.call(arguments, 1);
+ 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[symbol];
+ let ftype = classes.getMethodType(obj, name);
+ return checkAndCall(f, ftype, obj, args, displayName);
+ }
+
+ function dsend(obj, method/*, ...args*/) {
+ return callMethod(obj, method, slice.call(arguments, 2));
+ }
+ exports.dsend = dsend;
+
+ function dindex(obj, index) {
+ return callMethod(obj, 'get', [index], '[]');
+ }
+ exports.dindex = dindex;
+
+ function dsetindex(obj, index, value) {
+ return callMethod(obj, 'set', [index, 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)) {
+ console.error('Ignoring cast fail from ' + types.typeToString(actual) +
+ ' to ' + types.typeToString(type));
+ return true;
+ }
+ return false;
+ }
+
+ function instanceOf(obj, type) {
+ return types.isSubtype(rtti.realRuntimeType(obj), type);
+ }
+ exports.instanceOf = instanceOf;
+
+ function instanceOfOrNull(obj, type) {
+ if ((obj == null) || instanceOf(obj, type)) return true;
+ let actual = rtti.realRuntimeType(obj);
+ if (_ignoreTypeFailure(actual, type)) return true;
+ return false;
+ }
+ exports.instanceOfOrNull = instanceOfOrNull;
+
+ function cast(obj, type) {
+ // TODO(vsm): handle non-nullable types
+ if (obj == null) return obj;
+ let actual = rtti.realRuntimeType(obj);
+ if (types.isSubtype(actual, type)) return obj;
+ if (_ignoreTypeFailure(actual, type)) return obj;
+ errors.throwCastError(actual, type);
+ }
+ exports.cast = cast;
+
+ 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 };
+ }
+ 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) {
+ // TODO(leafp): This is probably not the right error to throw.
+ if (x == null) throwRuntimeError('expected not-null value');
+ 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)) {
+ 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 getOwnPropertyNames(values)) {
+ map.set(key, values[key]);
+ }
+ }
+ return map;
+ }
+ exports.map = map;
+
+ function assert(condition) {
+ if (!condition) errors.throwAssertionError();
+ }
+ exports.assert = assert;
+
+ function throw_(obj) { throw obj; }
+ exports.throw_ = throw_;
+
+
+ function stackTrace(exception) {
+ return _js_helper.getTraceFromException(exception);
+ }
+ exports.stackTrace = stackTrace;
+
+ 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' } } } }
+ */
+ 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;
+ }
+ if (map.has(_value)) return map.get(_value);
+ let value = valueFn();
+ 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) {
+ 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;
+ }
+ 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);
+ }
+ switch (typeof obj) {
+ case "number":
+ case "boolean":
+ case "string":
+ errors.throwNoSuchMethod(obj, invocation.memberName,
+ invocation.positionalArguments, invocation.namedArguments);
+ }
+ return obj.noSuchMethod(invocation);
+ }
+ exports.noSuchMethod = noSuchMethod;
+
+ 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 };
+ }
+ }
+ exports.JsIterator = JsIterator;
+
+
+});

Powered by Google App Engine
This is Rietveld 408576698