Index: lib/runtime/dart_runtime.js |
diff --git a/lib/runtime/dart_runtime.js b/lib/runtime/dart_runtime.js |
index 5761a8e897ef533aa8424e37297e0b2fbffbfb5e..875a1472ce30e2f50cc4fc4c2122487daca74244 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,38 +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)); |
} |
- // 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 arg of r) { |
- arg.replace(FN_ARG, function(all, underscore, name){ |
- args.push(name); |
- }); |
- } |
- return args; |
- } |
- |
function dload(obj, field) { |
- field = _canonicalFieldName(obj, field); |
+ field = _canonicalFieldName(obj, field, []); |
if (_getMethodType(obj, field) !== void 0) { |
return dart.bind(obj, field); |
} |
@@ -52,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); |
} |
@@ -64,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]); |
// TODO(vsm): Implement NSM and type checks. |
// See: https://github.com/dart-lang/dev_compiler/issues/170 |
obj[field] = value; |
@@ -112,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); |
vsm
2015/06/02 17:59:10
displayName isn't passed in by dload / dput above.
Jennifer Messerly
2015/06/04 21:31:18
Done.
|
+ } |
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, displayName, args) { |
Leaf
2015/06/02 21:35:04
Slightly confusing that callMethod and _canonicalF
Jennifer Messerly
2015/06/04 21:31:18
Done.
|
+ 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, 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; |
@@ -199,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) { |
@@ -268,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? |
@@ -572,17 +546,17 @@ var dart, _js_helper, _js_primitives; |
if (arguments.length == 1) { |
// No type arguments, it's all dynamic |
let len = closure.length; |
- function build() { |
+ let build = () => { |
let args = Array.apply(null, new Array(len)).map(() => core.Object); |
return functionType(core.Object, args); |
- } |
+ }; |
// We could be called before Object is defined. |
if (core.Object === void 0) return fn(closure, build); |
t = build(); |
} 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); |
@@ -799,17 +773,53 @@ 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. |
+ jsProto[_extensionType] = extProto; |
vsm
2015/06/02 17:59:10
assert that this isn't already defined?
Jennifer Messerly
2015/06/04 21:31:18
Done.
|
+ 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`. |
+ * |
+ * Because it's implementing `Iterable` for the first time, it needs to |
+ * define the extension method symbols and have them call the normal Dart |
+ * method with the corresponding name. |
+ * |
+ * Unfortunately we have no way of knowing what interface members are, so |
+ * we just forward everything. These calls are only generated to static types |
+ * like `List list;` so it won't confuse dynamic dispatch. |
+ */ |
+ function implementExtension(type) { |
vsm
2015/06/02 17:59:10
Hmm, you're calling this everywhere with a second
Jennifer Messerly
2015/06/02 18:16:04
Sigh, I wish we could. The doc comment above expla
vsm
2015/06/03 15:48:39
General thoughts:
(1) it's worth declaring the se
Jennifer Messerly
2015/06/04 21:31:18
Update: redesigned this. We now emit extension met
|
+ let proto = type.prototype; |
+ for (let name of getOwnPropertyNames(proto)) { |
+ // TODO(jmesserly): skip named ctors as well |
+ if (name == type.name || name == 'constructor') continue; |
+ let symbol = getExtensionSymbol(name); |
+ defineProperty(proto, symbol, getOwnPropertyDescriptor(proto, name)); |
Jennifer Messerly
2015/06/02 23:43:42
Oops. Just noticed, this needs to generate a stub
vsm
2015/06/03 15:48:40
Good catch. :-)
Jennifer Messerly
2015/06/04 21:31:18
fixed now.
|
+ } |
+ } |
+ dart.implementExtension = implementExtension; |
+ |
function setBaseClass(derived, base) { |
// Link the extension to the type it's extending as a base class. |
derived.prototype.__proto__ = base.prototype; |
@@ -854,7 +864,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 { |
@@ -942,7 +952,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`. |
@@ -969,7 +979,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, |
@@ -1021,8 +1031,7 @@ var dart, _js_helper, _js_primitives; |
if (sigObj === void 0) return void 0; |
let parts = sigObj[name]; |
if (parts === void 0) return void 0; |
- let sig = functionType.apply(null, parts); |
- return sig; |
+ return functionType.apply(null, parts); |
} |
/// Get the type of a constructor from a class using the stored signature |
@@ -1036,8 +1045,7 @@ var dart, _js_helper, _js_primitives; |
if (sigCtor === void 0) return void 0; |
let parts = sigCtor[name]; |
if (parts === void 0) return void 0; |
- let sig = functionType.apply(null, parts); |
- return sig; |
+ return functionType.apply(null, parts); |
} |
dart.classGetConstructorType = _getConstructorType; |
@@ -1047,7 +1055,7 @@ var dart, _js_helper, _js_primitives; |
/// TODO(leafp): Consider caching the tearoff on the object? |
function bind(obj, name) { |
let f = obj[name].bind(obj); |
- let sig = _getMethodType(obj, name) |
+ let sig = _getMethodType(obj, name); |
assert(sig); |
setRuntimeType(f, sig); |
return f; |
@@ -1076,11 +1084,10 @@ var dart, _js_helper, _js_primitives; |
// Set the lazily computed runtime type field on static methods |
function _setStaticTypes(f, names) { |
for (let name of names) { |
- function getT() { |
+ defineProperty(f[name], _runtimeType, { get: function() { |
let parts = f[_staticSig][name]; |
return functionType.apply(null, parts); |
- }; |
- defineProperty(f[name], _runtimeType, {get : getT}); |
+ }}); |
} |
} |
@@ -1107,7 +1114,7 @@ var dart, _js_helper, _js_primitives; |
_setMethodSignature(f, methods); |
_setStaticSignature(f, statics); |
_setStaticTypes(f, names); |
- }; |
+ } |
dart.setSignature = setSignature; |
let _value = Symbol('_value'); |
@@ -1140,7 +1147,7 @@ var dart, _js_helper, _js_primitives; |
} |
/** The global constant table. */ |
- let constants = new Map(); |
+ const constants = new Map(); |
/** |
* Canonicalize a constant object. |
@@ -1151,11 +1158,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]); |
} |
@@ -1176,6 +1189,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; |
@@ -1282,44 +1301,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 = {})); |