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

Unified Diff: tool/input_sdk/private/types.dart

Issue 1530563003: Generate all runtime files from dart. (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: merged master Created 4 years, 11 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 | « tool/input_sdk/private/runtime.dart ('k') | tool/input_sdk/private/utils.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tool/input_sdk/private/types.dart
diff --git a/tool/input_sdk/private/types.dart b/tool/input_sdk/private/types.dart
new file mode 100644
index 0000000000000000000000000000000000000000..d74be7190782067ac9f11ce947719fcb1498a979
--- /dev/null
+++ b/tool/input_sdk/private/types.dart
@@ -0,0 +1,546 @@
+// 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 representation of runtime types.
+part of dart._runtime;
+
+/// The Symbol for storing type arguments on a specialized generic type.
+final _mixins = JS('', 'Symbol("mixins")');
+@JSExportName('implements')
+final implements_ = JS('', 'Symbol("implements")');
+final metadata = JS('', 'Symbol("metadata")');
+
+
+///
+/// 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();
+///
+///
+final TypeRep = JS('', '''
+ class TypeRep extends $LazyTagged(() => $Type) {
+ get name() {return this.toString();}
+ }
+''');
+
+final Dynamic = JS('', '''
+ class Dynamic extends $TypeRep {
+ toString() { return "dynamic"; }
+ }
+''');
+@JSExportName('dynamic')
+final dynamicR = JS('', 'new $Dynamic()');
+
+final Void = JS('', '''
+ class Void extends $TypeRep {
+ toString() { return "void"; }
+ }
+''');
+
+@JSExportName('void')
+final voidR = JS('', 'new $Void()');
+
+final Bottom = JS('', '''
+ class Bottom extends $TypeRep {
+ toString() { return "bottom"; }
+ }
+''');
+final bottom = JS('', 'new $Bottom()');
+
+final JSObject = JS('', '''
+ class JSObject extends $TypeRep {
+ toString() { return "NativeJavaScriptObject"; }
+ }
+''');
+final jsobject = JS('', 'new $JSObject()');
+
+final AbstractFunctionType = JS('', '''
+ class AbstractFunctionType extends $TypeRep {
+ constructor() {
+ super();
+ this._stringValue = null;
+ }
+
+ toString() { return this.name; }
+
+ get name() {
+ if (this._stringValue) return this._stringValue;
+
+ let buffer = '(';
+ for (let 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 (let i = 0; i < this.optionals.length; ++i) {
+ if (i > 0) {
+ buffer += ', ';
+ }
+ buffer += $typeName(this.optionals[i]);
+ }
+ buffer += ']';
+ } else if (Object.keys(this.named).length > 0) {
+ if (this.args.length > 0) buffer += ', ';
+ buffer += '{';
+ let names = $getOwnPropertyNames(this.named).sort();
+ for (let 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;
+ }
+ }
+''');
+
+final FunctionType = JS('', '''
+ class FunctionType extends $AbstractFunctionType {
+ /**
+ * 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;
+
+ // TODO(vsm): This is just parameter metadata for now.
+ this.metadata = [];
+ function process(array, metadata) {
+ var result = [];
+ for (var i = 0; i < array.length; ++i) {
+ var arg = array[i];
+ if (arg instanceof Array) {
+ metadata.push(arg.slice(1));
+ result.push(arg[0]);
+ } else {
+ metadata.push([]);
+ result.push(arg);
+ }
+ }
+ return result;
+ }
+ this.args = process(this.args, this.metadata);
+ this.optionals = process(this.optionals, this.metadata);
+ // TODO(vsm): Add named arguments.
+ this._canonize();
+ }
+ _canonize() {
+ if (this.definite) return;
+
+ function replace(a) {
+ return (a == $dynamicR) ? $bottom : 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;
+ }
+ }
+ }
+''');
+
+final Typedef = JS('', '''
+ class Typedef extends $AbstractFunctionType {
+ constructor(name, closure) {
+ super();
+ this._name = name;
+ this._closure = closure;
+ this._functionType = null;
+ }
+
+ get definite() {
+ return this._functionType.definite;
+ }
+
+ 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;
+ }
+
+ get metadata() {
+ return this.functionType.metadata;
+ }
+ }
+''');
+
+_functionType(definite, returnType, args, extra) => JS('', '''(() => {
+ // TODO(vsm): Cache / memomize?
+ let optionals;
+ let named;
+ if ($extra === void 0) {
+ optionals = [];
+ named = {};
+ } else if ($extra instanceof Array) {
+ optionals = $extra;
+ named = {};
+ } else {
+ optionals = [];
+ named = $extra;
+ }
+ return new $FunctionType($definite, $returnType, $args, optionals, named);
+})()''');
+
+///
+/// Create a "fuzzy" function type. If any arguments are dynamic
+/// they will be replaced with bottom.
+///
+functionType(returnType, args, extra) => JS('', '''(() => {
+ return _functionType(false, $returnType, $args, $extra);
+})()''');
+
+///
+/// Create a definite function type. No substitution of dynamic for
+/// bottom occurs.
+///
+definiteFunctionType(returnType, args, extra) => JS('', '''(() => {
+ return _functionType(true, $returnType, $args, $extra);
+})()''');
+
+typedef(name, closure) => JS('', '''(() => {
+ return new $Typedef($name, $closure);
+})()''');
+
+isDartType(type) => JS('', '''(() => {
+ return $read($type) === $Type;
+})()''');
+
+typeName(type) => JS('', '''(() => {
+ // Non-instance types
+ if ($type instanceof $TypeRep) return $type.toString();
+ // Instance types
+ let tag = $read($type);
+ if (tag === $Type) {
+ let name = $type.name;
+ let args = $getGenericArgs($type);
+ if (args) {
+ name += '<';
+ for (let i = 0; i < args.length; ++i) {
+ if (i > 0) name += ', ';
+ name += $typeName(args[i]);
+ }
+ name += '>';
+ }
+ return name;
+ }
+ if (tag) return "Not a type: " + tag.name;
+ return "JSObject<" + $type.name + ">";
+})()''');
+
+isFunctionType(type) => JS('', '''(() => {
+ return $type instanceof $AbstractFunctionType || $type == $Function;
+})()''');
+
+isFunctionSubType(ft1, ft2) => JS('', '''(() => {
+ if ($ft2 == $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 != $voidR) {
+ return false;
+ }
+ }
+
+ let args1 = $ft1.args;
+ let args2 = $ft2.args;
+
+ if (args1.length > args2.length) {
+ return false;
+ }
+
+ for (let i = 0; i < args1.length; ++i) {
+ if (!$isSubtype_(args2[i], args1[i])) {
+ return false;
+ }
+ }
+
+ let optionals1 = $ft1.optionals;
+ let optionals2 = $ft2.optionals;
+
+ if (args1.length + optionals1.length < args2.length + optionals2.length) {
+ return false;
+ }
+
+ let j = 0;
+ for (let i = args1.length; i < args2.length; ++i, ++j) {
+ if (!$isSubtype_(args2[i], optionals1[j])) {
+ return false;
+ }
+ }
+
+ for (let i = 0; i < optionals2.length; ++i, ++j) {
+ if (!$isSubtype_(optionals2[i], optionals1[j])) {
+ return false;
+ }
+ }
+
+ let named1 = $ft1.named;
+ let named2 = $ft2.named;
+
+ let names = $getOwnPropertyNames(named2);
+ for (let 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)) {
+ return false;
+ }
+ }
+
+ return true;
+})()''');
+
+///
+/// Computes the canonical type.
+/// This maps JS types onto their corresponding Dart Type.
+///
+// TODO(jmesserly): lots more needs to be done here.
+canonicalType(t) => JS('', '''(() => {
+ if ($t === Object) return $Object;
+ if ($t === Function) return $Function;
+ if ($t === Array) return $List;
+
+ // We shouldn't normally get here with these types, unless something strange
+ // happens like subclassing Number in JS and passing it to Dart.
+ if ($t === String) return $String;
+ if ($t === Number) return $double;
+ if ($t === Boolean) return $bool;
+ return $t;
+})()''');
+
+final subtypeMap = JS('', 'new Map()');
+isSubtype(t1, t2) => JS('', '''(() => {
+ // See if we already know the answer
+ // TODO(jmesserly): general purpose memoize function?
+ let map = $subtypeMap.get($t1);
+ let result;
+ if (map) {
+ result = map.get($t2);
+ if (result !== void 0) return result;
+ } else {
+ $subtypeMap.set($t1, map = new Map());
+ }
+ result = $isSubtype_($t1, $t2);
+ map.set($t2, result);
+ return result;
+})()''');
+
+_isBottom(type) => JS('', '''(() => {
+ return $type == $bottom;
+})()''');
+
+_isTop(type) => JS('', '''(() => {
+ return $type == $Object || ($type == $dynamicR);
+})()''');
+
+isSubtype_(t1, t2) => JS('', '''(() => {
+ $t1 = $canonicalType($t1);
+ $t2 = $canonicalType($t2);
+ if ($t1 == $t2) return true;
+
+ // Trivially true.
+ if ($_isTop($t2) || $_isBottom($t1)) {
+ return true;
+ }
+
+ // Trivially false.
+ if ($_isTop($t1) || $_isBottom($t2)) {
+ return false;
+ }
+
+ // "Traditional" name-based subtype check.
+ if ($isClassSubType($t1, $t2)) {
+ return true;
+ }
+
+ // Function subtyping.
+ // 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;
+})()''');
+
+isClassSubType(t1, t2) => JS('', '''(() => {
+ // We support Dart's covariant generics with the caveat that we do not
+ // substitute bottom for dynamic in subtyping rules.
+ // I.e., given T1, ..., Tn where at least one Ti != dynamic we disallow:
+ // - S !<: S<T1, ..., Tn>
+ // - S<dynamic, ..., dynamic> !<: S<T1, ..., Tn>
+ $t1 = $canonicalType($t1);
+ $assert_($t2 == $canonicalType($t2));
+ if ($t1 == $t2) return true;
+
+ if ($t1 == $Object) return false;
+
+ // If t1 is a JS Object, we may not hit core.Object.
+ if ($t1 == null) return $t2 == $Object || $t2 == $dynamicR;
+
+ // Check if t1 and t2 have the same raw type. If so, check covariance on
+ // type parameters.
+ let raw1 = $getGenericClass($t1);
+ let raw2 = $getGenericClass($t2);
+ if (raw1 != null && raw1 == raw2) {
+ let typeArguments1 = $getGenericArgs($t1);
+ let typeArguments2 = $getGenericArgs($t2);
+ let length = typeArguments1.length;
+ if (typeArguments2.length == 0) {
+ // t2 is the raw form of t1
+ return true;
+ } else if (length == 0) {
+ // t1 is raw, but t2 is not
+ return false;
+ }
+ $assert_(length == typeArguments2.length);
+ for (let i = 0; i < length; ++i) {
+ if (!$isSubtype(typeArguments1[i], typeArguments2[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Check superclass.
+ if ($isClassSubType($t1.__proto__, $t2)) return true;
+
+ // Check mixins.
+ let mixins = $getMixins($t1);
+ if (mixins) {
+ for (let m1 of mixins) {
+ // TODO(jmesserly): remove the != null check once we can load core libs.
+ if (m1 != null && $isClassSubType(m1, $t2)) return true;
+ }
+ }
+
+ // Check interfaces.
+ let getInterfaces = $getImplements($t1);
+ if (getInterfaces) {
+ for (let i1 of getInterfaces()) {
+ // TODO(jmesserly): remove the != null check once we can load core libs.
+ if (i1 != null && $isClassSubType(i1, $t2)) return true;
+ }
+ }
+
+ return false;
+})()''');
+
+// 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.
+isGroundType(type) => JS('', '''(() => {
+ // TODO(vsm): Cache this if we start using it at runtime.
+
+ if ($type instanceof $AbstractFunctionType) {
+ if (!$_isTop($type.returnType)) return false;
+ for (let i = 0; i < $type.args.length; ++i) {
+ if (!$_isBottom($type.args[i])) return false;
+ }
+ for (let i = 0; i < $type.optionals.length; ++i) {
+ 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]])) return false;
+ }
+ return true;
+ }
+
+ let typeArgs = $getGenericArgs($type);
+ if (!typeArgs) return true;
+ for (let t of typeArgs) {
+ if (t != $Object && t != $dynamicR) return false;
+ }
+ return true;
+})()''');
« no previous file with comments | « tool/input_sdk/private/runtime.dart ('k') | tool/input_sdk/private/utils.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698