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

Side by Side 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: Eliminate top level libraries from loader 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 unified diff | Download patch
« no previous file with comments | « no previous file | lib/runtime/dart/_foreign_helper.js » ('j') | lib/runtime/dart_runtime.js » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 /* This library defines the operations that define and manipulate Dart
6 * classes. Included in this are:
7 * - Generics
8 * - Class metadata
9 * - Extension methods
10 */
11
12 // TODO(leafp): Consider splitting some of this out.
13 loader.library('dart/classes', null, /* Imports */[
vsm 2015/06/12 15:50:32 Perhaps rename 'dart/classes', etc., to 'dart/_cla
Leaf 2015/06/12 19:52:13 Done.
14 ], /* Lazy Imports */[
15 'dart/core',
16 'dart/dartx',
17 'dart/types',
18 'dart/rtti',
19 'dart/_interceptors'
20 ], function(exports, core, dartx, types, rtti, _interceptors) {
21 'use strict';
22
23 const assert = js_utils.assert;
24 const copyProperties = js_utils.copyProperties;
25 const copyTheseProperties = js_utils.copyTheseProperties;
26 const defineMemoizedGetter = js_utils.defineMemoizedGetter;
27 const defineProperty = js_utils.defineProperty;
28 const getOwnPropertyDescriptor = js_utils.getOwnPropertyDescriptor;
29 const getOwnPropertySymbols = js_utils.getOwnPropertySymbols;
30 const getOwnPropertyNames = js_utils.getOwnPropertyNames;
31 const safeGetOwnProperty = js_utils.safeGetOwnProperty;
32 const slice = js_utils.slice;
33 const throwRuntimeError = js_utils.throwRuntimeError;
34
35
36 /** The Symbol for storing type arguments on a specialized generic type. */
37 let _mixins = Symbol('mixins');
38 let _implements = Symbol('implements');
39 exports.implements = _implements;
40 let _metadata = Symbol('metadata');
41 exports.metadata = _metadata;
42
43 /**
44 * Returns a new type that mixes members from base and all mixins.
45 *
46 * Each mixin applies in sequence, with further to the right ones overriding
47 * previous entries.
48 *
49 * For each mixin, we only take its own properties, not anything from its
50 * superclass (prototype).
51 */
52 function mixin(base/*, ...mixins*/) {
53 // Create an initializer for the mixin, so when derived constructor calls
54 // super, we can correctly initialize base and mixins.
55 let mixins = slice.call(arguments, 1);
56
57 // Create a class that will hold all of the mixin methods.
58 class Mixin extends base {
59 // Initializer method: run mixin initializers, then the base.
60 [base.name](/*...args*/) {
61 // Run mixin initializers. They cannot have arguments.
62 // Run them backwards so most-derived mixin is initialized first.
63 for (let i = mixins.length - 1; i >= 0; i--) {
64 let mixin = mixins[i];
65 let init = mixin.prototype[mixin.name];
66 if (init) init.call(this);
67 }
68 // Run base initializer.
69 let init = base.prototype[base.name];
70 if (init) init.apply(this, arguments);
71 }
72 }
73 // Copy each mixin's methods, with later ones overwriting earlier entries.
74 for (let m of mixins) {
75 copyProperties(Mixin.prototype, m.prototype);
76 }
77
78 // Set the signature of the Mixin class to be the composition
79 // of the signatures of the mixins.
80 setSignature(Mixin, {
81 methods: () => {
82 let s = {};
83 for (let m of mixins) {
84 copyProperties(s, m[_methodSig]);
85 }
86 return s;
87 }
88 });
89
90 // Save mixins for reflection
91 Mixin[_mixins] = mixins;
92 return Mixin;
93 }
94 exports.mixin = mixin;
95
96 function getMixins (clazz) {
97 return clazz[_mixins];
98 }
99 exports.getMixins = getMixins;
100
101 function getImplements (clazz) {
102 return clazz[_implements];
103 }
104 exports.getImplements = getImplements;
105
106 /** The Symbol for storing type arguments on a specialized generic type. */
107 let _typeArguments = Symbol('typeArguments');
108 let _originalDeclaration = Symbol('originalDeclaration');
109
110 /** Memoize a generic type constructor function. */
111 function generic(typeConstructor) {
112 let length = typeConstructor.length;
113 if (length < 1) throwRuntimeError('must have at least one generic type argum ent');
114
115 let resultMap = new Map();
116 function makeGenericType(/*...arguments*/) {
117 if (arguments.length != length && arguments.length != 0) {
118 throwRuntimeError('requires ' + length + ' or 0 type arguments');
119 }
120 let args = slice.call(arguments);
121 // TODO(leafp): This should really be core.Object for
122 // consistency, but Object is not attached to core
123 // until the entire core library has been processed,
124 // which is too late.
125 while (args.length < length) args.push(types.dynamic);
126
127 let value = resultMap;
128 for (let i = 0; i < length; i++) {
129 let arg = args[i];
130 if (arg == null) {
131 throwRuntimeError('type arguments should not be null: ' + typeConstruc tor);
vsm 2015/06/12 15:50:32 nit: length
Leaf 2015/06/12 19:52:13 Done.
132 }
133 let map = value;
134 value = map.get(arg);
135 if (value === void 0) {
136 if (i + 1 == length) {
137 value = typeConstructor.apply(null, args);
138 // Save the type constructor and arguments for reflection.
139 if (value) {
140 value[_typeArguments] = args;
141 value[_originalDeclaration] = makeGenericType;
142 }
143 } else {
144 value = new Map();
145 }
146 map.set(arg, value);
147 }
148 }
149 return value;
150 }
151 return makeGenericType;
152 }
153 exports.generic = generic;
154
155 function getGenericClass(type) {
156 return safeGetOwnProperty(type, _originalDeclaration);
157 };
158 exports.getGenericClass = getGenericClass;
159
160 function getGenericArgs(type) {
161 return safeGetOwnProperty(type, _typeArguments);
162 };
163 exports.getGenericArgs = getGenericArgs;
164
165 let _constructorSig = Symbol('sigCtor');
166 let _methodSig = Symbol("sig");
167 let _staticSig = Symbol("sigStatic");
168
169 /// Get the type of a method using the stored signature
170 function _getMethodType(obj, name) {
171 if (obj === void 0) return void 0;
172 if (obj == null) return void 0;
173 let sigObj = obj.__proto__.constructor[_methodSig];
174 if (sigObj === void 0) return void 0;
175 let parts = sigObj[name];
176 if (parts === void 0) return void 0;
177 return types.functionType.apply(null, parts);
178 }
179
180 /// Get the type of a constructor from a class using the stored signature
181 /// If name is undefined, returns the type of the default constructor
182 /// Returns undefined if the constructor is not found.
183 function _getConstructorType(cls, name) {
184 if(!name) name = cls.name;
185 if (cls === void 0) return void 0;
186 if (cls == null) return void 0;
187 let sigCtor = cls[_constructorSig];
188 if (sigCtor === void 0) return void 0;
189 let parts = sigCtor[name];
190 if (parts === void 0) return void 0;
191 return types.functionType.apply(null, parts);
192 }
193 exports.classGetConstructorType = _getConstructorType;
194
195 /// Given an object and a method name, tear off the method.
196 /// Sets the runtime type of the torn off method appropriately,
197 /// and also binds the object.
198 /// TODO(leafp): Consider caching the tearoff on the object?
199 function bind(obj, name) {
200 let f = obj[name].bind(obj);
201 let sig = _getMethodType(obj, name);
202 assert(sig);
203 rtti.tag(f, sig);
204 return f;
205 }
206 exports.bind = bind;
207
208 // Set up the method signature field on the constructor
209 function _setMethodSignature(f, sigF) {
210 defineMemoizedGetter(f, _methodSig, () => {
211 let sigObj = sigF();
212 sigObj.__proto__ = f.__proto__[_methodSig];
213 return sigObj;
214 });
215 }
216
217 // Set up the constructor signature field on the constructor
218 function _setConstructorSignature(f, sigF) {
219 defineMemoizedGetter(f, _constructorSig, sigF);
220 }
221
222 // Set up the static signature field on the constructor
223 function _setStaticSignature(f, sigF) {
224 defineMemoizedGetter(f, _staticSig, sigF);
225 }
226
227 // Set the lazily computed runtime type field on static methods
228 function _setStaticTypes(f, names) {
229 for (let name of names) {
230 rtti.tagMemoized(f[name], function() {
231 let parts = f[_staticSig][name];
232 return types.functionType.apply(null, parts);
233 })
234 }
235 }
236
237 /// Set up the type signature of a class (constructor object)
238 /// f is a constructor object
239 /// signature is an object containing optional properties as follows:
240 /// methods: A function returning an object mapping method names
241 /// to method types. The function is evaluated lazily and cached.
242 /// statics: A function returning an object mapping static method
243 /// names to types. The function is evalutated lazily and cached.
244 /// names: An array of the names of the static methods. Used to
245 /// permit eagerly setting the runtimeType field on the methods
246 /// while still lazily computing the type descriptor object.
247 function setSignature(f, signature) {
248 let constructors =
249 ('constructors' in signature) ? signature.constructors : () => ({});
250 let methods =
251 ('methods' in signature) ? signature.methods : () => ({});
252 let statics =
253 ('statics' in signature) ? signature.statics : () => ({});
254 let names =
255 ('names' in signature) ? signature.names : [];
256 _setConstructorSignature(f, constructors);
257 _setMethodSignature(f, methods);
258 _setStaticSignature(f, statics);
259 _setStaticTypes(f, names);
260 rtti.tagMemoized(f, () => core.Type);
261 }
262 exports.setSignature = setSignature;
263
264 function hasMethod(obj, name) {
265 return _getMethodType(obj, name) !== void 0;
266 }
267 exports.hasMethod = hasMethod;
268
269 exports.getMethodType = _getMethodType;
270
271 /**
272 * This is called whenever a derived class needs to introduce a new field,
273 * shadowing a field or getter/setter pair on its parent.
274 *
275 * This is important because otherwise, trying to read or write the field
276 * would end up calling the getter or setter, and one of those might not even
277 * exist, resulting in a runtime error. Even if they did exist, that's the
278 * wrong behavior if a new field was declared.
279 */
280 function virtualField(subclass, fieldName) {
281 // If the field is already overridden, do nothing.
282 let prop = getOwnPropertyDescriptor(subclass.prototype, fieldName);
283 if (prop) return;
284
285 let symbol = Symbol(subclass.name + '.' + fieldName);
286 defineProperty(subclass.prototype, fieldName, {
287 get: function() { return this[symbol]; },
288 set: function(x) { this[symbol] = x; }
289 });
290 }
291 exports.virtualField = virtualField;
292
293 /**
294 * Given a class and an initializer method name, creates a constructor
295 * function with the same name. For example `new SomeClass.name(args)`.
296 */
297 function defineNamedConstructor(clazz, name) {
298 let proto = clazz.prototype;
299 let initMethod = proto[name];
300 let ctor = function() { return initMethod.apply(this, arguments); };
301 ctor.prototype = proto;
302 // Use defineProperty so we don't hit a property defined on Function,
303 // like `caller` and `arguments`.
304 defineProperty(clazz, name, { value: ctor, configurable: true });
305 }
306 exports.defineNamedConstructor = defineNamedConstructor;
307
308 let _extensionType = Symbol('extensionType');
309
310 function getExtensionSymbol(name) {
311 let sym = dartx[name];
312 if (!sym) dartx[name] = sym = Symbol('dartx.' + name);
313 return sym;
314 }
315
316 function defineExtensionNames(names) {
317 names.forEach(getExtensionSymbol);
318 }
319 exports.defineExtensionNames = defineExtensionNames;
320
321 /**
322 * Copy symbols from the prototype of the source to destination.
323 * These are the only properties safe to copy onto an existing public
324 * JavaScript class.
325 */
326 function registerExtension(jsType, dartExtType) {
327 let extProto = dartExtType.prototype;
328 let jsProto = jsType.prototype;
329
330 // Mark the JS type's instances so we can easily check for extensions.
331 assert(jsProto[_extensionType] === void 0);
332 jsProto[_extensionType] = extProto;
333
334 let dartObjProto = core.Object.prototype;
335 while (extProto !== dartObjProto && extProto !== jsProto) {
336 copyTheseProperties(jsProto, extProto, getOwnPropertySymbols(extProto));
337 extProto = extProto.__proto__;
338 }
339 }
340 exports.registerExtension = registerExtension;
341
342 /**
343 * Mark a concrete type as implementing extension methods.
344 * For example: `class MyIter implements Iterable`.
345 *
346 * This takes a list of names, which are the extension methods implemented.
347 * It will add a forwarder, so the extension method name redirects to the
348 * normal Dart method name. For example:
349 *
350 * defineExtensionMembers(MyType, ['add', 'remove']);
351 *
352 * Results in:
353 *
354 * MyType.prototype[dartx.add] = MyType.prototype.add;
355 * MyType.prototype[dartx.remove] = MyType.prototype.remove;
356 */
357 // TODO(jmesserly): essentially this gives two names to the same method.
358 // This benefit is roughly equivalent call performance either way, but the
359 // cost is we need to call defineExtensionMEmbers any time a subclass override s
vsm 2015/06/12 15:50:32 nit: line len && fix cap on MEmbers
Leaf 2015/06/12 19:52:13 Done.
360 // one of these methods.
361 function defineExtensionMembers(type, methodNames) {
362 let proto = type.prototype;
363 for (let name of methodNames) {
364 let method = getOwnPropertyDescriptor(proto, name);
365 defineProperty(proto, getExtensionSymbol(name), method);
366 }
367 // Ensure the signature is available too.
368 // TODO(jmesserly): not sure if we can do this in a cleaner way. Essentially
369 // we need to copy the signature (and in the future, other data like
370 // annotations) any time we copy a method as part of our metaprogramming.
371 // It might be more friendly to JS metaprogramming if we include this info
372 // on the function.
373 let originalSigFn = getOwnPropertyDescriptor(type, _methodSig).get;
374 defineMemoizedGetter(type, _methodSig, function() {
375 let sig = originalSigFn();
376 for (let name of methodNames) {
377 sig[getExtensionSymbol(name)] = sig[name];
378 }
379 return sig;
380 });
381 }
382 exports.defineExtensionMembers = defineExtensionMembers;
383
384 function canonicalMember(obj, name) {
385 if (obj[_extensionType]) return dartx[name];
386 return name;
387 }
388 exports.canonicalMember = canonicalMember;
389
390 // TODO(vsm): Rationalize these type methods. We're currently using the
391 // setType / proto scheme for nominal types (e.g., classes) and the
392 // setRuntimeType / field scheme for structural types (e.g., functions
vsm 2015/06/12 15:50:32 I think setRuntimeType is gone. Can you either re
Leaf 2015/06/12 19:52:13 Done.
393 // - and only in tests for now).
394 // See: https://github.com/dart-lang/dev_compiler/issues/172
395
396 /** Sets the type of `obj` to be `type` */
397 function setType(obj, type) {
398 obj.__proto__ = type.prototype;
399 return obj;
400 }
401 exports.setType = setType;
402
403 /** Sets the element type of a list literal. */
404 function list(obj, elementType) {
405 return setType(obj, _interceptors.JSArray$(elementType));
406 }
407 exports.list = list;
408
409 function setBaseClass(derived, base) {
410 // Link the extension to the type it's extending as a base class.
411 derived.prototype.__proto__ = base.prototype;
412 }
413 exports.setBaseClass = setBaseClass;
414
415 });
OLDNEW
« no previous file with comments | « no previous file | lib/runtime/dart/_foreign_helper.js » ('j') | lib/runtime/dart_runtime.js » ('J')

Powered by Google App Engine
This is Rietveld 408576698