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

Unified Diff: lib/runtime/dart_runtime.js

Issue 1138793002: Tag closures with their types (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: More comment fixes 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
« no previous file with comments | « lib/runtime/dart/typed_data.js ('k') | lib/src/codegen/js_codegen.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/runtime/dart_runtime.js
diff --git a/lib/runtime/dart_runtime.js b/lib/runtime/dart_runtime.js
index 079a812356f8ef3ec04577f42919ff4c5097c362..9343f9fec3cfb886e480a9396409886243be435b 100644
--- a/lib/runtime/dart_runtime.js
+++ b/lib/runtime/dart_runtime.js
@@ -42,13 +42,18 @@ var dart, _js_helper, _js_primitives;
}
function dload(obj, field) {
+ field = _canonicalFieldName(obj, field);
+ if (_getMethodType(obj, field) !== void 0) {
+ return dart.tearoff(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
var result = obj[field];
- // TODO(vsm): Check this more robustly.
+ // 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)) {
// This appears to be a method tearoff. Bind this.
@@ -59,6 +64,7 @@ var dart, _js_helper, _js_primitives;
dart.dload = dload;
function dput(obj, field, value) {
+ field = _canonicalFieldName(obj, field);
// TODO(vsm): Implement NSM and type checks.
// See: https://github.com/dart-lang/dev_compiler/issues/170
obj[field] = value;
@@ -71,34 +77,38 @@ var dart, _js_helper, _js_primitives;
throw new core.NoSuchMethodError(obj, name, args);
}
- function checkAndCall(f, obj, args, name) {
+ 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) f = f.call;
+ if (f !== null) {
+ ftype = _getMethodType(f, 'call');
+ f = f.call;
+ }
if (!(f instanceof Function)) {
throwNoSuchMethod(obj, name, args);
}
}
- // TODO(jmesserly): enable this when we can fix => and methods.
- /*
- let formals = formalParameterList(f);
- // TODO(vsm): Type check args! We need to encode sufficient type info on f.
- if (formals.length < args.length) {
- throwNoSuchMethod(obj, name, args, f);
- } else if (formals.length > args.length) {
- for (let i = args.length; i < formals.length; ++i) {
- if (formals[i].indexOf("opt$") != 0) {
- throwNoSuchMethod(obj, name, args, f);
- }
- }
+ // 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 = _getFunctionType(f);
}
- */
- return f.apply(obj, args);
+
+ // TODO(leafp): Allow JS objects to go through?
+ if (!ftype) throw "Unable to find a type for applicand";
+
+ if (ftype.checkApply(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 = Array.prototype.slice.call(arguments, 1);
- return checkAndCall(f, void 0, args, 'call');
+ let ftype = _getFunctionType(f);
+ return checkAndCall(f, ftype, void 0, args, 'call');
}
dart.dcall = dcall;
@@ -113,14 +123,19 @@ var dart, _js_helper, _js_primitives;
'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]();
+ return name;
+ }
+
function dsend(obj, method/*, ...args*/) {
let args = Array.prototype.slice.call(arguments, 2);
- var f = obj[method];
- if (f === void 0) {
- var symbol = _extensionMethods[method]();
- f = obj[symbol];
- }
- return checkAndCall(f, obj, args, method);
+ let symbol = _canonicalFieldName(obj, method);
+ let f = obj[symbol];
+ let ftype = _getMethodType(obj, symbol);
+ return checkAndCall(f, ftype, obj, args, method);
}
dart.dsend = dsend;
@@ -444,6 +459,7 @@ var dart, _js_helper, _js_primitives;
dart.notNull = notNull;
function _typeName(type) {
+ if (type === void 0) throw "Undefined type";
var name = type.name;
if (!name) throw 'Unexpected type: ' + type;
return name;
@@ -454,6 +470,46 @@ var dart, _js_helper, _js_primitives;
this._stringValue = null;
}
+ /// Check that a function of this type can be applied to
+ /// actuals.
+ checkApply(actuals) {
+ if (actuals.length < this.args.length) return false;
+ var index = 0;
+ for(let i = 0; i < this.args.length; ++i) {
+ let t = realRuntimeType(actuals[i]);
+ if (!isSubtype(t, this.args[i])) return false;
+ ++index;
+ }
+ if (actuals.length == this.args.length) return true;
+ let extras = actuals.length - this.args.length;
+ if (this.optionals.length > 0) {
+ if (extras > this.optionals.length) return false;
+ for(let i = 0; i < extras; ++i) {
+ let t = realRuntimeType(actuals[index + i]);
+ if (!isSubtype(t, this.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(this.named).length == 0) return false;
+ let opts = actuals[index];
+ let names = getOwnPropertyNames(opts);
+ // This is something other than a map
+ if (names.length == 0) return false;
+ for (name of names) {
+ if (!(Object.prototype.hasOwnProperty.call(this.named, name))) {
+ return false;
+ }
+ let t = realRuntimeType(opts[name]);
+ if (!isSubtype(t, this.named[name])) return false;
+ }
+ return true;
+ }
+
get name() {
if (this._stringValue) return this._stringValue;
@@ -474,7 +530,7 @@ var dart, _js_helper, _js_primitives;
buffer += _typeName(this.optionals[i]);
}
buffer += ']';
- } else if (this.named.length > 0) {
+ } else if (Object.keys(this.named).length > 0) {
if (this.args.length > 0) buffer += ', ';
buffer += '{';
let names = getOwnPropertyNames(this.named).sort();
@@ -503,6 +559,36 @@ var dart, _js_helper, _js_primitives;
}
}
+ /// Tag a closure with a type, using one of three forms:
+ /// dart.fn(cls) marks cls has having no optional or named
+ /// parameters, with all argument and return types as dynamic
+ /// dart.fn(cls, func) marks cls with the lazily computed
+ /// runtime type as computed by func()
+ /// dart.fn(cls, rType, argsT, extras) marks cls as having the
+ /// runtime type dart.functionType(rType, argsT, extras)
+ function fn(closure/* ...args*/) {
+ // Closure and a lazy type constructor
+ if (arguments.length == 2) {
+ defineLazyProperty(closure, _runtimeType, {get : arguments[1]});
+ return closure;
+ }
+ var t;
+ if (arguments.length == 1) {
+ // No type arguments, it's all dynamic
+ let len = closure.length;
+ let args = Array.apply(null, new Array(len)).map(() => dart.dynamic);
+ t = functionType(dart.dynamic, args);
+ } else {
+ // We're passed the piecewise components of the function type,
+ // construct it.
+ let args = Array.prototype.slice.call(arguments, 1);
+ t = functionType.apply(functionType, args);
+ }
+ setRuntimeType(closure, t);
+ return closure;
+ }
+ dart.fn = fn;
+
function functionType(returnType, args, extra) {
// TODO(vsm): Cache / memomize?
var optionals;
@@ -685,6 +771,17 @@ var dart, _js_helper, _js_primitives;
dart.defineLazyProperties = defineLazy;
dart.defineLazyClassGeneric = defineLazyProperty;
+ function defineMemoizedGetter(obj, name, get) {
+ let cache = null;
+ function getter() {
+ if (cache != null) return cache;
+ cache = get();
+ get = null;
+ return cache;
+ }
+ defineProperty(obj, name, {get : getter});
+ }
+
function copyPropertiesHelper(to, from, names) {
for (let name of names) {
defineProperty(to, name, getOwnPropertyDescriptor(from, name));
@@ -778,6 +875,19 @@ var dart, _js_helper, _js_primitives;
for (let m of mixins) {
copyProperties(Mixin.prototype, m.prototype);
}
+
+ // Set the signature of the Mixin class to be the composition
+ // of the signatures of the mixins.
+ dart.setSignature(Mixin, {
+ methods : () => {
+ let s = {};
+ for (let m of mixins) {
+ copyProperties(s, m[_methodSig]);
+ }
+ return s;
+ }
+ });
+
// Save mixins for reflection
Mixin[dart.mixins] = mixins;
return Mixin;
@@ -889,6 +999,82 @@ var dart, _js_helper, _js_primitives;
}
dart.generic = generic;
+ let _methodSig = Symbol("sig");
+ let _staticSig = Symbol("sigStatic");
+
+ /// Get the type of a function using the store runtime type
+ function _getFunctionType(f) {
+ return f[_runtimeType];
+ }
+
+ /// Get the type of a method using the stored signature
+ function _getMethodType(obj, name) {
+ if (obj === void 0) return void 0;
+ if (obj == null) return void 0;
+ let sigObj = obj.__proto__.constructor[_methodSig];
+ if (sigObj === void 0) return void 0;
+ let sig = sigObj[name];
+ return sig;
+ }
+
+ /// Given an object and a method name, tear off the method.
+ /// Sets the runtime type of the torn off method appropriately,
+ /// and also binds the object.
+ /// TODO(leafp): Consider caching the tearoff on the object?
+ function tearoff(obj, name) {
+ let f = obj[name].bind(obj);
+ let sig = _getMethodType(obj, name)
+ assert(sig);
+ setRuntimeType(f, sig);
+ return f;
+ }
+ dart.tearoff = tearoff;
+
+ // Set up the method signature field on the constructor
+ function _setMethodSignature(f, sigF) {
+ defineMemoizedGetter(f, _methodSig, () => {
+ let sigObj = sigF();
+ sigObj.__proto__ = f.__proto__[_methodSig];
+ return sigObj;
+ });
+ }
+
+ // Set up the static signature field on the constructor
+ function _setStaticSignature(f, sigF) {
+ defineMemoizedGetter(f, _staticSig, sigF);
+ }
+
+ // Set the lazily computed runtime type field on static methods
+ function _setStaticTypes(f, names) {
+ for (let name of names) {
+ function getT() { return f[_staticSig][name];};
+ defineProperty(f[name], _runtimeType, {get : getT});
+ }
+ }
+
+ /// Set up the type signature of a class (constructor object)
+ /// f is a constructor object
+ /// signature is an object containing optional properties as follows:
+ /// methods: A function returning an object mapping method names
+ /// to method types. The function is evaluated lazily and cached.
+ /// statics: A function returning an object mapping static method
+ /// names to types. The function is evalutated lazily and cached.
+ /// names: An array of the names of the static methods. Used to
+ /// permit eagerly setting the runtimeType field on the methods
+ /// while still lazily computing the type descriptor object.
+ function setSignature(f, signature) {
+ let methods =
+ ('methods' in signature) ? signature.methods : () => ({});
+ let statics =
+ ('statics' in signature) ? signature.statics : () => ({});
+ let names =
+ ('names' in signature) ? signature.names : [];
+ _setMethodSignature(f, methods);
+ _setStaticSignature(f, statics);
+ _setStaticTypes(f, names);
+ };
+ dart.setSignature = setSignature;
+
let _value = Symbol('_value');
/**
* Looks up a sequence of [keys] in [map], recursively, and
@@ -1026,9 +1212,9 @@ var dart, _js_helper, _js_primitives;
// TODO(jmesserly): right now this is a sentinel. It should be a type object
// of some sort, assuming we keep around `dynamic` at runtime.
- dart.dynamic = { toString() { return 'dynamic'; } };
- dart.void = { toString() { return 'void'; } };
- dart.bottom = { toString() { return 'bottom'; } };
+ dart.dynamic = { toString() { return 'dynamic'; }, get name() {return toString();}};
+ dart.void = { toString() { return 'void'; }, get name() {return toString();}};
+ dart.bottom = { toString() { return 'bottom'; }, get name() {return toString();}};
dart.global = window || global;
dart.JsSymbol = Symbol;
« no previous file with comments | « lib/runtime/dart/typed_data.js ('k') | lib/src/codegen/js_codegen.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698