Files
sdk/pkg/dev_compiler/lib/runtime/_rtti.js
T
Leaf Petersen 4e7920c487 Handle dynamic as bottom inside of function type reps.
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
2015-06-17 16:46:38 -07:00

181 lines
5.7 KiB
JavaScript

// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// 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.
/* This library defines the association between runtime objects and
* runtime types.
*/
dart_library.library('dart_runtime/_rtti', null, /* Imports */[
], /* Lazy Imports */[
'dart/core',
'dart_runtime/_types'
], function(exports, core, types) {
'use strict';
const defineLazyProperty = dart_utils.defineLazyProperty;
const defineProperty = Object.defineProperty;
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
* 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)
*
* 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
if (arguments.length == 2) {
defineLazyProperty(closure, _runtimeType, {get : arguments[1]});
return closure;
}
let 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(() => 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.definiteFunctionType.apply(null, args);
}
tag(closure, t);
return closure;
}
exports.fn = fn;
// TODO(vsm): How should we encode the runtime type?
const _runtimeType = Symbol('_runtimeType');
function checkPrimitiveType(obj) {
switch (typeof obj) {
case "undefined":
return core.Null;
case "number":
return Math.floor(obj) == obj ? core.int : core.double;
case "boolean":
return core.bool;
case "string":
return core.String;
case "symbol":
return Symbol;
}
// Undefined is handled above. For historical reasons,
// typeof null == "object" in JS.
if (obj === null) return core.Null;
return null;
}
function runtimeType(obj) {
let result = checkPrimitiveType(obj);
if (result !== null) return result;
return obj.runtimeType;
}
exports.runtimeType = runtimeType;
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(() => types.dynamic);
return types.definiteFunctionType(types.bottom, args);
}
/**
* Returns the runtime type of obj. This is the same as `obj.realRuntimeType`
* but will not call an overridden getter.
*
* Currently this will return null for non-Dart objects.
*/
function realRuntimeType(obj) {
let result = checkPrimitiveType(obj);
if (result !== null) return result;
// TODO(vsm): Should we treat Dart and JS objects differently here?
// E.g., we can check if obj instanceof core.Object to differentiate.
result = obj[_runtimeType];
if (result) return result;
result = obj.constructor;
if (result == Function) {
return getFunctionType(obj);
}
return result;
}
exports.realRuntimeType = realRuntimeType;
function LazyTagged(infoFn) {
class _Tagged {
get [_runtimeType]() {return infoFn();}
}
return _Tagged;
}
exports.LazyTagged = LazyTagged;
function read(value) {
return value[_runtimeType];
}
exports.read = read;
function tag(value, info) {
value[_runtimeType] = info;
}
exports.tag = tag;
function tagComputed(value, compute) {
defineProperty(value, _runtimeType, { get: compute });
}
exports.tagComputed = tagComputed;
function tagMemoized(value, compute) {
let cache = null;
function getter() {
if (compute == null) return cache;
cache = compute();
compute = null;
return cache;
}
tagComputed(value, getter);
}
exports.tagMemoized = tagMemoized;
});