OLD | NEW |
---|---|
(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 */[ | |
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; | |
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
| |
34 | |
35 | |
36 /** The Symbol for storing type arguments on a specialized generic type. */ | |
37 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.
| |
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'); | |
Jennifer Messerly
2015/06/12 17:32:25
long line
Leaf
2015/06/12 19:52:14
Done.
| |
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); | |
Jennifer Messerly
2015/06/12 17:32:25
here too
Leaf
2015/06/12 19:52:14
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 | |
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.
| |
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 | |
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 }); | |
OLD | NEW |