Chromium Code Reviews| Index: lib/runtime/dart_runtime.js |
| diff --git a/lib/runtime/dart_runtime.js b/lib/runtime/dart_runtime.js |
| index 200d187f7e01a6436d27ab56f7eb53e6aa172304..468ac5dd7aabdfe7c4bef2a83ea2643f3b83f548 100644 |
| --- a/lib/runtime/dart_runtime.js |
| +++ b/lib/runtime/dart_runtime.js |
| @@ -2,7 +2,7 @@ |
| // 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, _js_primitives; |
| +var dart, _js_helper, _js_primitives, dartx; |
| (function (dart) { |
| 'use strict'; |
| @@ -11,17 +11,19 @@ var dart, _js_helper, _js_primitives; |
| // See: https://github.com/dart-lang/dev_compiler/issues/164 |
| dart.globalState = null; |
| - let defineProperty = Object.defineProperty; |
| - let getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; |
| - let getOwnPropertyNames = Object.getOwnPropertyNames; |
| - let getOwnPropertySymbols = Object.getOwnPropertySymbols; |
| + const defineProperty = Object.defineProperty; |
| + const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; |
| + const getOwnPropertyNames = Object.getOwnPropertyNames; |
| + const getOwnPropertySymbols = Object.getOwnPropertySymbols; |
| + const hasOwnProperty = Object.prototype.hasOwnProperty; |
| + const slice = [].slice; |
| function getOwnNamesAndSymbols(obj) { |
| return getOwnPropertyNames(obj).concat(getOwnPropertySymbols(obj)); |
| } |
| function dload(obj, field) { |
| - field = _canonicalFieldName(obj, field); |
| + field = _canonicalFieldName(obj, field, [], field); |
| if (_getMethodType(obj, field) !== void 0) { |
| return dart.bind(obj, field); |
| } |
| @@ -31,10 +33,8 @@ var dart, _js_helper, _js_primitives; |
| // See: https://github.com/dart-lang/dev_compiler/issues/169 |
| var result = obj[field]; |
| - // TODO(leafp): Decide whether to keep this for javascript |
| - // objects, or just use the javascript semantics. |
| - if (typeof result == "function" && |
| - !Object.prototype.hasOwnProperty.call(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); |
| } |
| @@ -43,7 +43,7 @@ var dart, _js_helper, _js_primitives; |
| dart.dload = dload; |
| function dput(obj, field, value) { |
| - field = _canonicalFieldName(obj, field); |
| + 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; |
| @@ -91,51 +91,46 @@ var dart, _js_helper, _js_primitives; |
| } |
| function dcall(f/*, ...args*/) { |
| - let args = Array.prototype.slice.call(arguments, 1); |
| + let args = slice.call(arguments, 1); |
| let ftype = _getFunctionType(f); |
| return checkAndCall(f, ftype, void 0, args, 'call'); |
| } |
| dart.dcall = dcall; |
| - // TODO(vsm): Automatically build this. |
| - // All dynamic methods should check for these. |
| - // See: https://github.com/dart-lang/dev_compiler/issues/142 |
| - var _extensionMethods = { |
| - // Lazy - as these symbols may not be loaded yet. |
| - // TODO(vsm): This should record / check the receiver type |
| - // as well. E.g., only look for core.$map if the receiver |
| - // is an Iterable. |
| - 'map': () => core.$map |
| - }; |
| - |
| - // TODO(leafp): Integrate this with the eventual proper extension |
| - // method system. |
| - function _canonicalFieldName(obj, name) { |
| - if (obj[name] === void 0) return _extensionMethods[name](); |
| + let _extensionType = Symbol('extensionType'); |
| + function _canonicalFieldName(obj, name, args, displayName) { |
| + if (obj[_extensionType]) { |
| + var extension = dartx[name]; |
| + if (extension) return extension; |
| + // 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. |
| + throwNoSuchMethod(obj, displayName, args); |
| + } |
| return name; |
| } |
| - function dsend(obj, method/*, ...args*/) { |
| - let args = Array.prototype.slice.call(arguments, 2); |
| - let symbol = _canonicalFieldName(obj, method); |
| + /** 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 = _getMethodType(obj, symbol); |
| - return checkAndCall(f, ftype, obj, args, method); |
| + let ftype = _getMethodType(obj, name); |
| + return checkAndCall(f, ftype, obj, args, displayName); |
| + } |
| + |
| + function dsend(obj, method/*, ...args*/) { |
| + return callMethod(obj, method, slice.call(arguments, 2)); |
| } |
| dart.dsend = dsend; |
| function dindex(obj, index) { |
| - // TODO(jmesserly): remove this special case once Array extensions are |
| - // hooked up. |
| - if (obj instanceof Array && realRuntimeType(index) == core.int) { |
| - return obj[index]; |
| - } |
| - return checkAndCall(obj.get, void 0, obj, [index], '[]'); |
| + return callMethod(obj, 'get', [index], '[]'); |
| } |
| dart.dindex = dindex; |
| function dsetindex(obj, index, value) { |
| - return checkAndCall(obj.set, void 0, obj, [index, value], '[]='); |
| + return callMethod(obj, 'set', [index, value], '[]='); |
| } |
| dart.dsetindex = dsetindex; |
| @@ -178,7 +173,7 @@ var dart, _js_helper, _js_primitives; |
| // TODO(vsm): How should we encode the runtime type? |
| - let _runtimeType = Symbol('_runtimeType'); |
| + const _runtimeType = Symbol('_runtimeType'); |
| function checkPrimitiveType(obj) { |
| switch (typeof obj) { |
| @@ -247,7 +242,7 @@ var dart, _js_helper, _js_primitives; |
| return t; |
| } |
| - let subtypeMap = new Map(); |
| + const subtypeMap = new Map(); |
| function isSubtype(t1, t2) { |
| // See if we already know the answer |
| // TODO(jmesserly): general purpose memoize function? |
| @@ -561,7 +556,7 @@ var dart, _js_helper, _js_primitives; |
| } else { |
| // We're passed the piecewise components of the function type, |
| // construct it. |
| - let args = Array.prototype.slice.call(arguments, 1); |
| + let args = slice.call(arguments, 1); |
| t = functionType.apply(null, args); |
| } |
| setRuntimeType(closure, t); |
| @@ -778,17 +773,59 @@ var dart, _js_helper, _js_primitives; |
| } |
| dart.copyProperties = copyProperties; |
| + function getExtensionSymbol(name) { |
| + let sym = dartx[name]; |
| + if (!sym) dartx[name] = sym = Symbol('dartx.' + name); |
| + return sym; |
| + } |
| + |
| /** |
| * Copy symbols from the prototype of the source to destination. |
| * These are the only properties safe to copy onto an existing public |
| * JavaScript class. |
| */ |
| - function registerExtension(to, from) { |
| - return copyPropertiesHelper(to.prototype, from.prototype, |
| - getOwnPropertySymbols(from.prototype)); |
| + function registerExtension(jsType, dartExtType) { |
| + let extProto = dartExtType.prototype; |
| + let jsProto = jsType.prototype; |
| + |
| + // Mark the JS type's instances so we can easily check for extensions. |
| + assert(jsProto[_extensionType] === void 0); |
| + jsProto[_extensionType] = extProto; |
| + for (let name of getOwnPropertyNames(extProto)) { |
| + let symbol = getExtensionSymbol(name); |
| + defineProperty(jsProto, symbol, getOwnPropertyDescriptor(extProto, name)); |
| + } |
| } |
| dart.registerExtension = registerExtension; |
| + /** |
| + * Mark a concrete type as implementing extension methods. |
| + * For example: `class MyIter implements Iterable`. |
| + * |
| + * This takes a list of names, which are the extension methods implemented. |
| + * It will add a forwarder, so the extension method name redirects to the |
| + * normal Dart method name. For example: |
| + * |
| + * defineExtensionMembers(MyType, ['add', 'remove']); |
| + * |
| + * Results in: |
| + * |
| + * MyType.prototype[dartx.add] = MyType.prototype.add; |
| + * MyType.prototype[dartx.remove] = MyType.prototype.remove; |
| + */ |
| + // TODO(jmesserly): essentially this gives two names to the same method. |
| + // This benefit is roughly equivalent call performance either way, but the |
| + // cost is we need to call implementExtension any time a subclass overrides |
| + // one of these methods. |
|
vsm
2015/06/04 21:50:02
I see. So, the virtual dispatch is happening on f
Jennifer Messerly
2015/06/04 21:57:46
exactly. If we wanted to do it the other way, we'd
|
| + function defineExtensionMembers(type, methodNames) { |
| + let proto = type.prototype; |
| + for (let name of methodNames) { |
| + let method = getOwnPropertyDescriptor(proto, name); |
| + defineProperty(proto, getExtensionSymbol(name), method); |
| + } |
| + } |
| + dart.defineExtensionMembers = defineExtensionMembers; |
| + |
| function setBaseClass(derived, base) { |
| // Link the extension to the type it's extending as a base class. |
| derived.prototype.__proto__ = base.prototype; |
| @@ -833,7 +870,7 @@ var dart, _js_helper, _js_primitives; |
| 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); |
| + let mixins = slice.call(arguments, 1); |
| // Create a class that will hold all of the mixin methods. |
| class Mixin extends base { |
| @@ -921,7 +958,7 @@ var dart, _js_helper, _js_primitives; |
| function defineNamedConstructor(clazz, name) { |
| let proto = clazz.prototype; |
| let initMethod = proto[name]; |
| - let ctor = function() { return initMethod.apply(this, arguments); } |
| + let ctor = function() { return initMethod.apply(this, arguments); }; |
| ctor.prototype = proto; |
| // Use defineProperty so we don't hit a property defined on Function, |
| // like `caller` and `arguments`. |
| @@ -948,7 +985,7 @@ var dart, _js_helper, _js_primitives; |
| if (arguments.length != length && arguments.length != 0) { |
| throw Error('requires ' + length + ' or 0 type arguments'); |
| } |
| - let args = Array.prototype.slice.call(arguments); |
| + let args = slice.call(arguments); |
| // TODO(leafp): This should really be core.Object for |
| // consistency, but Object is not attached to core |
| // until the entire core library has been processed, |
| @@ -1116,7 +1153,7 @@ var dart, _js_helper, _js_primitives; |
| } |
| /** The global constant table. */ |
| - let constants = new Map(); |
| + const constants = new Map(); |
| /** |
| * Canonicalize a constant object. |
| @@ -1127,11 +1164,17 @@ var dart, _js_helper, _js_primitives; |
| */ |
| function constant(obj) { |
| let objectKey = [realRuntimeType(obj)]; |
| - // 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 them, but easier to not depend on that. |
| + // 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)) { |
| - // TODO(jmesserly): we can make this faster if needed. |
| objectKey.push(name); |
| objectKey.push(obj[name]); |
| } |
| @@ -1152,6 +1195,12 @@ var dart, _js_helper, _js_primitives; |
| } |
| dart.setType = setType; |
| + /** Sets the element type of a list literal. */ |
| + function list(obj, elementType) { |
| + return setType(obj, _interceptors.JSArray$(elementType)); |
| + } |
| + dart.list = list; |
| + |
| /** Sets the internal runtime type of `obj` to be `type` */ |
| function setRuntimeType(obj, type) { |
| obj[_runtimeType] = type; |
| @@ -1258,44 +1307,13 @@ var dart, _js_helper, _js_primitives; |
| _js_primitives = _js_primitives || {}; |
| _js_primitives.printString = (s) => console.log(s); |
| - // TODO(vsm): Plumb this correctly. |
| - // See: https://github.com/dart-lang/dev_compiler/issues/40 |
| - String.prototype.contains = function(sub) { return this.indexOf(sub) >= 0; } |
| - let _split = String.prototype.split; |
| - String.prototype.split = function() { |
| - let result = _split.apply(this, arguments); |
| - dart.setType(result, core.List$(core.String)); |
| - return result; |
| - }; |
| - String.prototype.get = function(i) { |
| - return this[i]; |
| - }; |
| - String.prototype.codeUnitAt = function(i) { |
| - return this.charCodeAt(i); |
| - }; |
| - String.prototype.replaceAllMapped = function(from, cb) { |
| - return this.replace(from.multiple, function() { |
| - // Remove offset & string from the result array |
| - var matches = arguments; |
| - matches.splice(-2, 2); |
| - // The callback receives match, p1, ..., pn |
| - return cb(matches); |
| - }); |
| - }; |
| - String.prototype['+'] = function(arg) { return this.valueOf() + arg; }; |
| - |
| - Boolean.prototype['!'] = function() { return !this.valueOf(); }; |
| - Boolean.prototype['&&'] = function(arg) { return this.valueOf() && arg; }; |
| - Boolean.prototype['||'] = function(arg) { return this.valueOf() || arg; }; |
| - Number.prototype['<'] = function(arg) { return this.valueOf() < arg; }; |
| - Number.prototype['<='] = function(arg) { return this.valueOf() <= arg; }; |
| - Number.prototype['>'] = function(arg) { return this.valueOf() > arg; }; |
| - Number.prototype['+'] = function(arg) { return this.valueOf() + arg; }; |
| - |
| // TODO(vsm): DOM facades? |
| // See: https://github.com/dart-lang/dev_compiler/issues/173 |
| NodeList.prototype.get = function(i) { return this[i]; }; |
| NamedNodeMap.prototype.get = function(i) { return this[i]; }; |
| DOMTokenList.prototype.get = function(i) { return this[i]; }; |
| + /** Dart extension members. */ |
| + dartx = dartx || {}; |
| + |
| })(dart || (dart = {})); |