Index: lib/runtime/_types.js |
diff --git a/lib/runtime/_types.js b/lib/runtime/_types.js |
index e8dc2d0c38c034a33867abc9f5b2719181fa7fb5..af5e2d04d24fce2fe1fdf0f7c351b3816aaed66a 100644 |
--- a/lib/runtime/_types.js |
+++ b/lib/runtime/_types.js |
@@ -19,6 +19,32 @@ dart_library.library('dart_runtime/_types', null, /* Imports */[ |
const copyProperties = dart_utils.copyProperties; |
const safeGetOwnProperty = dart_utils.safeGetOwnProperty; |
+ /** |
+ * Types in dart are represented at runtime as follows. |
+ * - Normal nominal types, produced from classes, are represented |
+ * at runtime by the JS class of which they are an instance. |
+ * If the type is the result of instantiating a generic class, |
+ * then the "classes" module manages the association between the |
+ * instantiated class and the original class declaration |
+ * and the type arguments with which it was instantiated. This |
+ * assocation can be queried via the "classes" module". |
+ * |
+ * - All other types are represented as instances of class TypeRep, |
+ * defined in this module. |
+ * - Dynamic, Void, and Bottom are singleton instances of sentinal |
+ * classes. |
+ * - Function types are instances of subclasses of AbstractFunctionType. |
+ * |
+ * Function types are represented in one of two ways: |
+ * - As an instance of FunctionType. These are eagerly computed. |
+ * - As an instance of TypeDef. The TypeDef representation lazily |
+ * computes an instance of FunctionType, and delegates to that instance. |
+ * |
+ * All types satisfy the following interface: |
+ * get String name; |
+ * String toString(); |
+ * |
+ */ |
class TypeRep extends rtti.LazyTagged(() => core.Type) { |
get name() {return this.toString();} |
} |
@@ -90,12 +116,51 @@ dart_library.library('dart_runtime/_types', null, /* Imports */[ |
} |
class FunctionType extends AbstractFunctionType { |
- constructor(returnType, args, optionals, named) { |
+ /** |
+ * Construct a function type. There are two arrow constructors, |
+ * distinguished by the "definite" flag. |
+ * |
+ * The fuzzy arrow (definite is false) treats any arguments |
+ * of type dynamic as having type bottom, and will always be |
+ * called with a dynamic invoke. |
+ * |
+ * The definite arrow (definite is true) leaves arguments unchanged. |
+ * |
+ * We eagerly canonize the argument types to avoid having to deal with |
+ * this logic in multiple places. |
+ * |
+ * TODO(leafp): Figure out how to present this to the user. How |
+ * should these be printed out? |
+ */ |
+ constructor(definite, returnType, args, optionals, named) { |
super(); |
+ this.definite = definite; |
this.returnType = returnType; |
this.args = args; |
this.optionals = optionals; |
this.named = named; |
+ this._canonize(); |
+ } |
+ _canonize() { |
+ if (this.definite) return; |
+ |
+ function replace(a) { |
+ return (a == dynamicR) ? bottomR : a; |
+ } |
+ |
+ this.args = this.args.map(replace); |
+ |
+ if (this.optionals.length > 0) { |
+ this.optionals = this.optionals.map(replace); |
+ } |
+ |
+ if (Object.keys(this.named).length > 0) { |
+ let r = {}; |
+ for (let name of getOwnPropertyNames(this.named)) { |
+ r[name] = replace(this.named[name]); |
+ } |
+ this.named = r; |
+ } |
} |
} |
@@ -107,6 +172,10 @@ dart_library.library('dart_runtime/_types', null, /* Imports */[ |
this._functionType = null; |
} |
+ get definite() { |
+ return this._functionType.definite; |
+ } |
+ |
get name() { |
return this._name; |
} |
@@ -135,7 +204,7 @@ dart_library.library('dart_runtime/_types', null, /* Imports */[ |
} |
} |
- function functionType(returnType, args, extra) { |
+ function _functionType(definite, returnType, args, extra) { |
// TODO(vsm): Cache / memomize? |
let optionals; |
let named; |
@@ -149,10 +218,27 @@ dart_library.library('dart_runtime/_types', null, /* Imports */[ |
optionals = []; |
named = extra; |
} |
- return new FunctionType(returnType, args, optionals, named); |
+ return new FunctionType(definite, returnType, args, optionals, named); |
+ } |
+ |
+ /** |
+ * Create a "fuzzy" function type. If any arguments are dynamic |
+ * they will be replaced with bottom. |
+ */ |
+ function functionType(returnType, args, extra) { |
+ return _functionType(false, returnType, args, extra); |
} |
exports.functionType = functionType; |
+ /** |
+ * Create a definite function type. No substitution of dynamic for |
+ * bottom occurs. |
+ */ |
+ function definiteFunctionType(returnType, args, extra) { |
+ return _functionType(true, returnType, args, extra); |
+ } |
+ exports.definiteFunctionType = definiteFunctionType; |
+ |
function typedef(name, closure) { |
return new Typedef(name, closure); |
} |
@@ -218,7 +304,7 @@ dart_library.library('dart_runtime/_types', null, /* Imports */[ |
} |
for (let i = 0; i < args1.length; ++i) { |
- if (!isSubtype_(args2[i], args1[i], true)) { |
+ if (!isSubtype_(args2[i], args1[i])) { |
return false; |
} |
} |
@@ -232,13 +318,13 @@ dart_library.library('dart_runtime/_types', null, /* Imports */[ |
let j = 0; |
for (let i = args1.length; i < args2.length; ++i, ++j) { |
- if (!isSubtype_(args2[i], optionals1[j], true)) { |
+ if (!isSubtype_(args2[i], optionals1[j])) { |
return false; |
} |
} |
for (let i = 0; i < optionals2.length; ++i, ++j) { |
- if (!isSubtype_(optionals2[i], optionals1[j], true)) { |
+ if (!isSubtype_(optionals2[i], optionals1[j])) { |
return false; |
} |
} |
@@ -254,7 +340,7 @@ dart_library.library('dart_runtime/_types', null, /* Imports */[ |
if (n1 === void 0) { |
return false; |
} |
- if (!isSubtype_(n2, n1, true)) { |
+ if (!isSubtype_(n2, n1)) { |
return false; |
} |
} |
@@ -298,33 +384,26 @@ dart_library.library('dart_runtime/_types', null, /* Imports */[ |
} |
exports.isSubtype = isSubtype; |
- function _isBottom(type, dynamicIsBottom) { |
- return (type == dynamicR && dynamicIsBottom) || type == bottomR; |
+ function _isBottom(type) { |
+ return type == bottomR; |
} |
- function _isTop(type, dynamicIsBottom) { |
- return type == core.Object || (type == dynamicR && !dynamicIsBottom); |
+ function _isTop(type) { |
+ return type == core.Object || (type == dynamicR); |
} |
- function isSubtype_(t1, t2, opt_dynamicIsBottom) { |
- let dynamicIsBottom = |
- opt_dynamicIsBottom === void 0 ? false : opt_dynamicIsBottom; |
- |
+ function isSubtype_(t1, t2) { |
t1 = canonicalType(t1); |
t2 = canonicalType(t2); |
if (t1 == t2) return true; |
- // In Dart, dynamic is effectively both top and bottom. |
- // Here, we treat dynamic as one or the other depending on context, |
- // but not both. |
- |
// Trivially true. |
- if (_isTop(t2, dynamicIsBottom) || _isBottom(t1, dynamicIsBottom)) { |
+ if (_isTop(t2) || _isBottom(t1)) { |
return true; |
} |
// Trivially false. |
- if (_isTop(t1, dynamicIsBottom) || _isBottom(t2, dynamicIsBottom)) { |
+ if (_isTop(t1) || _isBottom(t2)) { |
return false; |
} |
@@ -413,16 +492,16 @@ dart_library.library('dart_runtime/_types', null, /* Imports */[ |
// TODO(vsm): Cache this if we start using it at runtime. |
if (type instanceof AbstractFunctionType) { |
- if (!_isTop(type.returnType, false)) return false; |
+ if (!_isTop(type.returnType)) return false; |
for (let i = 0; i < type.args.length; ++i) { |
- if (!_isBottom(type.args[i], true)) return false; |
+ if (!_isBottom(type.args[i])) return false; |
} |
for (let i = 0; i < type.optionals.length; ++i) { |
- if (!_isBottom(type.optionals[i], true)) return false; |
+ if (!_isBottom(type.optionals[i])) return false; |
} |
let names = getOwnPropertyNames(type.named); |
for (let i = 0; i < names.length; ++i) { |
- if (!_isBottom(type.named[names[i]], true)) return false; |
+ if (!_isBottom(type.named[names[i]])) return false; |
} |
return true; |
} |