| 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 |