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 /// |
11 | 11 |
12 // TODO(leafp): Consider splitting some of this out. | 12 // TODO(leafp): Consider splitting some of this out. |
13 part of dart._runtime; | 13 part of dart._runtime; |
14 | 14 |
15 /// | 15 /// |
16 /// Returns a new type that mixes members from base and all mixins. | 16 /// Returns a new type that mixes members from base and all mixins. |
17 /// | 17 /// |
18 /// Each mixin applies in sequence, with further to the right ones overriding | 18 /// Each mixin applies in sequence, with further to the right ones overriding |
19 /// previous entries. | 19 /// previous entries. |
20 /// | 20 /// |
21 /// For each mixin, we only take its own properties, not anything from its | 21 /// For each mixin, we only take its own properties, not anything from its |
22 /// superclass (prototype). | 22 /// superclass (prototype). |
23 mixin(base, @rest mixins) => JS( | 23 mixin(base, @rest mixins) => JS('', '''(() => { |
24 '', | |
25 '''(() => { | |
26 // Create an initializer for the mixin, so when derived constructor calls | 24 // Create an initializer for the mixin, so when derived constructor calls |
27 // super, we can correctly initialize base and mixins. | 25 // super, we can correctly initialize base and mixins. |
28 | 26 |
29 // Create a class that will hold all of the mixin methods. | 27 // Create a class that will hold all of the mixin methods. |
30 class Mixin extends $base {} | 28 class Mixin extends $base {} |
31 // Save the original constructor. For ClassTypeAlias definitions, this | 29 // Save the original constructor. For ClassTypeAlias definitions, this |
32 // is the concrete type. We embed metadata (e.g., implemented interfaces) | 30 // is the concrete type. We embed metadata (e.g., implemented interfaces) |
33 // on this constructor and need to access that from runtime instances. | 31 // on this constructor and need to access that from runtime instances. |
34 let constructor = Mixin.prototype.constructor; | 32 let constructor = Mixin.prototype.constructor; |
35 // Copy each mixin's methods, with later ones overwriting earlier entries. | 33 // Copy each mixin's methods, with later ones overwriting earlier entries. |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
106 clazz, _implements, clazz, _implements); | 104 clazz, _implements, clazz, _implements); |
107 | 105 |
108 /// The Symbol for storing type arguments on a specialized generic type. | 106 /// The Symbol for storing type arguments on a specialized generic type. |
109 final _typeArguments = JS('', 'Symbol("typeArguments")'); | 107 final _typeArguments = JS('', 'Symbol("typeArguments")'); |
110 | 108 |
111 final _originalDeclaration = JS('', 'Symbol("originalDeclaration")'); | 109 final _originalDeclaration = JS('', 'Symbol("originalDeclaration")'); |
112 | 110 |
113 final mixinNew = JS('', 'Symbol("dart.mixinNew")'); | 111 final mixinNew = JS('', 'Symbol("dart.mixinNew")'); |
114 | 112 |
115 /// Wrap a generic class builder function with future flattening. | 113 /// Wrap a generic class builder function with future flattening. |
116 flattenFutures(builder) => JS( | 114 flattenFutures(builder) => JS('', '''(() => { |
117 '', | |
118 '''(() => { | |
119 function flatten(T) { | 115 function flatten(T) { |
120 if (!T) return $builder($dynamic); | 116 if (!T) return $builder($dynamic); |
121 let futureClass = $getGenericClass($Future); | 117 let futureClass = $getGenericClass($Future); |
122 //TODO(leafp): This only handles the direct flattening case. | 118 //TODO(leafp): This only handles the direct flattening case. |
123 // It would probably be good to at least search up the class | 119 // It would probably be good to at least search up the class |
124 // hierarchy. If we keep doing flattening long term, we may | 120 // hierarchy. If we keep doing flattening long term, we may |
125 // want to implement the full future flattening per spec. | 121 // want to implement the full future flattening per spec. |
126 if ($getGenericClass(T) == futureClass) { | 122 if ($getGenericClass(T) == futureClass) { |
127 let args = $getGenericArgs(T); | 123 let args = $getGenericArgs(T); |
128 if (args) return $builder(args[0]); | 124 if (args) return $builder(args[0]); |
129 } | 125 } |
130 return $builder(T); | 126 return $builder(T); |
131 } | 127 } |
132 return flatten; | 128 return flatten; |
133 })()'''); | 129 })()'''); |
134 | 130 |
135 /// Memoize a generic type constructor function. | 131 /// Memoize a generic type constructor function. |
136 generic(typeConstructor, [setBaseClass]) => JS( | 132 generic(typeConstructor, [setBaseClass]) => JS('', '''(() => { |
137 '', | |
138 '''(() => { | |
139 let length = $typeConstructor.length; | 133 let length = $typeConstructor.length; |
140 if (length < 1) { | 134 if (length < 1) { |
141 $throwInternalError('must have at least one generic type argument'); | 135 $throwInternalError('must have at least one generic type argument'); |
142 } | 136 } |
143 let resultMap = new Map(); | 137 let resultMap = new Map(); |
144 function makeGenericType(...args) { | 138 function makeGenericType(...args) { |
145 if (args.length != length && args.length != 0) { | 139 if (args.length != length && args.length != 0) { |
146 $throwInternalError('requires ' + length + ' or 0 type arguments'); | 140 $throwInternalError('requires ' + length + ' or 0 type arguments'); |
147 } | 141 } |
148 while (args.length < length) args.push($dynamic); | 142 while (args.length < length) args.push($dynamic); |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
258 | 252 |
259 finalFieldType(type, metadata) => | 253 finalFieldType(type, metadata) => |
260 JS('', '{ type: #, isFinal: true, metadata: # }', type, metadata); | 254 JS('', '{ type: #, isFinal: true, metadata: # }', type, metadata); |
261 | 255 |
262 fieldType(type, metadata) => | 256 fieldType(type, metadata) => |
263 JS('', '{ type: #, isFinal: false, metadata: # }', type, metadata); | 257 JS('', '{ type: #, isFinal: false, metadata: # }', type, metadata); |
264 | 258 |
265 /// Get the type of a constructor from a class using the stored signature | 259 /// Get the type of a constructor from a class using the stored signature |
266 /// If name is undefined, returns the type of the default constructor | 260 /// If name is undefined, returns the type of the default constructor |
267 /// Returns undefined if the constructor is not found. | 261 /// Returns undefined if the constructor is not found. |
268 classGetConstructorType(cls, name) => JS( | 262 classGetConstructorType(cls, name) => JS('', '''(() => { |
269 '', | |
270 '''(() => { | |
271 if(!$name) $name = 'new'; | 263 if(!$name) $name = 'new'; |
272 if ($cls === void 0) return void 0; | 264 if ($cls === void 0) return void 0; |
273 if ($cls == null) return void 0; | 265 if ($cls == null) return void 0; |
274 let sigCtor = $cls[$_constructorSig]; | 266 let sigCtor = $cls[$_constructorSig]; |
275 if (sigCtor === void 0) return void 0; | 267 if (sigCtor === void 0) return void 0; |
276 return sigCtor[$name]; | 268 return sigCtor[$name]; |
277 })()'''); | 269 })()'''); |
278 | 270 |
279 // Set up the method signature field on the constructor | 271 // Set up the method signature field on the constructor |
280 _setInstanceSignature(f, sigF, kind) => defineMemoizedGetter( | 272 _setInstanceSignature(f, sigF, kind) => defineMemoizedGetter( |
(...skipping 29 matching lines...) Expand all Loading... |
310 _setStaticFieldSignature(f, sigF) => | 302 _setStaticFieldSignature(f, sigF) => |
311 JS('', '$defineMemoizedGetter($f, $_staticFieldSig, $sigF)'); | 303 JS('', '$defineMemoizedGetter($f, $_staticFieldSig, $sigF)'); |
312 | 304 |
313 _setStaticGetterSignature(f, sigF) => | 305 _setStaticGetterSignature(f, sigF) => |
314 JS('', '$defineMemoizedGetter($f, $_staticGetterSig, $sigF)'); | 306 JS('', '$defineMemoizedGetter($f, $_staticGetterSig, $sigF)'); |
315 | 307 |
316 _setStaticSetterSignature(f, sigF) => | 308 _setStaticSetterSignature(f, sigF) => |
317 JS('', '$defineMemoizedGetter($f, $_staticSetterSig, $sigF)'); | 309 JS('', '$defineMemoizedGetter($f, $_staticSetterSig, $sigF)'); |
318 | 310 |
319 // Set the lazily computed runtime type field on static methods | 311 // Set the lazily computed runtime type field on static methods |
320 _setStaticTypes(f, names) => JS( | 312 _setStaticTypes(f, names) => JS('', '''(() => { |
321 '', | |
322 '''(() => { | |
323 for (let name of $names) { | 313 for (let name of $names) { |
324 // TODO(vsm): Need to generate static methods. | 314 // TODO(vsm): Need to generate static methods. |
325 if (!$f[name]) continue; | 315 if (!$f[name]) continue; |
326 $tagLazy($f[name], function() { | 316 $tagLazy($f[name], function() { |
327 return $f[$_staticSig][name]; | 317 return $f[$_staticSig][name]; |
328 }) | 318 }) |
329 } | 319 } |
330 })()'''); | 320 })()'''); |
331 | 321 |
332 /// Set up the type signature of a class (constructor object) | 322 /// Set up the type signature of a class (constructor object) |
333 /// f is a constructor object | 323 /// f is a constructor object |
334 /// signature is an object containing optional properties as follows: | 324 /// signature is an object containing optional properties as follows: |
335 /// methods: A function returning an object mapping method names | 325 /// methods: A function returning an object mapping method names |
336 /// to method types. The function is evaluated lazily and cached. | 326 /// to method types. The function is evaluated lazily and cached. |
337 /// statics: A function returning an object mapping static method | 327 /// statics: A function returning an object mapping static method |
338 /// names to types. The function is evaluated lazily and cached. | 328 /// names to types. The function is evaluated lazily and cached. |
339 /// names: An array of the names of the static methods. Used to | 329 /// names: An array of the names of the static methods. Used to |
340 /// permit eagerly setting the runtimeType field on the methods | 330 /// permit eagerly setting the runtimeType field on the methods |
341 /// while still lazily computing the type descriptor object. | 331 /// while still lazily computing the type descriptor object. |
342 /// fields: A function returning an object mapping instance field | 332 /// fields: A function returning an object mapping instance field |
343 /// names to types. | 333 /// names to types. |
344 setSignature(f, signature) => JS( | 334 setSignature(f, signature) => JS('', '''(() => { |
345 '', | |
346 '''(() => { | |
347 // TODO(ochafik): Deconstruct these when supported by Chrome. | 335 // TODO(ochafik): Deconstruct these when supported by Chrome. |
348 let constructors = | 336 let constructors = |
349 ('constructors' in signature) ? signature.constructors : () => ({}); | 337 ('constructors' in signature) ? signature.constructors : () => ({}); |
350 let methods = | 338 let methods = |
351 ('methods' in signature) ? signature.methods : () => ({}); | 339 ('methods' in signature) ? signature.methods : () => ({}); |
352 let fields = | 340 let fields = |
353 ('fields' in signature) ? signature.fields : () => ({}); | 341 ('fields' in signature) ? signature.fields : () => ({}); |
354 let getters = | 342 let getters = |
355 ('getters' in signature) ? signature.getters : () => ({}); | 343 ('getters' in signature) ? signature.getters : () => ({}); |
356 let setters = | 344 let setters = |
(...skipping 13 matching lines...) Expand all Loading... |
370 $_setFieldSignature($f, fields); | 358 $_setFieldSignature($f, fields); |
371 $_setGetterSignature($f, getters); | 359 $_setGetterSignature($f, getters); |
372 $_setSetterSignature($f, setters); | 360 $_setSetterSignature($f, setters); |
373 $_setStaticSignature($f, statics); | 361 $_setStaticSignature($f, statics); |
374 $_setStaticFieldSignature($f, staticFields); | 362 $_setStaticFieldSignature($f, staticFields); |
375 $_setStaticGetterSignature($f, staticGetters); | 363 $_setStaticGetterSignature($f, staticGetters); |
376 $_setStaticSetterSignature($f, staticSetters); | 364 $_setStaticSetterSignature($f, staticSetters); |
377 $_setStaticTypes($f, names); | 365 $_setStaticTypes($f, names); |
378 })()'''); | 366 })()'''); |
379 | 367 |
380 bool _hasSigEntry(type, sigF, name) => JS( | 368 bool _hasSigEntry(type, sigF, name) => JS('bool', '''(() => { |
381 'bool', | |
382 '''(() => { | |
383 let sigObj = $type[$sigF]; | 369 let sigObj = $type[$sigF]; |
384 if (sigObj === void 0) return false; | 370 if (sigObj === void 0) return false; |
385 return $name in sigObj; | 371 return $name in sigObj; |
386 })()'''); | 372 })()'''); |
387 | 373 |
388 bool hasMethod(type, name) => _hasSigEntry(type, _methodSig, name); | 374 bool hasMethod(type, name) => _hasSigEntry(type, _methodSig, name); |
389 bool hasGetter(type, name) => _hasSigEntry(type, _getterSig, name); | 375 bool hasGetter(type, name) => _hasSigEntry(type, _getterSig, name); |
390 bool hasSetter(type, name) => _hasSigEntry(type, _setterSig, name); | 376 bool hasSetter(type, name) => _hasSigEntry(type, _setterSig, name); |
391 bool hasField(type, name) => _hasSigEntry(type, _fieldSig, name); | 377 bool hasField(type, name) => _hasSigEntry(type, _fieldSig, name); |
392 | 378 |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
434 var names = getOwnPropertyNames(coreObjProto); | 420 var names = getOwnPropertyNames(coreObjProto); |
435 for (int i = 0; i < JS('int', '#.length', names); ++i) { | 421 for (int i = 0; i < JS('int', '#.length', names); ++i) { |
436 var name = JS('', '#[#]', names, i); | 422 var name = JS('', '#[#]', names, i); |
437 var desc = getOwnPropertyDescriptor(coreObjProto, name); | 423 var desc = getOwnPropertyDescriptor(coreObjProto, name); |
438 defineProperty(jsProto, getExtensionSymbol(name), desc); | 424 defineProperty(jsProto, getExtensionSymbol(name), desc); |
439 } | 425 } |
440 } | 426 } |
441 | 427 |
442 final _extensionMap = JS('', 'new Map()'); | 428 final _extensionMap = JS('', 'new Map()'); |
443 | 429 |
444 _applyExtension(jsType, dartExtType) => JS( | 430 _applyExtension(jsType, dartExtType) => JS('', '''(() => { |
445 '', | |
446 '''(() => { | |
447 // TODO(vsm): Not all registered js types are real. | 431 // TODO(vsm): Not all registered js types are real. |
448 if (!$jsType) return; | 432 if (!$jsType) return; |
449 | 433 |
450 let jsProto = $jsType.prototype; | 434 let jsProto = $jsType.prototype; |
451 | 435 |
452 // TODO(vsm): This sometimes doesn't exist on FF. These types will be | 436 // TODO(vsm): This sometimes doesn't exist on FF. These types will be |
453 // broken. | 437 // broken. |
454 if (!jsProto) return; | 438 if (!jsProto) return; |
455 | 439 |
456 $_installProperties(jsProto, $dartExtType, jsProto[$_extensionType]); | 440 $_installProperties(jsProto, $dartExtType, jsProto[$_extensionType]); |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
498 /// | 482 /// |
499 /// Results in: | 483 /// Results in: |
500 /// | 484 /// |
501 /// MyType.prototype[dartx.add] = MyType.prototype.add; | 485 /// MyType.prototype[dartx.add] = MyType.prototype.add; |
502 /// MyType.prototype[dartx.remove] = MyType.prototype.remove; | 486 /// MyType.prototype[dartx.remove] = MyType.prototype.remove; |
503 /// | 487 /// |
504 // TODO(jmesserly): essentially this gives two names to the same method. | 488 // TODO(jmesserly): essentially this gives two names to the same method. |
505 // This benefit is roughly equivalent call performance either way, but the | 489 // This benefit is roughly equivalent call performance either way, but the |
506 // cost is we need to call defineExtensionMembers any time a subclass | 490 // cost is we need to call defineExtensionMembers any time a subclass |
507 // overrides one of these methods. | 491 // overrides one of these methods. |
508 defineExtensionMembers(type, methodNames) => JS( | 492 defineExtensionMembers(type, methodNames) => JS('', '''(() => { |
509 '', | |
510 '''(() => { | |
511 let proto = $type.prototype; | 493 let proto = $type.prototype; |
512 for (let name of $methodNames) { | 494 for (let name of $methodNames) { |
513 let method = $getOwnPropertyDescriptor(proto, name); | 495 let method = $getOwnPropertyDescriptor(proto, name); |
514 $defineProperty(proto, $getExtensionSymbol(name), method); | 496 $defineProperty(proto, $getExtensionSymbol(name), method); |
515 } | 497 } |
516 // Ensure the signature is available too. | 498 // Ensure the signature is available too. |
517 // TODO(jmesserly): not sure if we can do this in a cleaner way. Essentially | 499 // TODO(jmesserly): not sure if we can do this in a cleaner way. Essentially |
518 // we need to copy the signature (and in the future, other data like | 500 // we need to copy the signature (and in the future, other data like |
519 // annotations) any time we copy a method as part of our metaprogramming. | 501 // annotations) any time we copy a method as part of our metaprogramming. |
520 // It might be more friendly to JS metaprogramming if we include this info | 502 // It might be more friendly to JS metaprogramming if we include this info |
(...skipping 19 matching lines...) Expand all Loading... |
540 upgradeSig($_getterSig); | 522 upgradeSig($_getterSig); |
541 upgradeSig($_setterSig); | 523 upgradeSig($_setterSig); |
542 })()'''); | 524 })()'''); |
543 | 525 |
544 /// Sets the type of `obj` to be `type` | 526 /// Sets the type of `obj` to be `type` |
545 setType(obj, type) { | 527 setType(obj, type) { |
546 JS('', '#.__proto__ = #.prototype', obj, type); | 528 JS('', '#.__proto__ = #.prototype', obj, type); |
547 return obj; | 529 return obj; |
548 } | 530 } |
549 | 531 |
550 /// Sets the element type of a list literal. | |
551 list(obj, elementType) => | |
552 setType(obj, JS('', '#(#)', getGenericClass(JSArray), elementType)); | |
553 | |
554 /// Link the extension to the type it's extending as a base class. | 532 /// Link the extension to the type it's extending as a base class. |
555 setBaseClass(derived, base) { | 533 setBaseClass(derived, base) { |
556 JS('', '#.prototype.__proto__ = #.prototype', derived, base); | 534 JS('', '#.prototype.__proto__ = #.prototype', derived, base); |
557 // We use __proto__ to track the superclass hierarchy (see isSubtype). | 535 // We use __proto__ to track the superclass hierarchy (see isSubtype). |
558 JS('', '#.__proto__ = #', derived, base); | 536 JS('', '#.__proto__ = #', derived, base); |
559 } | 537 } |
560 | 538 |
561 /// Like [setBaseClass], but for generic extension types such as `JSArray<E>`. | 539 /// Like [setBaseClass], but for generic extension types such as `JSArray<E>`. |
562 setExtensionBaseClass(dartType, jsType) { | 540 setExtensionBaseClass(dartType, jsType) { |
563 // Mark the generic type as an extension type and link the prototype objects. | 541 // Mark the generic type as an extension type and link the prototype objects. |
564 var dartProto = JS('', '#.prototype', dartType); | 542 var dartProto = JS('', '#.prototype', dartType); |
565 JS('', '#[#] = #', dartProto, _extensionType, dartType); | 543 JS('', '#[#] = #', dartProto, _extensionType, dartType); |
566 JS('', '#.__proto__ = #.prototype', dartProto, jsType); | 544 JS('', '#.__proto__ = #.prototype', dartProto, jsType); |
567 } | 545 } |
568 | 546 |
569 defineEnumValues(enumClass, names) { | 547 defineEnumValues(enumClass, names) { |
570 var values = []; | 548 var values = []; |
571 for (var i = 0; i < JS('int', '#.length', names); i++) { | 549 for (var i = 0; i < JS('int', '#.length', names); i++) { |
572 var value = const_(JS('', 'new #.new(#)', enumClass, i)); | 550 var value = const_(JS('', 'new #.new(#)', enumClass, i)); |
573 JS('', '#.push(#)', values, value); | 551 JS('', '#.push(#)', values, value); |
574 defineValue(enumClass, JS('', '#[#]', names, i), value); | 552 defineValue(enumClass, JS('', '#[#]', names, i), value); |
575 } | 553 } |
576 JS('', '#.values = #', enumClass, constList(values, enumClass)); | 554 JS('', '#.values = #', enumClass, constList(values, enumClass)); |
577 } | 555 } |
OLD | NEW |