Skip to content

Commit

Permalink
Handle dynamic as bottom inside of function type reps.
Browse files Browse the repository at this point in the history
This changes the way we handle dynamic at runtime to be more correct and cleaner.  We now simply emit dynamic as dynamic instead of as core.Object.  There are now two ways to construct a function type: one can construct a fuzzy function type (the default), or a definite function type.  The constructor for a fuzzy function type replaces all uses of dynamic with bottom.  This function type is used for all type annotations.  Definite function types do not replace dynamic with bottom.  These only occur as the runtime type of actual functions, for which we really know the type. Because we now eagerly sort this out when we create the function type, the subtyping code doesn't need to deal with this.

This allows some additional subtyping: closures which actually are typed to take dynamic would previously not have been allowed to be cast to something with a concrete argument type.  Now this is allowed (see the change in runtime_tests.js for an example of this).

This fixes #107.

BUG=
R=vsm@google.com

Review URL: https://codereview.chromium.org/1195523002
  • Loading branch information
leafpetersen committed Jun 17, 2015
1 parent 8d76f5f commit 4e7920c
Show file tree
Hide file tree
Showing 32 changed files with 859 additions and 765 deletions.
10 changes: 3 additions & 7 deletions pkg/dev_compiler/lib/runtime/_classes.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,6 @@ dart_library.library('dart_runtime/_classes', null, /* Imports */[
throwError('requires ' + length + ' or 0 type 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,
// which is too late.
while (args.length < length) args.push(types.dynamic);

let value = resultMap;
Expand Down Expand Up @@ -175,7 +171,7 @@ dart_library.library('dart_runtime/_classes', null, /* Imports */[
if (sigObj === void 0) return void 0;
let parts = sigObj[name];
if (parts === void 0) return void 0;
return types.functionType.apply(null, parts);
return types.definiteFunctionType.apply(null, parts);
}

/// Get the type of a constructor from a class using the stored signature
Expand All @@ -189,7 +185,7 @@ dart_library.library('dart_runtime/_classes', null, /* Imports */[
if (sigCtor === void 0) return void 0;
let parts = sigCtor[name];
if (parts === void 0) return void 0;
return types.functionType.apply(null, parts);
return types.definiteFunctionType.apply(null, parts);
}
exports.classGetConstructorType = _getConstructorType;

Expand Down Expand Up @@ -230,7 +226,7 @@ dart_library.library('dart_runtime/_classes', null, /* Imports */[
for (let name of names) {
rtti.tagMemoized(f[name], function() {
let parts = f[_staticSig][name];
return types.functionType.apply(null, parts);
return types.definiteFunctionType.apply(null, parts);
})
}
}
Expand Down
51 changes: 41 additions & 10 deletions pkg/dev_compiler/lib/runtime/_rtti.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,38 @@ dart_library.library('dart_runtime/_rtti', null, /* Imports */[

const slice = [].slice;

/**
* Runtime type information. This module defines the mapping from
* runtime objects to their runtime type information. See the types
* module for the definition of how type information is represented.
*
* Runtime objects fall into four main categories:
*
* - Things represented by javascript primitives, such as
* null, numbers, booleans, strings, and symbols. For these
* we map directly from the javascript type (given by typeof)
* to the appropriate class type from core, which serves as their
* rtti.
*
* - Functions, which are represented by javascript functions.
* Representations of Dart functions always have a
* _runtimeType property attached to them with the appropriate
* rtti.
*
* - Objects (instances) which are represented by instances of
* javascript (ES6) classes. Their types are given by their
* classes, and the rtti is accessed by projecting out their
* constructor field.
*
* - Types objects, which are represented as described in the types
* module. Types always have a _runtimeType property attached to
* them with the appropriate rtti. The rtti for these is always
* core.Type. TODO(leafp): consider the possibility that we can
* reliably recognize type objects and map directly to core.Type
* rather than attaching this property everywhere.
*
*/

/**
*Tag a closure with a type, using one of three forms:
* dart.fn(cls) marks cls has having no optional or named
Expand All @@ -27,6 +59,9 @@ dart_library.library('dart_runtime/_rtti', null, /* Imports */[
* runtime type as computed by func()
* dart.fn(cls, rType, argsT, extras) marks cls as having the
* runtime type dart.functionType(rType, argsT, extras)
*
* Note that since we are producing a type for a concrete function,
* it is sound to use the definite arrow type.
*/
function fn(closure/* ...args*/) {
// Closure and a lazy type constructor
Expand All @@ -38,18 +73,13 @@ dart_library.library('dart_runtime/_rtti', null, /* Imports */[
if (arguments.length == 1) {
// No type arguments, it's all dynamic
let len = closure.length;
let build = () => {
let args = Array.apply(null, new Array(len)).map(() => core.Object);
return types.functionType(core.Object, args);
};
// We could be called before Object is defined.
if (core.Object === void 0) return fn(closure, build);
t = build();
let args = Array.apply(null, new Array(len)).map(() => types.dynamic);
t = types.definiteFunctionType(types.dynamic, args);
} else {
// We're passed the piecewise components of the function type,
// construct it.
let args = slice.call(arguments, 1);
t = types.functionType.apply(null, args);
t = types.definiteFunctionType.apply(null, args);
}
tag(closure, t);
return closure;
Expand Down Expand Up @@ -87,8 +117,9 @@ dart_library.library('dart_runtime/_rtti', null, /* Imports */[

function getFunctionType(obj) {
// TODO(vsm): Encode this properly on the function for Dart-generated code.
let args = Array.apply(null, new Array(obj.length)).map(() => core.Object);
return types.functionType(types.bottom, args);
let args =
Array.apply(null, new Array(obj.length)).map(() => types.dynamic);
return types.definiteFunctionType(types.bottom, args);
}

/**
Expand Down
129 changes: 104 additions & 25 deletions pkg/dev_compiler/lib/runtime/_types.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();}
}
Expand Down Expand Up @@ -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;
}
}
}

Expand All @@ -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;
}
Expand Down Expand Up @@ -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;
Expand All @@ -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);
}
Expand Down Expand Up @@ -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;
}
}
Expand All @@ -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;
}
}
Expand All @@ -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;
}
}
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}
Expand Down
16 changes: 8 additions & 8 deletions pkg/dev_compiler/lib/runtime/dart/_foreign_helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,23 @@ dart_library.library('dart/_foreign_helper', null, /* Imports */[
if (arg11 === void 0)
arg11 = null;
}
dart.fn(JS, core.Object, [core.String, core.String], [core.Object, core.Object, core.Object, core.Object, core.Object, core.Object, core.Object, core.Object, core.Object, core.Object, core.Object, core.Object]);
dart.fn(JS, dart.dynamic, [core.String, core.String], [dart.dynamic, dart.dynamic, dart.dynamic, dart.dynamic, dart.dynamic, dart.dynamic, dart.dynamic, dart.dynamic, dart.dynamic, dart.dynamic, dart.dynamic, dart.dynamic]);
function JS_CURRENT_ISOLATE_CONTEXT() {
}
dart.fn(JS_CURRENT_ISOLATE_CONTEXT, () => dart.functionType(IsolateContext, []));
dart.fn(JS_CURRENT_ISOLATE_CONTEXT, () => dart.definiteFunctionType(IsolateContext, []));
class IsolateContext extends core.Object {}
function JS_CALL_IN_ISOLATE(isolate, func) {
}
dart.fn(JS_CALL_IN_ISOLATE, core.Object, [core.Object, core.Function]);
dart.fn(JS_CALL_IN_ISOLATE, dart.dynamic, [dart.dynamic, core.Function]);
function DART_CLOSURE_TO_JS(func) {
}
dart.fn(DART_CLOSURE_TO_JS, core.Object, [core.Function]);
dart.fn(DART_CLOSURE_TO_JS, dart.dynamic, [core.Function]);
function RAW_DART_FUNCTION_REF(func) {
}
dart.fn(RAW_DART_FUNCTION_REF, core.Object, [core.Function]);
dart.fn(RAW_DART_FUNCTION_REF, dart.dynamic, [core.Function]);
function JS_SET_CURRENT_ISOLATE(isolate) {
}
dart.fn(JS_SET_CURRENT_ISOLATE, dart.void, [core.Object]);
dart.fn(JS_SET_CURRENT_ISOLATE, dart.void, [dart.dynamic]);
function JS_CREATE_ISOLATE() {
}
dart.fn(JS_CREATE_ISOLATE);
Expand All @@ -56,7 +56,7 @@ dart_library.library('dart/_foreign_helper', null, /* Imports */[
dart.fn(JS_DART_OBJECT_CONSTRUCTOR);
function JS_INTERCEPTOR_CONSTANT(type) {
}
dart.fn(JS_INTERCEPTOR_CONSTANT, core.Object, [core.Type]);
dart.fn(JS_INTERCEPTOR_CONSTANT, dart.dynamic, [core.Type]);
function JS_OPERATOR_IS_PREFIX() {
}
dart.fn(JS_OPERATOR_IS_PREFIX, core.String, []);
Expand Down Expand Up @@ -107,7 +107,7 @@ dart_library.library('dart/_foreign_helper', null, /* Imports */[
dart.fn(JS_GET_NAME, core.String, [core.String]);
function JS_EMBEDDED_GLOBAL(typeDescription, name) {
}
dart.fn(JS_EMBEDDED_GLOBAL, core.Object, [core.String, core.String]);
dart.fn(JS_EMBEDDED_GLOBAL, dart.dynamic, [core.String, core.String]);
function JS_GET_FLAG(name) {
}
dart.fn(JS_GET_FLAG, core.bool, [core.String]);
Expand Down
Loading

0 comments on commit 4e7920c

Please sign in to comment.