OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 /// This library defines the operations that define and manipulate Dart | 5 /// This library defines the operations that define and manipulate Dart |
6 /// classes. Included in this are: | 6 /// classes. Included in this are: |
7 /// - Generics | 7 /// - Generics |
8 /// - Class metadata | 8 /// - Class metadata |
9 /// - Extension methods | 9 /// - Extension methods |
10 /// | 10 /// |
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
179 _setConstructorSignature(f, sigF) => | 179 _setConstructorSignature(f, sigF) => |
180 JS('', '$defineMemoizedGetter($f, $_constructorSig, $sigF)'); | 180 JS('', '$defineMemoizedGetter($f, $_constructorSig, $sigF)'); |
181 | 181 |
182 // Set up the static signature field on the constructor | 182 // Set up the static signature field on the constructor |
183 _setStaticSignature(f, sigF) => | 183 _setStaticSignature(f, sigF) => |
184 JS('', '$defineMemoizedGetter($f, $_staticSig, $sigF)'); | 184 JS('', '$defineMemoizedGetter($f, $_staticSig, $sigF)'); |
185 | 185 |
186 // Set the lazily computed runtime type field on static methods | 186 // Set the lazily computed runtime type field on static methods |
187 _setStaticTypes(f, names) => JS('', '''(() => { | 187 _setStaticTypes(f, names) => JS('', '''(() => { |
188 for (let name of $names) { | 188 for (let name of $names) { |
| 189 // TODO(vsm): Need to generate static methods. |
| 190 if (!$f[name]) continue; |
189 $tagMemoized($f[name], function() { | 191 $tagMemoized($f[name], function() { |
190 let parts = $f[$_staticSig][name]; | 192 let parts = $f[$_staticSig][name]; |
191 return $definiteFunctionType.apply(null, parts); | 193 return $definiteFunctionType.apply(null, parts); |
192 }) | 194 }) |
193 } | 195 } |
194 })()'''); | 196 })()'''); |
195 | 197 |
196 /// Set up the type signature of a class (constructor object) | 198 /// Set up the type signature of a class (constructor object) |
197 /// f is a constructor object | 199 /// f is a constructor object |
198 /// signature is an object containing optional properties as follows: | 200 /// signature is an object containing optional properties as follows: |
(...skipping 30 matching lines...) Expand all Loading... |
229 /// This is important because otherwise, trying to read or write the field | 231 /// This is important because otherwise, trying to read or write the field |
230 /// would end up calling the getter or setter, and one of those might not even | 232 /// would end up calling the getter or setter, and one of those might not even |
231 /// exist, resulting in a runtime error. Even if they did exist, that's the | 233 /// exist, resulting in a runtime error. Even if they did exist, that's the |
232 /// wrong behavior if a new field was declared. | 234 /// wrong behavior if a new field was declared. |
233 /// | 235 /// |
234 virtualField(subclass, fieldName) => JS('', '''(() => { | 236 virtualField(subclass, fieldName) => JS('', '''(() => { |
235 // If the field is already overridden, do nothing. | 237 // If the field is already overridden, do nothing. |
236 let prop = $getOwnPropertyDescriptor($subclass.prototype, $fieldName); | 238 let prop = $getOwnPropertyDescriptor($subclass.prototype, $fieldName); |
237 if (prop) return; | 239 if (prop) return; |
238 | 240 |
239 let symbol = Symbol($subclass.name + '.' + $fieldName); | 241 let symbol = Symbol($subclass.name + '.' + $fieldName.toString()); |
240 $defineProperty($subclass.prototype, $fieldName, { | 242 $defineProperty($subclass.prototype, $fieldName, { |
241 get: function() { return this[symbol]; }, | 243 get: function() { return this[symbol]; }, |
242 set: function(x) { this[symbol] = x; } | 244 set: function(x) { this[symbol] = x; } |
243 }); | 245 }); |
244 })()'''); | 246 })()'''); |
245 | 247 |
246 /// | 248 /// |
247 /// Given a class and an initializer method name, creates a constructor | 249 /// Given a class and an initializer method name, creates a constructor |
248 /// function with the same name. For example `new SomeClass.name(args)`. | 250 /// function with the same name. For example `new SomeClass.name(args)`. |
249 /// | 251 /// |
250 defineNamedConstructor(clazz, name) => JS('', '''(() => { | 252 defineNamedConstructor(clazz, name) => JS('', '''(() => { |
251 let proto = $clazz.prototype; | 253 let proto = $clazz.prototype; |
252 let initMethod = proto[$name]; | 254 let initMethod = proto[$name]; |
253 let ctor = function() { return initMethod.apply(this, arguments); }; | 255 let ctor = function() { return initMethod.apply(this, arguments); }; |
254 ctor.prototype = proto; | 256 ctor.prototype = proto; |
255 // Use defineProperty so we don't hit a property defined on Function, | 257 // Use defineProperty so we don't hit a property defined on Function, |
256 // like `caller` and `arguments`. | 258 // like `caller` and `arguments`. |
257 $defineProperty($clazz, $name, { value: ctor, configurable: true }); | 259 $defineProperty($clazz, $name, { value: ctor, configurable: true }); |
258 })()'''); | 260 })()'''); |
259 | 261 |
260 final _extensionType = JS('', 'Symbol("extensionType")'); | 262 final _extensionType = JS('', 'Symbol("extensionType")'); |
261 | 263 |
262 getExtensionType(obj) => JS('', '$obj[$_extensionType]'); | 264 getExtensionType(obj) => JS('', '$obj[$_extensionType]'); |
263 | 265 |
264 final dartx = JS('', '{}'); | 266 final dartx = JS('', '{}'); |
265 | 267 |
266 getExtensionSymbol(name) => JS('', '''(() => { | 268 getExtensionSymbol(name) => JS('', '''(() => { |
267 let sym = $dartx[$name]; | 269 let sym = $dartx[$name]; |
268 if (!sym) $dartx[$name] = sym = Symbol('dartx.' + $name); | 270 if (!sym) $dartx[$name] = sym = Symbol('dartx.' + $name.toString()); |
269 return sym; | 271 return sym; |
270 })()'''); | 272 })()'''); |
271 | 273 |
272 defineExtensionNames(names) => JS('', '$names.forEach($getExtensionSymbol)'); | 274 defineExtensionNames(names) => JS('', '$names.forEach($getExtensionSymbol)'); |
273 | 275 |
274 // Install properties in prototype order. Properties / descriptors from | 276 // Install properties in prototype order. Properties / descriptors from |
275 // more specific types should overwrite ones from less specific types. | 277 // more specific types should overwrite ones from less specific types. |
276 _installProperties(jsProto, extProto) => JS('', '''(() => { | 278 _installProperties(jsProto, extProto) => JS('', '''(() => { |
277 if (extProto !== $Object.prototype && extProto !== jsProto) { | 279 if (extProto !== $Object.prototype && extProto !== jsProto) { |
278 $_installProperties(jsProto, extProto.__proto__); | 280 $_installProperties(jsProto, extProto.__proto__); |
279 } | 281 } |
280 $copyTheseProperties(jsProto, extProto, $getOwnPropertySymbols(extProto)); | 282 $copyTheseProperties(jsProto, extProto, $getOwnPropertySymbols(extProto)); |
281 })()'''); | 283 })()'''); |
282 | 284 |
283 /// | 285 /// |
284 /// Copy symbols from the prototype of the source to destination. | 286 /// Copy symbols from the prototype of the source to destination. |
285 /// These are the only properties safe to copy onto an existing public | 287 /// These are the only properties safe to copy onto an existing public |
286 /// JavaScript class. | 288 /// JavaScript class. |
287 /// | 289 /// |
288 registerExtension(jsType, dartExtType) => JS('', '''(() => { | 290 registerExtension(jsType, dartExtType) => JS('', '''(() => { |
| 291 // TODO(vsm): Not all registered js types are real. |
| 292 if (!jsType) return; |
| 293 |
289 let extProto = $dartExtType.prototype; | 294 let extProto = $dartExtType.prototype; |
290 let jsProto = $jsType.prototype; | 295 let jsProto = $jsType.prototype; |
291 | 296 |
292 // Mark the JS type's instances so we can easily check for extensions. | 297 // Mark the JS type's instances so we can easily check for extensions. |
293 $assert_(jsProto[$_extensionType] === void 0); | |
294 jsProto[$_extensionType] = $dartExtType; | 298 jsProto[$_extensionType] = $dartExtType; |
295 $_installProperties(jsProto, extProto); | 299 $_installProperties(jsProto, extProto); |
296 let originalSigFn = $getOwnPropertyDescriptor($dartExtType, $_methodSig).get; | 300 let originalSigFn = $getOwnPropertyDescriptor($dartExtType, $_methodSig).get; |
297 $assert_(originalSigFn); | 301 $assert_(originalSigFn); |
298 $defineMemoizedGetter($jsType, $_methodSig, originalSigFn); | 302 $defineMemoizedGetter($jsType, $_methodSig, originalSigFn); |
299 })()'''); | 303 })()'''); |
300 | 304 |
301 /// | 305 /// |
302 /// Mark a concrete type as implementing extension methods. | 306 /// Mark a concrete type as implementing extension methods. |
303 /// For example: `class MyIter implements Iterable`. | 307 /// For example: `class MyIter implements Iterable`. |
(...skipping 10 matching lines...) Expand all Loading... |
314 /// MyType.prototype[dartx.remove] = MyType.prototype.remove; | 318 /// MyType.prototype[dartx.remove] = MyType.prototype.remove; |
315 /// | 319 /// |
316 // TODO(jmesserly): essentially this gives two names to the same method. | 320 // TODO(jmesserly): essentially this gives two names to the same method. |
317 // This benefit is roughly equivalent call performance either way, but the | 321 // This benefit is roughly equivalent call performance either way, but the |
318 // cost is we need to call defineExtensionMembers any time a subclass | 322 // cost is we need to call defineExtensionMembers any time a subclass |
319 // overrides one of these methods. | 323 // overrides one of these methods. |
320 defineExtensionMembers(type, methodNames) => JS('', '''(() => { | 324 defineExtensionMembers(type, methodNames) => JS('', '''(() => { |
321 let proto = $type.prototype; | 325 let proto = $type.prototype; |
322 for (let name of $methodNames) { | 326 for (let name of $methodNames) { |
323 let method = $getOwnPropertyDescriptor(proto, name); | 327 let method = $getOwnPropertyDescriptor(proto, name); |
| 328 // TODO(vsm): We should be able to generate code to avoid this case. |
| 329 // The method may be null if this type implements a potentially native |
| 330 // interface but isn't native itself. For a field on this type, we're not |
| 331 // generating a corresponding getter/setter method - it's just a field. |
| 332 if (!method) continue; |
324 $defineProperty(proto, $getExtensionSymbol(name), method); | 333 $defineProperty(proto, $getExtensionSymbol(name), method); |
325 } | 334 } |
326 // Ensure the signature is available too. | 335 // Ensure the signature is available too. |
327 // TODO(jmesserly): not sure if we can do this in a cleaner way. Essentially | 336 // TODO(jmesserly): not sure if we can do this in a cleaner way. Essentially |
328 // we need to copy the signature (and in the future, other data like | 337 // we need to copy the signature (and in the future, other data like |
329 // annotations) any time we copy a method as part of our metaprogramming. | 338 // annotations) any time we copy a method as part of our metaprogramming. |
330 // It might be more friendly to JS metaprogramming if we include this info | 339 // It might be more friendly to JS metaprogramming if we include this info |
331 // on the function. | 340 // on the function. |
332 let originalSigFn = $getOwnPropertyDescriptor($type, $_methodSig).get; | 341 let originalSigFn = $getOwnPropertyDescriptor($type, $_methodSig).get; |
333 $defineMemoizedGetter(type, $_methodSig, function() { | 342 $defineMemoizedGetter(type, $_methodSig, function() { |
(...skipping 24 matching lines...) Expand all Loading... |
358 })()'''); | 367 })()'''); |
359 | 368 |
360 /// Sets the element type of a list literal. | 369 /// Sets the element type of a list literal. |
361 list(obj, elementType) => | 370 list(obj, elementType) => |
362 JS('', '$setType($obj, ${getGenericClass(JSArray)}($elementType))'); | 371 JS('', '$setType($obj, ${getGenericClass(JSArray)}($elementType))'); |
363 | 372 |
364 setBaseClass(derived, base) => JS('', '''(() => { | 373 setBaseClass(derived, base) => JS('', '''(() => { |
365 // Link the extension to the type it's extending as a base class. | 374 // Link the extension to the type it's extending as a base class. |
366 $derived.prototype.__proto__ = $base.prototype; | 375 $derived.prototype.__proto__ = $base.prototype; |
367 })()'''); | 376 })()'''); |
OLD | NEW |