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

Unified Diff: lib/runtime/classes.js

Issue 1182653002: Refactor runtime into libraries, better type reps (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Add missing file Created 5 years, 6 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 | « no previous file | lib/runtime/dart/_foreign_helper.js » ('j') | lib/runtime/dartx.js » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/runtime/classes.js
diff --git a/lib/runtime/classes.js b/lib/runtime/classes.js
new file mode 100644
index 0000000000000000000000000000000000000000..86f5c531f7e3bcb98faf50d4ab6b2771e0b14764
--- /dev/null
+++ b/lib/runtime/classes.js
@@ -0,0 +1,415 @@
+// 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 operations that define and manipulate Dart
+ * classes. Included in this are:
+ * - Generics
+ * - Class metadata
+ * - Extension methods
+ */
+
+// TODO(leafp): Consider splitting some of this out.
+loader.library('dart/classes', null, /* Imports */[
+], /* Lazy Imports */[
+ 'dart/core',
+ 'dart/dartx',
+ 'dart/types',
+ 'dart/rtti',
+ 'dart/_interceptors'
+], function(exports, core, dartx, types, rtti, _interceptors) {
+ 'use strict';
+
+ const assert = js_utils.assert;
+ const copyProperties = js_utils.copyProperties;
+ const copyTheseProperties = js_utils.copyTheseProperties;
+ const defineMemoizedGetter = js_utils.defineMemoizedGetter;
+ const defineProperty = js_utils.defineProperty;
+ const getOwnPropertyDescriptor = js_utils.getOwnPropertyDescriptor;
+ const getOwnPropertySymbols = js_utils.getOwnPropertySymbols;
+ const getOwnPropertyNames = js_utils.getOwnPropertyNames;
+ const safeGetOwnProperty = js_utils.safeGetOwnProperty;
+ const slice = js_utils.slice;
+ const throwRuntimeError = js_utils.throwRuntimeError;
Jennifer Messerly 2015/06/12 17:32:25 do we really need this function? if so, maybe just
Leaf 2015/06/12 19:52:14 I wanted us to have a specific canonical way to th
+
+
+ /** The Symbol for storing type arguments on a specialized generic type. */
+ let _mixins = Symbol('mixins');
Jennifer Messerly 2015/06/12 17:32:25 s/let/const? (JS `let` : Dart `var` :: JS `const`
Leaf 2015/06/12 19:52:14 Done.
+ let _implements = Symbol('implements');
+ exports.implements = _implements;
+ let _metadata = Symbol('metadata');
+ exports.metadata = _metadata;
+
+ /**
+ * Returns a new type that mixes members from base and all mixins.
+ *
+ * Each mixin applies in sequence, with further to the right ones overriding
+ * previous entries.
+ *
+ * For each mixin, we only take its own properties, not anything from its
+ * superclass (prototype).
+ */
+ function mixin(base/*, ...mixins*/) {
+ // Create an initializer for the mixin, so when derived constructor calls
+ // super, we can correctly initialize base and mixins.
+ let mixins = slice.call(arguments, 1);
+
+ // Create a class that will hold all of the mixin methods.
+ class Mixin extends base {
+ // Initializer method: run mixin initializers, then the base.
+ [base.name](/*...args*/) {
+ // Run mixin initializers. They cannot have arguments.
+ // Run them backwards so most-derived mixin is initialized first.
+ for (let i = mixins.length - 1; i >= 0; i--) {
+ let mixin = mixins[i];
+ let init = mixin.prototype[mixin.name];
+ if (init) init.call(this);
+ }
+ // Run base initializer.
+ let init = base.prototype[base.name];
+ if (init) init.apply(this, arguments);
+ }
+ }
+ // Copy each mixin's methods, with later ones overwriting earlier entries.
+ for (let m of mixins) {
+ copyProperties(Mixin.prototype, m.prototype);
+ }
+
+ // Set the signature of the Mixin class to be the composition
+ // of the signatures of the mixins.
+ setSignature(Mixin, {
+ methods: () => {
+ let s = {};
+ for (let m of mixins) {
+ copyProperties(s, m[_methodSig]);
+ }
+ return s;
+ }
+ });
+
+ // Save mixins for reflection
+ Mixin[_mixins] = mixins;
+ return Mixin;
+ }
+ exports.mixin = mixin;
+
+ function getMixins (clazz) {
+ return clazz[_mixins];
+ }
+ exports.getMixins = getMixins;
+
+ function getImplements (clazz) {
+ return clazz[_implements];
+ }
+ exports.getImplements = getImplements;
+
+ /** The Symbol for storing type arguments on a specialized generic type. */
+ let _typeArguments = Symbol('typeArguments');
+ let _originalDeclaration = Symbol('originalDeclaration');
+
+ /** Memoize a generic type constructor function. */
+ function generic(typeConstructor) {
+ let length = typeConstructor.length;
+ if (length < 1) throwRuntimeError('must have at least one generic type argument');
Jennifer Messerly 2015/06/12 17:32:25 long line
Leaf 2015/06/12 19:52:14 Done.
+
+ let resultMap = new Map();
+ function makeGenericType(/*...arguments*/) {
+ if (arguments.length != length && arguments.length != 0) {
+ throwRuntimeError('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;
+ for (let i = 0; i < length; i++) {
+ let arg = args[i];
+ if (arg == null) {
+ throwRuntimeError('type arguments should not be null: ' + typeConstructor);
Jennifer Messerly 2015/06/12 17:32:25 here too
Leaf 2015/06/12 19:52:14 Done.
+ }
+ let map = value;
+ value = map.get(arg);
+ if (value === void 0) {
+ if (i + 1 == length) {
+ value = typeConstructor.apply(null, args);
+ // Save the type constructor and arguments for reflection.
+ if (value) {
+ value[_typeArguments] = args;
+ value[_originalDeclaration] = makeGenericType;
+ }
+ } else {
+ value = new Map();
+ }
+ map.set(arg, value);
+ }
+ }
+ return value;
+ }
+ return makeGenericType;
+ }
+ exports.generic = generic;
+
+ function getGenericClass(type) {
+ return safeGetOwnProperty(type, _originalDeclaration);
+ };
+ exports.getGenericClass = getGenericClass;
+
+ function getGenericArgs(type) {
+ return safeGetOwnProperty(type, _typeArguments);
+ };
+ exports.getGenericArgs = getGenericArgs;
+
+ let _constructorSig = Symbol('sigCtor');
+ let _methodSig = Symbol("sig");
+ let _staticSig = Symbol("sigStatic");
+
+ /// Get the type of a method using the stored signature
+ function _getMethodType(obj, name) {
+ if (obj === void 0) return void 0;
+ if (obj == null) return void 0;
+ let sigObj = obj.__proto__.constructor[_methodSig];
+ if (sigObj === void 0) return void 0;
+ let parts = sigObj[name];
+ if (parts === void 0) return void 0;
+ return types.functionType.apply(null, parts);
+ }
+
+ /// Get the type of a constructor from a class using the stored signature
+ /// If name is undefined, returns the type of the default constructor
+ /// Returns undefined if the constructor is not found.
+ function _getConstructorType(cls, name) {
+ if(!name) name = cls.name;
+ if (cls === void 0) return void 0;
+ if (cls == null) return void 0;
+ let sigCtor = cls[_constructorSig];
+ if (sigCtor === void 0) return void 0;
+ let parts = sigCtor[name];
+ if (parts === void 0) return void 0;
+ return types.functionType.apply(null, parts);
+ }
+ exports.classGetConstructorType = _getConstructorType;
+
+ /// Given an object and a method name, tear off the method.
+ /// Sets the runtime type of the torn off method appropriately,
+ /// and also binds the object.
+ /// TODO(leafp): Consider caching the tearoff on the object?
+ function bind(obj, name) {
+ let f = obj[name].bind(obj);
+ let sig = _getMethodType(obj, name);
+ assert(sig);
+ rtti.tag(f, sig);
+ return f;
+ }
+ exports.bind = bind;
+
+ // Set up the method signature field on the constructor
+ function _setMethodSignature(f, sigF) {
+ defineMemoizedGetter(f, _methodSig, () => {
+ let sigObj = sigF();
+ sigObj.__proto__ = f.__proto__[_methodSig];
+ return sigObj;
+ });
+ }
+
+ // Set up the constructor signature field on the constructor
+ function _setConstructorSignature(f, sigF) {
+ defineMemoizedGetter(f, _constructorSig, sigF);
+ }
+
+ // Set up the static signature field on the constructor
+ function _setStaticSignature(f, sigF) {
+ defineMemoizedGetter(f, _staticSig, sigF);
+ }
+
+ // Set the lazily computed runtime type field on static methods
+ function _setStaticTypes(f, names) {
+ for (let name of names) {
+ rtti.tagMemoized(f[name], function() {
+ let parts = f[_staticSig][name];
+ return types.functionType.apply(null, parts);
+ })
+ }
+ }
+
+ /// Set up the type signature of a class (constructor object)
+ /// f is a constructor object
+ /// signature is an object containing optional properties as follows:
+ /// methods: A function returning an object mapping method names
+ /// to method types. The function is evaluated lazily and cached.
+ /// statics: A function returning an object mapping static method
+ /// names to types. The function is evalutated lazily and cached.
+ /// names: An array of the names of the static methods. Used to
+ /// permit eagerly setting the runtimeType field on the methods
+ /// while still lazily computing the type descriptor object.
+ function setSignature(f, signature) {
+ let constructors =
+ ('constructors' in signature) ? signature.constructors : () => ({});
+ let methods =
+ ('methods' in signature) ? signature.methods : () => ({});
+ let statics =
+ ('statics' in signature) ? signature.statics : () => ({});
+ let names =
+ ('names' in signature) ? signature.names : [];
+ _setConstructorSignature(f, constructors);
+ _setMethodSignature(f, methods);
+ _setStaticSignature(f, statics);
+ _setStaticTypes(f, names);
+ rtti.tagMemoized(f, () => core.Type);
+ }
+ exports.setSignature = setSignature;
+
+ function hasMethod(obj, name) {
+ return _getMethodType(obj, name) !== void 0;
+ }
+ exports.hasMethod = hasMethod;
+
+ exports.getMethodType = _getMethodType;
+
+ /**
+ * This is called whenever a derived class needs to introduce a new field,
+ * shadowing a field or getter/setter pair on its parent.
+ *
+ * This is important because otherwise, trying to read or write the field
+ * would end up calling the getter or setter, and one of those might not even
+ * exist, resulting in a runtime error. Even if they did exist, that's the
+ * wrong behavior if a new field was declared.
+ */
+ function virtualField(subclass, fieldName) {
+ // If the field is already overridden, do nothing.
+ let prop = getOwnPropertyDescriptor(subclass.prototype, fieldName);
+ if (prop) return;
+
+ let symbol = Symbol(subclass.name + '.' + fieldName);
+ defineProperty(subclass.prototype, fieldName, {
+ get: function() { return this[symbol]; },
+ set: function(x) { this[symbol] = x; }
+ });
+ }
+ exports.virtualField = virtualField;
+
+ /**
+ * Given a class and an initializer method name, creates a constructor
+ * function with the same name. For example `new SomeClass.name(args)`.
+ */
+ function defineNamedConstructor(clazz, name) {
+ let proto = clazz.prototype;
+ let initMethod = proto[name];
+ let ctor = function() { return initMethod.apply(this, arguments); };
+ ctor.prototype = proto;
+ // Use defineProperty so we don't hit a property defined on Function,
+ // like `caller` and `arguments`.
+ defineProperty(clazz, name, { value: ctor, configurable: true });
+ }
+ exports.defineNamedConstructor = defineNamedConstructor;
+
+ let _extensionType = Symbol('extensionType');
+
+ function getExtensionSymbol(name) {
+ let sym = dartx[name];
+ if (!sym) dartx[name] = sym = Symbol('dartx.' + name);
+ return sym;
+ }
+
+ function defineExtensionNames(names) {
+ names.forEach(getExtensionSymbol);
+ }
+ exports.defineExtensionNames = defineExtensionNames;
+
+ /**
+ * Copy symbols from the prototype of the source to destination.
+ * These are the only properties safe to copy onto an existing public
+ * JavaScript class.
+ */
+ function registerExtension(jsType, dartExtType) {
+ let extProto = dartExtType.prototype;
+ let jsProto = jsType.prototype;
+
+ // Mark the JS type's instances so we can easily check for extensions.
+ assert(jsProto[_extensionType] === void 0);
+ jsProto[_extensionType] = extProto;
+
+ let dartObjProto = core.Object.prototype;
+ while (extProto !== dartObjProto && extProto !== jsProto) {
+ copyTheseProperties(jsProto, extProto, getOwnPropertySymbols(extProto));
+ extProto = extProto.__proto__;
+ }
+ }
+ exports.registerExtension = registerExtension;
+
+ /**
+ * Mark a concrete type as implementing extension methods.
+ * For example: `class MyIter implements Iterable`.
+ *
+ * This takes a list of names, which are the extension methods implemented.
+ * It will add a forwarder, so the extension method name redirects to the
+ * normal Dart method name. For example:
+ *
+ * defineExtensionMembers(MyType, ['add', 'remove']);
+ *
+ * Results in:
+ *
+ * MyType.prototype[dartx.add] = MyType.prototype.add;
+ * MyType.prototype[dartx.remove] = MyType.prototype.remove;
+ */
+ // TODO(jmesserly): essentially this gives two names to the same method.
+ // This benefit is roughly equivalent call performance either way, but the
+ // cost is we need to call defineExtensionMEmbers any time a subclass overrides
Jennifer Messerly 2015/06/12 17:32:25 long line, also looks like I typo'd defineExtensio
Leaf 2015/06/12 19:52:14 Done.
+ // one of these methods.
+ function defineExtensionMembers(type, methodNames) {
+ let proto = type.prototype;
+ for (let name of methodNames) {
+ let method = getOwnPropertyDescriptor(proto, name);
+ defineProperty(proto, getExtensionSymbol(name), method);
+ }
+ // Ensure the signature is available too.
+ // TODO(jmesserly): not sure if we can do this in a cleaner way. Essentially
+ // we need to copy the signature (and in the future, other data like
+ // annotations) any time we copy a method as part of our metaprogramming.
+ // It might be more friendly to JS metaprogramming if we include this info
+ // on the function.
+ let originalSigFn = getOwnPropertyDescriptor(type, _methodSig).get;
+ defineMemoizedGetter(type, _methodSig, function() {
+ let sig = originalSigFn();
+ for (let name of methodNames) {
+ sig[getExtensionSymbol(name)] = sig[name];
+ }
+ return sig;
+ });
+ }
+ exports.defineExtensionMembers = defineExtensionMembers;
+
+ function canonicalMember(obj, name) {
+ if (obj[_extensionType]) return dartx[name];
+ return name;
+ }
+ exports.canonicalMember = canonicalMember;
+
+ // TODO(vsm): Rationalize these type methods. We're currently using the
+ // setType / proto scheme for nominal types (e.g., classes) and the
+ // setRuntimeType / field scheme for structural types (e.g., functions
+ // - and only in tests for now).
+ // See: https://github.com/dart-lang/dev_compiler/issues/172
+
+ /** Sets the type of `obj` to be `type` */
+ function setType(obj, type) {
+ obj.__proto__ = type.prototype;
+ return obj;
+ }
+ exports.setType = setType;
+
+ /** Sets the element type of a list literal. */
+ function list(obj, elementType) {
+ return setType(obj, _interceptors.JSArray$(elementType));
+ }
+ exports.list = list;
+
+ function setBaseClass(derived, base) {
+ // Link the extension to the type it's extending as a base class.
+ derived.prototype.__proto__ = base.prototype;
+ }
+ exports.setBaseClass = setBaseClass;
+
+});
« no previous file with comments | « no previous file | lib/runtime/dart/_foreign_helper.js » ('j') | lib/runtime/dartx.js » ('J')

Powered by Google App Engine
This is Rietveld 408576698