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