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

Unified Diff: tool/input_sdk/private/classes.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/lib/js/dart2js/js_dart2js.dart ('k') | tool/input_sdk/private/errors.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tool/input_sdk/private/classes.dart
diff --git a/tool/input_sdk/private/classes.dart b/tool/input_sdk/private/classes.dart
new file mode 100644
index 0000000000000000000000000000000000000000..7a784a4257c321f8ed92d193a05ddaca7ac5034c
--- /dev/null
+++ b/tool/input_sdk/private/classes.dart
@@ -0,0 +1,358 @@
+// 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.
+part of dart._runtime;
+
+///
+/// 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).
+///
+mixin(base, @rest mixins) => JS('', '''(() => {
+ // Create an initializer for the mixin, so when derived constructor calls
+ // super, we can correctly initialize base and mixins.
+
+ // 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, args);
+ }
+ }
+ // 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;
+})()''');
+
+getMixins(clazz) => JS('', '$clazz[$_mixins]');
+
+getImplements(clazz) => JS('', '$clazz[$implements_]');
+
+/// The Symbol for storing type arguments on a specialized generic type.
+final _typeArguments = JS('', 'Symbol("typeArguments")');
+final _originalDeclaration = JS('', 'Symbol("originalDeclaration")');
+
+/// Memoize a generic type constructor function.
+generic(typeConstructor) => JS('', '''(() => {
+ let length = $typeConstructor.length;
+ if (length < 1) {
+ $throwInternalError('must have at least one generic type argument');
+ }
+ let resultMap = new Map();
+ function makeGenericType(...args) {
+ if (args.length != length && args.length != 0) {
+ $throwInternalError('requires ' + length + ' or 0 type arguments');
+ }
+ while (args.length < length) args.push($dynamicR);
+
+ let value = resultMap;
+ for (let i = 0; i < length; i++) {
+ let arg = args[i];
+ if (arg == null) {
+ $throwInternalError('type arguments should not be null: '
+ + $typeConstructor);
+ }
+ 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;
+})()''');
+
+getGenericClass(type) =>
+ JS('', '$safeGetOwnProperty($type, $_originalDeclaration)');
+
+getGenericArgs(type) =>
+ JS('', '$safeGetOwnProperty($type, $_typeArguments)');
+
+final _constructorSig = JS('', 'Symbol("sigCtor")');
+final _methodSig = JS('', 'Symbol("sig")');
+final _staticSig = JS('', 'Symbol("sigStatic")');
+
+/// Get the type of a method using the stored signature
+getMethodType(obj, name) => JS('', '''(() => {
+ 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 $definiteFunctionType.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.
+classGetConstructorType(cls, name) => JS('', '''(() => {
+ 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 $definiteFunctionType.apply(null, parts);
+})()''');
+
+/// 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.
+///
+/// If the optional `f` argument is passed in, it will be used as the method.
+/// This supports cases like `super.foo` where we need to tear off the method
+/// from the superclass, not from the `obj` directly.
+/// TODO(leafp): Consider caching the tearoff on the object?
+bind(obj, name, f) => JS('', '''(() => {
+ if ($f === void 0) $f = $obj[$name];
+ $f = $f.bind($obj);
+ // TODO(jmesserly): track the function's signature on the function, instead
+ // of having to go back to the class?
+ let sig = $getMethodType($obj, $name);
+ $assert_(sig);
+ $tag($f, sig);
+ return $f;
+})()''');
+
+// Set up the method signature field on the constructor
+_setMethodSignature(f, sigF) => JS('', '''(() => {
+ $defineMemoizedGetter($f, $_methodSig, () => {
+ let sigObj = $sigF();
+ sigObj.__proto__ = $f.__proto__[$_methodSig];
+ return sigObj;
+ });
+})()''');
+
+// Set up the constructor signature field on the constructor
+_setConstructorSignature(f, sigF) =>
+ JS('', '$defineMemoizedGetter($f, $_constructorSig, $sigF)');
+
+// Set up the static signature field on the constructor
+_setStaticSignature(f, sigF) =>
+ JS('', '$defineMemoizedGetter($f, $_staticSig, $sigF)');
+
+// Set the lazily computed runtime type field on static methods
+_setStaticTypes(f, names) => JS('', '''(() => {
+ for (let name of $names) {
+ $tagMemoized($f[name], function() {
+ let parts = $f[$_staticSig][name];
+ return $definiteFunctionType.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.
+setSignature(f, signature) => JS('', '''(() => {
+ // TODO(ochafik): Deconstruct these when supported by Chrome.
+ 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);
+ $tagMemoized($f, () => $Type);
+})()''');
+
+hasMethod(obj, name) => JS('', '$getMethodType($obj, $name) !== void 0');
+
+///
+/// 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.
+///
+virtualField(subclass, fieldName) => JS('', '''(() => {
+ // 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; }
+ });
+})()''');
+
+///
+/// Given a class and an initializer method name, creates a constructor
+/// function with the same name. For example `new SomeClass.name(args)`.
+///
+defineNamedConstructor(clazz, name) => JS('', '''(() => {
+ 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 });
+})()''');
+
+final _extensionType = JS('', 'Symbol("extensionType")');
+
+final dartx = JS('', '{}');
+
+getExtensionSymbol(name) => JS('', '''(() => {
+ let sym = $dartx[$name];
+ if (!sym) $dartx[$name] = sym = Symbol('dartx.' + $name);
+ return sym;
+})()''');
+
+defineExtensionNames(names) => JS('', '$names.forEach($getExtensionSymbol)');
+
+///
+/// Copy symbols from the prototype of the source to destination.
+/// These are the only properties safe to copy onto an existing public
+/// JavaScript class.
+///
+registerExtension(jsType, dartExtType) => JS('', '''(() => {
+ 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 = $Object.prototype;
+ while (extProto !== dartObjProto && extProto !== jsProto) {
+ $copyTheseProperties(jsProto, extProto, $getOwnPropertySymbols(extProto));
+ extProto = extProto.__proto__;
+ }
+ let originalSigFn = $getOwnPropertyDescriptor($dartExtType, $_methodSig).get;
+ $assert_(originalSigFn);
+ $defineMemoizedGetter($jsType, $_methodSig, originalSigFn);
+})()''');
+
+///
+/// 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 one of these methods.
+defineExtensionMembers(type, methodNames) => JS('', '''(() => {
+ 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;
+ });
+})()''');
+
+canonicalMember(obj, name) => JS('', '''(() => {
+ if ($obj != null && $obj[$_extensionType]) return $dartx[$name];
+ // Check for certain names that we can't use in JS
+ if ($name == 'constructor' || $name == 'prototype') {
+ $name = '+' + $name;
+ }
+ return $name;
+})()''');
+
+/// Sets the type of `obj` to be `type`
+setType(obj, type) => JS('', '''(() => {
+ $obj.__proto__ = $type.prototype;
+ return $obj;
+})()''');
+
+/// Sets the element type of a list literal.
+list(obj, elementType) =>
+ JS('', '$setType($obj, ${getGenericClass(JSArray)}($elementType))');
+
+setBaseClass(derived, base) => JS('', '''(() => {
+ // Link the extension to the type it's extending as a base class.
+ $derived.prototype.__proto__ = $base.prototype;
+})()''');
« no previous file with comments | « tool/input_sdk/lib/js/dart2js/js_dart2js.dart ('k') | tool/input_sdk/private/errors.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698