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

Unified Diff: lib/runtime/dart_runtime.js

Issue 1076883003: Initial support for runtime function types and type checking (Closed) Base URL: https://github.com/dart-lang/dev_compiler.git@master
Patch Set: Address comments Created 5 years, 8 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/isolate.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 149543b21313802e1b5d86b7c21f6dd22122e3dc..29105d48a2d76a341d49eba6f60cd2e074a81f14 100644
--- a/lib/runtime/dart_runtime.js
+++ b/lib/runtime/dart_runtime.js
@@ -126,7 +126,15 @@ var dart, _js_helper;
// Undefined is handled above. For historical reasons,
// typeof null == "object" in JS.
if (obj === null) return core.Null;
- return obj.constructor;
+ // TODO(vsm): Should we treat Dart and JS objects differently here?
+ // E.g., we can check if obj instanceof core.Object to differentiate.
+ var result = obj[dart.runtimeType];
+ if (result) return result;
+ result = obj.constructor;
+ if (result == Function) {
+ return getFunctionType(obj);
+ }
+ return result;
}
dart.getRuntimeType = getRuntimeType;
@@ -170,18 +178,35 @@ var dart, _js_helper;
}
dart.isSubtype = isSubtype;
- function isSubtype_(t1, t2) {
+ function _isBottom(type, dynamicIsBottom) {
+ return (type == dart.dynamic && dynamicIsBottom) || type == dart.bottom;
+ }
+
+ function _isTop(type, dynamicIsBottom) {
+ return type == core.Object || (type == dart.dynamic && !dynamicIsBottom);
+ }
+
+ function isSubtype_(t1, t2, opt_dynamicIsBottom) {
+ let dynamicIsBottom =
+ opt_dynamicIsBottom === void 0 ? false : opt_dynamicIsBottom;
+
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 top - the base type of everything.
- if (t1 == dart.dynamic) return false;
- if (t2 == dart.dynamic) return true;
+ // Here, we treat dynamic as one or the other depending on context,
+ // but not both.
- if (t2 == core.Object) return true;
- if (t1 == core.Object) return false;
+ // Trivially true.
+ if (_isTop(t2, dynamicIsBottom) || _isBottom(t1, dynamicIsBottom)) {
+ return true;
+ }
+
+ // Trivially false.
+ if (_isTop(t1, dynamicIsBottom) || _isBottom(t2, dynamicIsBottom)) {
+ return false;
+ }
// "Traditional" name-based subtype check.
if (isClassSubType(t1, t2)) {
@@ -189,10 +214,11 @@ var dart, _js_helper;
}
// Function subtyping.
- // TODO(jmesserly): implement this properly.
- if (isClassSubType(t1, core.Function) &&
- isClassSubType(t2, core.Function)) {
- return true;
+ // TODO(vsm): Handle Objects with call methods. Those are functions
+ // even if they do not *nominally* subtype core.Function.
+ if (isFunctionType(t1) &&
+ isFunctionType(t2)) {
+ return isFunctionSubType(t1, t2);
}
return false;
}
@@ -212,6 +238,9 @@ var dart, _js_helper;
if (t1 == core.Object) return false;
+ // If t1 is a JS Object, we may not hit core.Object.
+ if (t1 == null) return t2 == core.Object || t2 == dart.dynamic;
+
// Check if t1 and t2 have the same raw type. If so, check covariance on
// type parameters.
let raw1 = safeGetOwnProperty(t1, dart.originalDeclaration);
@@ -260,17 +289,27 @@ var dart, _js_helper;
return false;
}
- function closureWrap(obj, type) {
- // TODO(vsm): Remove this once we handle in the checker.
- return obj;
- }
- dart.closureWrap = closureWrap;
-
-
// TODO(jmesserly): this isn't currently used, but it could be if we want
// `obj is NonGroundType<T,S>` to be rejected at runtime instead of compile
- // time. Also TODO: update this to handle functions.
+ // time.
function isGroundType(type) {
+ // TODO(vsm): Cache this if we start using it at runtime.
+
+ if (type instanceof AbstractFunctionType) {
+ if (!_isTop(type.returnType, false)) return false;
+ for (var i = 0; i < type.args.length; ++i) {
+ if (!_isBottom(type.args[i], true)) return false;
+ }
+ for (var i = 0; i < type.optionals.length; ++i) {
+ if (!_isBottom(type.optionals[i], true)) return false;
+ }
+ var names = Object.getOwnPropertyNames(type.named);
+ for (var i = 0; i < names.length; ++i) {
+ if (!_isBottom(type.named[names[i]], true)) return false;
+ }
+ return true;
+ }
+
let typeArgs = safeGetOwnProperty(type, dart.typeArguments);
if (!typeArgs) return true;
for (let t of typeArgs) {
@@ -301,6 +340,207 @@ var dart, _js_helper;
}
dart.notNull = notNull;
+ function _typeName(type) {
+ var name = type.name;
+ if (!name) throw 'Unexpected type: ' + type;
+ return name;
+ }
+
+ class AbstractFunctionType {
+ constructor() {
+ this._stringValue = null;
+ }
+
+ get name() {
+ if (this._stringValue) return this._stringValue;
+
+ var buffer = '(';
+ for (var i = 0; i < this.args.length; ++i) {
+ if (i > 0) {
+ buffer += ', ';
+ }
+ buffer += _typeName(this.args[i]);
+ }
+ if (this.optionals.length > 0) {
+ if (this.args.length > 0) buffer += ', ';
+ buffer += '[';
+ for (var i = 0; i < this.optionals.length; ++i) {
+ if (i > 0) {
+ buffer += ', ';
+ }
+ buffer += _typeName(this.optionals[i]);
+ }
+ buffer += ']';
+ } else if (this.named.length > 0) {
+ if (this.args.length > 0) buffer += ', ';
+ buffer += '{';
+ let names = Object.getOwnPropertyNames(this.named).sort();
+ for (var i = 0; i < names.length; ++i) {
+ if (i > 0) {
+ buffer += ', ';
+ }
+ buffer += names[i] + ': ' + _typeName(this.named[names[i]]);
+ }
+ buffer += '}';
+ }
+
+ buffer += ') -> ' + _typeName(this.returnType);
+ this._stringValue = buffer;
+ return buffer;
+ }
+ }
+
+ class FunctionType extends AbstractFunctionType {
+ constructor(returnType, args, optionals, named) {
+ super();
+ this.returnType = returnType;
+ this.args = args;
+ this.optionals = optionals;
+ this.named = named;
+ }
+ }
+
+ function functionType(returnType, args, extra) {
+ // TODO(vsm): Cache / memomize?
+ var optionals;
+ var named;
+ if (extra === void 0) {
+ optionals = [];
+ named = {};
+ } else if (extra instanceof Array) {
+ optionals = extra;
+ named = {};
+ } else {
+ optionals = [];
+ named = extra;
+ }
+ return new FunctionType(returnType, args, optionals, named);
+ }
+ dart.functionType = functionType;
+
+ class Typedef extends AbstractFunctionType {
+ constructor(name, closure) {
+ super();
+ this._name = name;
+ this._closure = closure;
+ this._functionType = null;
+ }
+
+ get name() {
+ return this._name;
+ }
+
+ get functionType() {
+ if (!this._functionType) {
+ this._functionType = this._closure();
+ }
+ return this._functionType;
+ }
+
+ get returnType() {
+ return this.functionType.returnType;
+ }
+
+ get args() {
+ return this.functionType.args;
+ }
+
+ get optionals() {
+ return this.functionType.optionals;
+ }
+
+ get named() {
+ return this.functionType.named;
+ }
+ }
+
+ function typedef(name, closure) {
+ return new Typedef(name, closure);
+ }
+ dart.typedef = typedef;
+
+ function isFunctionType(type) {
+ return isClassSubType(type, core.Function) || type instanceof AbstractFunctionType;
+ }
+
+ function getFunctionType(obj) {
+ // TODO(vsm): Encode this properly on the function for Dart-generated code.
+ var args = Array.apply(null, new Array(obj.length)).map(function(){return core.Object});
+ return functionType(dart.bottom, args);
+ }
+
+ function isFunctionSubType(ft1, ft2) {
+ if (ft2 == core.Function) {
+ return true;
+ }
+
+ let ret1 = ft1.returnType;
+ let ret2 = ft2.returnType;
+
+ if (!isSubtype_(ret1, ret2)) {
+ // Covariant return types
+ // Note, void (which can only appear as a return type) is effectively
+ // treated as dynamic. If the base return type is void, we allow any
+ // subtype return type.
+ // E.g., we allow:
+ // () -> int <: () -> void
+ if (ret2 != dart.void) {
+ return false;
+ }
+ }
+
+ let args1 = ft1.args;
+ let args2 = ft2.args;
+
+ if (args1.length > args2.length) {
+ return false;
+ }
+
+ for (var i = 0; i < args1.length; ++i) {
+ if (!isSubtype_(args2[i], args1[i], true)) {
+ return false;
+ }
+ }
+
+ let optionals1 = ft1.optionals;
+ let optionals2 = ft2.optionals;
+
+ if (args1.length + optionals1.length < args2.length + optionals2.length) {
+ return false;
+ }
+
+ var j = 0;
+ for (var i = args1.length; i < args2.length; ++i, ++j) {
+ if (!isSubtype_(args2[i], optionals1[j], true)) {
+ return false;
+ }
+ }
+
+ for (var i = 0; i < optionals2.length; ++i, ++j) {
+ if (!isSubtype_(optionals2[i], optionals1[j], true)) {
+ return false;
+ }
+ }
+
+ let named1 = ft1.named;
+ let named2 = ft2.named;
+
+ let names = Object.getOwnPropertyNames(named2);
+ for (var i = 0; i < names.length; ++i) {
+ let name = names[i];
+ let n1 = named1[name]
+ let n2 = named2[name];
+ if (n1 === void 0) {
+ return false;
+ }
+ if (!isSubtype_(n2, n1, true)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
/**
* Defines a lazy property.
* After initial get or set, it will replace itself with a value property.
@@ -508,6 +748,11 @@ var dart, _js_helper;
// 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'; } };
+
+ // TODO(vsm): How should we encode the runtime type?
+ dart.runtimeType = Symbol('runtimeType');
dart.JsSymbol = Symbol;
« no previous file with comments | « lib/runtime/dart/isolate.js ('k') | lib/src/codegen/js_codegen.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698