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

Unified Diff: lib/runtime/dart_runtime.js

Issue 1153003003: fixes #40, extension methods for primitive types (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 years, 7 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/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 = {}));

Powered by Google App Engine
This is Rietveld 408576698