| // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| /// This library defines the operations that define and manipulate Dart |
| /// classes. Included in this are: |
| /// - Generics |
| /// - Class metadata |
| /// - Extension methods |
| /// |
| |
| // TODO(leafp): Consider splitting some of this out. |
| part of dart._runtime; |
| |
| /// |
| /// Returns a new type that mixes members from base and all mixins. |
| /// |
| /// Each mixin applies in sequence, with further to the right ones overriding |
| /// previous entries. |
| /// |
| /// For each mixin, we only take its own properties, not anything from its |
| /// superclass (prototype). |
| /// |
| mixin(base, @rest mixins) => JS( |
| '', |
| '''(() => { |
| // Create an initializer for the mixin, so when derived constructor calls |
| // super, we can correctly initialize base and mixins. |
| |
| // Create a class that will hold all of the mixin methods. |
| class Mixin extends $base {} |
| // Copy each mixin's methods, with later ones overwriting earlier entries. |
| for (let m of $mixins) { |
| $copyProperties(Mixin.prototype, m.prototype); |
| } |
| // Initializer method: run mixin initializers, then the base. |
| Mixin.prototype.new = function(...args) { |
| // Run mixin initializers. They cannot have arguments. |
| // Run them backwards so most-derived mixin is initialized first. |
| for (let i = $mixins.length - 1; i >= 0; i--) { |
| $mixins[i].prototype.new.call(this); |
| } |
| // Run base initializer. |
| $base.prototype.new.apply(this, args); |
| }; |
| |
| // Set the signature of the Mixin class to be the composition |
| // of the signatures of the mixins. |
| $setSignature(Mixin, { |
| methods: () => { |
| let s = {}; |
| for (let m of $mixins) { |
| if (m[$_methodSig]) $copyProperties(s, m[$_methodSig]); |
| } |
| return s; |
| }, |
| fields: () => { |
| let s = {}; |
| for (let m of $mixins) { |
| if (m[$_fieldSig]) $copyProperties(s, m[$_fieldSig]); |
| } |
| return s; |
| }, |
| getters: () => { |
| let s = {}; |
| for (let m of $mixins) { |
| if (m[$_getterSig]) $copyProperties(s, m[$_getterSig]); |
| } |
| return s; |
| }, |
| setters: () => { |
| let s = {}; |
| for (let m of $mixins) { |
| if (m[$_setterSig]) $copyProperties(s, m[$_setterSig]); |
| } |
| return s; |
| } |
| }); |
| |
| // Save mixins for reflection |
| Mixin[$_mixins] = $mixins; |
| return Mixin; |
| })()'''); |
| |
| /// The Symbol for storing type arguments on a specialized generic type. |
| final _mixins = JS('', 'Symbol("mixins")'); |
| |
| getMixins(clazz) => JS('', 'Object.hasOwnProperty.call(#, #) ? #[#] : null', |
| clazz, _mixins, clazz, _mixins); |
| |
| @JSExportName('implements') |
| final _implements = JS('', 'Symbol("implements")'); |
| |
| getImplements(clazz) => JS('', 'Object.hasOwnProperty.call(#, #) ? #[#] : null', |
| clazz, _implements, clazz, _implements); |
| |
| /// The Symbol for storing type arguments on a specialized generic type. |
| final _typeArguments = JS('', 'Symbol("typeArguments")'); |
| |
| final _originalDeclaration = JS('', 'Symbol("originalDeclaration")'); |
| |
| /// Wrap a generic class builder function with future flattening. |
| flattenFutures(builder) => JS( |
| '', |
| '''(() => { |
| function flatten(T) { |
| if (!T) return $builder($dynamic); |
| let futureClass = $getGenericClass($Future); |
| //TODO(leafp): This only handles the direct flattening case. |
| // It would probably be good to at least search up the class |
| // hierarchy. If we keep doing flattening long term, we may |
| // want to implement the full future flattening per spec. |
| if ($getGenericClass(T) == futureClass) { |
| let args = $getGenericArgs(T); |
| if (args) return $builder(args[0]); |
| } |
| return $builder(T); |
| } |
| return flatten; |
| })()'''); |
| |
| /// Memoize a generic type constructor function. |
| generic(typeConstructor, [setBaseClass]) => JS( |
| '', |
| '''(() => { |
| let length = $typeConstructor.length; |
| if (length < 1) { |
| $throwInternalError('must have at least one generic type argument'); |
| } |
| let resultMap = new Map(); |
| function makeGenericType(...args) { |
| if (args.length != length && args.length != 0) { |
| $throwInternalError('requires ' + length + ' or 0 type arguments'); |
| } |
| while (args.length < length) args.push($dynamic); |
| |
| let value = resultMap; |
| for (let i = 0; i < length; i++) { |
| let arg = args[i]; |
| if (arg == null) { |
| $throwInternalError('type arguments should not be null: ' |
| + $typeConstructor); |
| } |
| let map = value; |
| value = map.get(arg); |
| if (value === void 0) { |
| if (i + 1 == length) { |
| value = $typeConstructor.apply(null, args); |
| // Save the type constructor and arguments for reflection. |
| if (value) { |
| value[$_typeArguments] = args; |
| value[$_originalDeclaration] = makeGenericType; |
| } |
| map.set(arg, value); |
| if ($setBaseClass) $setBaseClass(value); |
| } else { |
| value = new Map(); |
| map.set(arg, value); |
| } |
| } |
| } |
| return value; |
| } |
| makeGenericType[$_genericTypeCtor] = $typeConstructor; |
| return makeGenericType; |
| })()'''); |
| |
| getGenericClass(type) => |
| JS('', '$safeGetOwnProperty($type, $_originalDeclaration)'); |
| |
| getGenericArgs(type) => JS('', '$safeGetOwnProperty($type, $_typeArguments)'); |
| |
| // TODO(vsm): Collapse into one expando. |
| final _constructorSig = JS('', 'Symbol("sigCtor")'); |
| final _methodSig = JS('', 'Symbol("sigMethod")'); |
| final _fieldSig = JS('', 'Symbol("sigField")'); |
| final _getterSig = JS('', 'Symbol("sigGetter")'); |
| final _setterSig = JS('', 'Symbol("sigSetter")'); |
| final _staticSig = JS('', 'Symbol("sigStaticMethod")'); |
| final _staticFieldSig = JS('', 'Symbol("sigStaticField")'); |
| final _staticGetterSig = JS('', 'Symbol("sigStaticGetter")'); |
| final _staticSetterSig = JS('', 'Symbol("sigStaticSetter")'); |
| final _genericTypeCtor = JS('', 'Symbol("genericType")'); |
| |
| // TODO(vsm): Collapse this as well - just provide a dart map to mirrors code. |
| // These are queried by mirrors code. |
| getConstructorSig(value) => JS('', '#[#]', value, _constructorSig); |
| getMethodSig(value) => JS('', '#[#]', value, _methodSig); |
| getFieldSig(value) => JS('', '#[#]', value, _fieldSig); |
| getGetterSig(value) => JS('', '#[#]', value, _getterSig); |
| getSetterSig(value) => JS('', '#[#]', value, _setterSig); |
| getStaticSig(value) => JS('', '#[#]', value, _staticSig); |
| getStaticFieldSig(value) => JS('', '#[#]', value, _staticFieldSig); |
| getStaticGetterSig(value) => JS('', '#[#]', value, _staticGetterSig); |
| getStaticSetterSig(value) => JS('', '#[#]', value, _staticSetterSig); |
| |
| getGenericTypeCtor(value) => JS('', '#[#]', value, _genericTypeCtor); |
| |
| /// Get the type of a method from an object using the stored signature |
| getType(obj) => JS( |
| '', |
| '''(() => { |
| return $obj == null ? $Object : $obj.__proto__.constructor; |
| })()'''); |
| |
| bool isJsInterop(obj) { |
| if (JS('bool', 'typeof # === "function"', obj)) { |
| // A function is a Dart function if it has runtime type information. |
| return _getRuntimeType(obj) == null; |
| } |
| // Primitive types are not JS interop types. |
| if (JS('bool', 'typeof # !== "object"', obj)) return false; |
| |
| // Extension types are not considered JS interop types. |
| // Note that it is still possible to call typed JS interop methods on |
| // extension types but the calls must be statically typed. |
| if (getExtensionType(obj) != null) return false; |
| return JS('bool', '!($obj instanceof $Object)'); |
| } |
| |
| /// Get the type of a method from a type using the stored signature |
| getMethodType(type, name) => JS( |
| '', |
| '''(() => { |
| let sigObj = $type[$_methodSig]; |
| if (sigObj === void 0) return void 0; |
| return sigObj[$name]; |
| })()'''); |
| |
| getFieldType(type, name) => JS( |
| '', |
| '''(() => { |
| let sigObj = $type[$_fieldSig]; |
| if (sigObj === void 0) return void 0; |
| let fieldType = sigObj[$name]; |
| // workaround to handle metadata. |
| return (fieldType instanceof Array) ? fieldType[0] : fieldType; |
| })()'''); |
| |
| getSetterType(type, name) => JS( |
| '', |
| '''(() => { |
| let sigObj = $type[$_setterSig]; |
| if (sigObj === void 0) return void 0; |
| return sigObj[$name]; |
| })()'''); |
| |
| /// Get the type of a constructor from a class using the stored signature |
| /// If name is undefined, returns the type of the default constructor |
| /// Returns undefined if the constructor is not found. |
| classGetConstructorType(cls, name) => JS( |
| '', |
| '''(() => { |
| if(!$name) $name = 'new'; |
| if ($cls === void 0) return void 0; |
| if ($cls == null) return void 0; |
| let sigCtor = $cls[$_constructorSig]; |
| if (sigCtor === void 0) return void 0; |
| return sigCtor[$name]; |
| })()'''); |
| |
| /// Given an object and a method name, tear off the method. |
| /// Sets the runtime type of the torn off method appropriately, |
| /// and also binds the object. |
| /// |
| /// If the optional `f` argument is passed in, it will be used as the method. |
| /// This supports cases like `super.foo` where we need to tear off the method |
| /// from the superclass, not from the `obj` directly. |
| /// TODO(leafp): Consider caching the tearoff on the object? |
| bind(obj, name, f) => JS( |
| '', |
| '''(() => { |
| if ($f === void 0) $f = $obj[$name]; |
| $f = $f.bind($obj); |
| // TODO(jmesserly): track the function's signature on the function, instead |
| // of having to go back to the class? |
| let sig = $getMethodType($getType($obj), $name); |
| $assert_(sig); |
| $tag($f, sig); |
| return $f; |
| })()'''); |
| |
| /// Instantiate a generic method. |
| /// |
| /// We need to apply the type arguments both to the function, as well as its |
| /// associated function type. |
| gbind(f, @rest typeArgs) { |
| var result = JS('', '#.apply(null, #)', f, typeArgs); |
| var sig = JS('', '#.apply(null, #)', _getRuntimeType(f), typeArgs); |
| tag(result, sig); |
| return result; |
| } |
| |
| // Set up the method signature field on the constructor |
| _setInstanceSignature(f, sigF, kind) => JS( |
| '', |
| '''(() => { |
| $defineMemoizedGetter($f, $kind, () => { |
| let sigObj = $sigF(); |
| let proto = $f.__proto__; |
| // We need to set the root proto to null not undefined. |
| sigObj.__proto__ = ($kind in proto) ? proto[$kind] : null; |
| return sigObj; |
| }); |
| })()'''); |
| |
| _setMethodSignature(f, sigF) => _setInstanceSignature(f, sigF, _methodSig); |
| _setFieldSignature(f, sigF) => _setInstanceSignature(f, sigF, _fieldSig); |
| _setGetterSignature(f, sigF) => _setInstanceSignature(f, sigF, _getterSig); |
| _setSetterSignature(f, sigF) => _setInstanceSignature(f, sigF, _setterSig); |
| |
| // Set up the constructor signature field on the constructor |
| _setConstructorSignature(f, sigF) => |
| JS('', '$defineMemoizedGetter($f, $_constructorSig, $sigF)'); |
| |
| // Set up the static signature field on the constructor |
| _setStaticSignature(f, sigF) => |
| JS('', '$defineMemoizedGetter($f, $_staticSig, $sigF)'); |
| |
| _setStaticFieldSignature(f, sigF) => |
| JS('', '$defineMemoizedGetter($f, $_staticFieldSig, $sigF)'); |
| |
| _setStaticGetterSignature(f, sigF) => |
| JS('', '$defineMemoizedGetter($f, $_staticGetterSig, $sigF)'); |
| |
| _setStaticSetterSignature(f, sigF) => |
| JS('', '$defineMemoizedGetter($f, $_staticSetterSig, $sigF)'); |
| |
| // Set the lazily computed runtime type field on static methods |
| _setStaticTypes(f, names) => JS( |
| '', |
| '''(() => { |
| for (let name of $names) { |
| // TODO(vsm): Need to generate static methods. |
| if (!$f[name]) continue; |
| $tagLazy($f[name], function() { |
| return $f[$_staticSig][name]; |
| }) |
| } |
| })()'''); |
| |
| /// Set up the type signature of a class (constructor object) |
| /// f is a constructor object |
| /// signature is an object containing optional properties as follows: |
| /// methods: A function returning an object mapping method names |
| /// to method types. The function is evaluated lazily and cached. |
| /// statics: A function returning an object mapping static method |
| /// names to types. The function is evalutated lazily and cached. |
| /// names: An array of the names of the static methods. Used to |
| /// permit eagerly setting the runtimeType field on the methods |
| /// while still lazily computing the type descriptor object. |
| /// fields: A function returning an object mapping instance field |
| /// names to types. |
| setSignature(f, signature) => JS( |
| '', |
| '''(() => { |
| // TODO(ochafik): Deconstruct these when supported by Chrome. |
| let constructors = |
| ('constructors' in signature) ? signature.constructors : () => ({}); |
| let methods = |
| ('methods' in signature) ? signature.methods : () => ({}); |
| let fields = |
| ('fields' in signature) ? signature.fields : () => ({}); |
| let getters = |
| ('getters' in signature) ? signature.getters : () => ({}); |
| let setters = |
| ('setters' in signature) ? signature.setters : () => ({}); |
| let statics = |
| ('statics' in signature) ? signature.statics : () => ({}); |
| let staticFields = |
| ('sfields' in signature) ? signature.sfields : () => ({}); |
| let staticGetters = |
| ('sgetters' in signature) ? signature.sgetters : () => ({}); |
| let staticSetters = |
| ('ssetters' in signature) ? signature.ssetters : () => ({}); |
| let names = |
| ('names' in signature) ? signature.names : []; |
| $_setConstructorSignature($f, constructors); |
| $_setMethodSignature($f, methods); |
| $_setFieldSignature($f, fields); |
| $_setGetterSignature($f, getters); |
| $_setSetterSignature($f, setters); |
| $_setStaticSignature($f, statics); |
| $_setStaticFieldSignature($f, staticFields); |
| $_setStaticGetterSignature($f, staticGetters); |
| $_setStaticSetterSignature($f, staticSetters); |
| $_setStaticTypes($f, names); |
| })()'''); |
| |
| _hasSigEntry(type, sigF, name) => JS( |
| '', |
| '''(() => { |
| let sigObj = $type[$sigF]; |
| if (sigObj === void 0) return false; |
| return $name in sigObj; |
| })()'''); |
| |
| hasMethod(type, name) => _hasSigEntry(type, _methodSig, name); |
| hasGetter(type, name) => _hasSigEntry(type, _getterSig, name); |
| hasSetter(type, name) => _hasSigEntry(type, _setterSig, name); |
| hasField(type, name) => _hasSigEntry(type, _fieldSig, name); |
| |
| /// Given a class and an initializer method name, creates a constructor |
| /// function with the same name. |
| /// |
| /// After we define the named constructor, the class can be constructed with |
| /// `new SomeClass.name(args)`. |
| defineNamedConstructor(clazz, name) => JS( |
| '', |
| '''(() => { |
| let proto = $clazz.prototype; |
| let initMethod = proto[$name]; |
| let ctor = function(...args) { initMethod.apply(this, args); }; |
| ctor[$isNamedConstructor] = true; |
| ctor.prototype = proto; |
| // Use defineProperty so we don't hit a property defined on Function, |
| // like `caller` and `arguments`. |
| $defineProperty($clazz, $name, { value: ctor, configurable: true }); |
| })()'''); |
| |
| final _extensionType = JS('', 'Symbol("extensionType")'); |
| |
| getExtensionType(obj) => JS('', '#[#]', obj, _extensionType); |
| |
| final dartx = JS('', 'dartx'); |
| |
| getExtensionSymbol(name) { |
| var sym = JS('', 'dartx[#]', name); |
| if (sym == null) { |
| sym = JS('', 'Symbol("dartx." + #.toString())', name); |
| JS('', 'dartx[#] = #', name, sym); |
| } |
| return sym; |
| } |
| |
| defineExtensionNames(names) => |
| JS('', '#.forEach(#)', names, getExtensionSymbol); |
| |
| /// Install properties in prototype-first order. Properties / descriptors from |
| /// more specific types should overwrite ones from less specific types. |
| void _installProperties(jsProto, extProto) { |
| // This relies on the Dart type literal evaluating to the JavaScript |
| // constructor. |
| var coreObjProto = JS('', '#.prototype', Object); |
| |
| var parentsExtension = JS('', '(#.__proto__)[#]', jsProto, _extensionType); |
| var installedParent = |
| JS('', '# && #.prototype', parentsExtension, parentsExtension); |
| |
| _installProperties2(jsProto, extProto, coreObjProto, installedParent); |
| } |
| |
| void _installProperties2(jsProto, extProto, coreObjProto, installedParent) { |
| if (JS('bool', '# === #', extProto, coreObjProto)) { |
| _installPropertiesForObject(jsProto, coreObjProto); |
| return; |
| } |
| if (JS('bool', '# !== #', jsProto, extProto)) { |
| var extParent = JS('', '#.__proto__', extProto); |
| |
| // If the extension methods of the parent have been installed on the parent |
| // of [jsProto], the methods will be available via prototype inheritance. |
| |
| if (JS('bool', '# !== #', installedParent, extParent)) { |
| _installProperties2(jsProto, extParent, coreObjProto, installedParent); |
| } |
| } |
| copyTheseProperties(jsProto, extProto, getOwnPropertySymbols(extProto)); |
| } |
| |
| void _installPropertiesForObject(jsProto, coreObjProto) { |
| // core.Object members need to be copied from the non-symbol name to the |
| // symbol name. |
| var names = getOwnPropertyNames(coreObjProto); |
| for (int i = 0; i < JS('int', '#.length', names); ++i) { |
| var name = JS('', '#[#]', names, i); |
| var desc = getOwnPropertyDescriptor(coreObjProto, name); |
| defineProperty(jsProto, getExtensionSymbol(name), desc); |
| } |
| return; |
| } |
| |
| /// Copy symbols from the prototype of the source to destination. |
| /// These are the only properties safe to copy onto an existing public |
| /// JavaScript class. |
| registerExtension(jsType, dartExtType) => JS( |
| '', |
| '''(() => { |
| // TODO(vsm): Not all registered js types are real. |
| if (!jsType) return; |
| |
| let extProto = $dartExtType.prototype; |
| let jsProto = $jsType.prototype; |
| |
| // TODO(vsm): This sometimes doesn't exist on FF. These types will be |
| // broken. |
| if (!jsProto) return; |
| |
| // Mark the JS type's instances so we can easily check for extensions. |
| jsProto[$_extensionType] = $dartExtType; |
| $_installProperties(jsProto, extProto); |
| function updateSig(sigF) { |
| let originalDesc = $getOwnPropertyDescriptor($dartExtType, sigF); |
| if (originalDesc === void 0) return; |
| let originalSigFn = originalDesc.get; |
| $assert_(originalSigFn); |
| $defineMemoizedGetter($jsType, sigF, originalSigFn); |
| } |
| updateSig($_methodSig); |
| updateSig($_fieldSig); |
| updateSig($_getterSig); |
| updateSig($_setterSig); |
| })()'''); |
| |
| /// |
| /// Mark a concrete type as implementing extension methods. |
| /// For example: `class MyIter implements Iterable`. |
| /// |
| /// This takes a list of names, which are the extension methods implemented. |
| /// It will add a forwarder, so the extension method name redirects to the |
| /// normal Dart method name. For example: |
| /// |
| /// defineExtensionMembers(MyType, ['add', 'remove']); |
| /// |
| /// Results in: |
| /// |
| /// MyType.prototype[dartx.add] = MyType.prototype.add; |
| /// MyType.prototype[dartx.remove] = MyType.prototype.remove; |
| /// |
| // TODO(jmesserly): essentially this gives two names to the same method. |
| // This benefit is roughly equivalent call performance either way, but the |
| // cost is we need to call defineExtensionMembers any time a subclass |
| // overrides one of these methods. |
| defineExtensionMembers(type, methodNames) => JS( |
| '', |
| '''(() => { |
| let proto = $type.prototype; |
| for (let name of $methodNames) { |
| let method = $getOwnPropertyDescriptor(proto, name); |
| $defineProperty(proto, $getExtensionSymbol(name), method); |
| } |
| // Ensure the signature is available too. |
| // TODO(jmesserly): not sure if we can do this in a cleaner way. Essentially |
| // we need to copy the signature (and in the future, other data like |
| // annotations) any time we copy a method as part of our metaprogramming. |
| // It might be more friendly to JS metaprogramming if we include this info |
| // on the function. |
| |
| function upgradeSig(sigF) { |
| let originalSigDesc = $getOwnPropertyDescriptor($type, sigF); |
| if (originalSigDesc === void 0) return; |
| let originalSigFn = originalSigDesc.get; |
| $defineMemoizedGetter(type, sigF, function() { |
| let sig = originalSigFn(); |
| let propertyNames = Object.getOwnPropertyNames(sig); |
| for (let name of methodNames) { |
| if (name in sig) { |
| sig[$getExtensionSymbol(name)] = sig[name]; |
| } |
| } |
| return sig; |
| }); |
| }; |
| upgradeSig($_methodSig); |
| upgradeSig($_fieldSig); |
| upgradeSig($_getterSig); |
| upgradeSig($_setterSig); |
| })()'''); |
| |
| /// Sets the type of `obj` to be `type` |
| setType(obj, type) { |
| JS('', '#.__proto__ = #.prototype', obj, type); |
| return obj; |
| } |
| |
| /// Sets the element type of a list literal. |
| list(obj, elementType) => |
| JS('', '$setType($obj, ${getGenericClass(JSArray)}($elementType))'); |
| |
| /// Link the extension to the type it's extending as a base class. |
| setBaseClass(derived, base) { |
| JS('', '#.prototype.__proto__ = #.prototype', derived, base); |
| // We use __proto__ to track the superclass hierarchy (see isSubtype). |
| JS('', '#.__proto__ = #', derived, base); |
| } |
| |
| /// Like [setBaseClass] but for generic extension types, e.g. `JSArray<E>` |
| setExtensionBaseClass(derived, base) { |
| // Mark the generic type as an extension type and link the prototype objects |
| return JS( |
| '', |
| '''(() => { |
| if ($base) { |
| $derived.prototype[$_extensionType] = $derived; |
| $derived.prototype.__proto__ = $base.prototype |
| } |
| })()'''); |
| } |
| |
| /// Given a special constructor function that creates a function instances, |
| /// and a class with a `call` method, merge them so the constructor function |
| /// will have the correct methods and prototype. |
| /// |
| /// For example: |
| /// |
| /// lib.Foo = dart.callableClass( |
| /// function Foo { function call(...args) { ... } ... return call; }, |
| /// class Foo { call(x) { ... } }); |
| /// ... |
| /// let f = new lib.Foo(); |
| /// f(42); |
| callableClass(callableCtor, classExpr) { |
| JS('', '#.prototype = #.prototype', callableCtor, classExpr); |
| // We're not going to use the original class, so we can safely replace it to |
| // point at this constructor for the runtime type information. |
| JS('', '#.prototype.constructor = #', callableCtor, callableCtor); |
| JS('', '#.__proto__ = #', callableCtor, classExpr); |
| return callableCtor; |
| } |
| |
| /// Given a class and an initializer method name and a call method, creates a |
| /// constructor function with the same name. |
| /// |
| /// For example it can be called with `new SomeClass.name(args)`. |
| /// |
| /// The constructor |
| defineNamedConstructorCallable(clazz, name, ctor) => JS( |
| '', |
| '''(() => { |
| ctor.prototype = $clazz.prototype; |
| // Use defineProperty so we don't hit a property defined on Function, |
| // like `caller` and `arguments`. |
| $defineProperty($clazz, $name, { value: ctor, configurable: true }); |
| })()'''); |
| |
| defineEnumValues(enumClass, names) => JS( |
| '', |
| '''(() => { |
| let values = []; |
| for (var i = 0; i < $names.length; i++) { |
| let value = $const_(new $enumClass(i)); |
| values.push(value); |
| Object.defineProperty($enumClass, $names[i], |
| { value: value, configurable: true }); |
| } |
| $enumClass.values = $constList(values, $enumClass); |
| })()'''); |