/** * Copyright (c) 2012-2016 André Bargull * Alle Rechte vorbehalten / All Rights Reserved. Use is subject to license terms. * * <https://github.com/anba/es6draft> */ package com.github.anba.es6draft.runtime.objects; import static com.github.anba.es6draft.runtime.AbstractOperations.*; import static com.github.anba.es6draft.runtime.internal.Errors.newTypeError; import static com.github.anba.es6draft.runtime.internal.Properties.createProperties; import static com.github.anba.es6draft.runtime.types.Null.NULL; import static com.github.anba.es6draft.runtime.types.PropertyDescriptor.FromPropertyDescriptor; import static com.github.anba.es6draft.runtime.types.PropertyDescriptor.ToPropertyDescriptor; import java.util.ArrayList; import java.util.List; import com.github.anba.es6draft.runtime.ExecutionContext; import com.github.anba.es6draft.runtime.Realm; import com.github.anba.es6draft.runtime.internal.CompatibilityOption; import com.github.anba.es6draft.runtime.internal.Initializable; import com.github.anba.es6draft.runtime.internal.Messages; import com.github.anba.es6draft.runtime.internal.Properties.Attributes; import com.github.anba.es6draft.runtime.internal.Properties.CompatibilityExtension; import com.github.anba.es6draft.runtime.internal.Properties.Function; import com.github.anba.es6draft.runtime.internal.Properties.Prototype; import com.github.anba.es6draft.runtime.internal.Properties.Value; import com.github.anba.es6draft.runtime.types.Constructor; import com.github.anba.es6draft.runtime.types.IntegrityLevel; import com.github.anba.es6draft.runtime.types.Intrinsics; import com.github.anba.es6draft.runtime.types.Property; import com.github.anba.es6draft.runtime.types.PropertyDescriptor; import com.github.anba.es6draft.runtime.types.ScriptObject; import com.github.anba.es6draft.runtime.types.Symbol; import com.github.anba.es6draft.runtime.types.Type; import com.github.anba.es6draft.runtime.types.builtins.ArrayObject; import com.github.anba.es6draft.runtime.types.builtins.BuiltinConstructor; import com.github.anba.es6draft.runtime.types.builtins.ImmutablePrototypeObject; import com.github.anba.es6draft.runtime.types.builtins.OrdinaryObject; /** * <h1>19 Fundamental Objects</h1><br> * <h2>19.1 Object Objects</h2> * <ul> * <li>19.1.1 The Object Constructor * <li>19.1.2 Properties of the Object Constructor * </ul> */ public final class ObjectConstructor extends BuiltinConstructor implements Initializable { /** * Constructs a new Object constructor function. * * @param realm * the realm object */ public ObjectConstructor(Realm realm) { super(realm, "Object", 1); } @Override public void initialize(Realm realm) { createProperties(realm, this, Properties.class); createProperties(realm, this, ValuesEntriesFunctions.class); createProperties(realm, this, GetOwnPropertyDescriptors.class); } @Override public ObjectConstructor clone() { return new ObjectConstructor(getRealm()); } /** * 19.1.1.1 Object ( [ value ] ) */ @Override public ScriptObject call(ExecutionContext callerContext, Object thisValue, Object... args) { ExecutionContext calleeContext = calleeContext(); Object value = argument(args, 0); /* step 1 (not applicable) */ /* step 2 */ if (Type.isUndefinedOrNull(value)) { // OrdinaryCreateFromConstructor instead of ObjectCreate in case the active function // (= `this`) is not the intrinsic %Object% constructor function. return OrdinaryCreateFromConstructor(calleeContext, this, Intrinsics.ObjectPrototype); } /* step 3 */ return ToObject(calleeContext, value); } /** * 19.1.1.1 Object ( [ value ] ) */ @Override public ScriptObject construct(ExecutionContext callerContext, Constructor newTarget, Object... args) { ExecutionContext calleeContext = calleeContext(); Object value = argument(args, 0); /* step 1 */ if (newTarget != this) { return OrdinaryCreateFromConstructor(calleeContext, newTarget, Intrinsics.ObjectPrototype); } /* step 2 */ if (Type.isUndefinedOrNull(value)) { // OrdinaryCreateFromConstructor instead of ObjectCreate in case the active function // (= `this`) is not the intrinsic %Object% constructor function. return OrdinaryCreateFromConstructor(calleeContext, this, Intrinsics.ObjectPrototype); } /* step 3 */ return ToObject(calleeContext, value); } /** * 19.1.2 Properties of the Object Constructor */ public enum Properties { ; @Prototype public static final Intrinsics __proto__ = Intrinsics.FunctionPrototype; @Value(name = "length", attributes = @Attributes(writable = false, enumerable = false, configurable = true)) public static final int length = 1; @Value(name = "name", attributes = @Attributes(writable = false, enumerable = false, configurable = true)) public static final String name = "Object"; /** * 19.1.2.16 Object.prototype */ @Value(name = "prototype", attributes = @Attributes(writable = false, enumerable = false, configurable = false)) public static final Intrinsics prototype = Intrinsics.ObjectPrototype; /** * 19.1.2.9 Object.getPrototypeOf ( O ) * * @param cx * the execution context * @param thisValue * the function this-value * @param o * the script object * @return the prototype object */ @Function(name = "getPrototypeOf", arity = 1) public static Object getPrototypeOf(ExecutionContext cx, Object thisValue, Object o) { /* steps 1-2 */ ScriptObject obj = ToObject(cx, o); /* step 3 */ ScriptObject proto = obj.getPrototypeOf(cx); return proto != null ? proto : NULL; } /** * 19.1.2.6 Object.getOwnPropertyDescriptor ( O, P ) * * @param cx * the execution context * @param thisValue * the function this-value * @param o * the script object * @param p * the property key * @return the property descriptor object or undefined */ @Function(name = "getOwnPropertyDescriptor", arity = 2) public static Object getOwnPropertyDescriptor(ExecutionContext cx, Object thisValue, Object o, Object p) { /* steps 1-2 */ ScriptObject obj = ToObject(cx, o); /* steps 3-4 */ Object key = ToPropertyKey(cx, p); /* steps 5-6 */ Property desc = obj.getOwnProperty(cx, key); /* step 7 */ return FromPropertyDescriptor(cx, desc); } /** * 19.1.2.7 Object.getOwnPropertyNames ( O ) * * @param cx * the execution context * @param thisValue * the function this-value * @param o * the script object * @return the own string-valued property keys of <var>o</var> */ @Function(name = "getOwnPropertyNames", arity = 1) public static Object getOwnPropertyNames(ExecutionContext cx, Object thisValue, Object o) { /* step 1 */ return GetOwnPropertyNames(cx, o); } /** * 19.1.2.8 Object.getOwnPropertySymbols ( O ) * * @param cx * the execution context * @param thisValue * the function this-value * @param o * the script object * @return the own symbol-valued property keys of <var>o</var> */ @Function(name = "getOwnPropertySymbols", arity = 1) public static Object getOwnPropertySymbols(ExecutionContext cx, Object thisValue, Object o) { /* step 1 */ return GetOwnPropertySymbols(cx, o); } /** * 19.1.2.2 Object.create ( O [, Properties] ) * * @param cx * the execution context * @param thisValue * the function this-value * @param o * the script object * @param properties * the properties object * @return the new script object */ @Function(name = "create", arity = 2) public static Object create(ExecutionContext cx, Object thisValue, Object o, Object properties) { /* step 1 */ if (!Type.isObjectOrNull(o)) { throw newTypeError(cx, Messages.Key.NotObjectOrNull); } /* step 2 */ OrdinaryObject obj = ObjectCreate(cx, Type.objectValueOrNull(o)); /* step 3 */ if (!Type.isUndefined(properties)) { return ObjectDefineProperties(cx, obj, properties); } /* step 4 */ return obj; } /** * 19.1.2.4 Object.defineProperty ( O, P, Attributes ) * * @param cx * the execution context * @param thisValue * the function this-value * @param o * the script object * @param p * the property key * @param attributes * the property descriptor object * @return the script object */ @Function(name = "defineProperty", arity = 3) public static Object defineProperty(ExecutionContext cx, Object thisValue, Object o, Object p, Object attributes) { /* step 1 */ if (!Type.isObject(o)) { throw newTypeError(cx, Messages.Key.NotObjectType); } /* steps 2-3 */ Object key = ToPropertyKey(cx, p); /* steps 4-5 */ PropertyDescriptor desc = ToPropertyDescriptor(cx, attributes); /* steps 6-7 */ DefinePropertyOrThrow(cx, Type.objectValue(o), key, desc); /* step 8 */ return o; } /** * 19.1.2.3 Object.defineProperties ( O, Properties ) * * @param cx * the execution context * @param thisValue * the function this-value * @param o * the script object * @param properties * the properties object * @return the script object */ @Function(name = "defineProperties", arity = 2) public static Object defineProperties(ExecutionContext cx, Object thisValue, Object o, Object properties) { /* step 1 */ return ObjectDefineProperties(cx, o, properties); } /** * 19.1.2.17 Object.seal ( O ) * * @param cx * the execution context * @param thisValue * the function this-value * @param o * the script object * @return the script object */ @Function(name = "seal", arity = 1) public static Object seal(ExecutionContext cx, Object thisValue, Object o) { /* step 1 */ if (!Type.isObject(o)) { return o; } /* steps 2-3 */ boolean status = SetIntegrityLevel(cx, Type.objectValue(o), IntegrityLevel.Sealed); /* step 4 */ if (!status) { throw newTypeError(cx, Messages.Key.ObjectSealFailed); } /* step 5 */ return o; } /** * 19.1.2.5 Object.freeze ( O ) * * @param cx * the execution context * @param thisValue * the function this-value * @param o * the script object * @return the script object */ @Function(name = "freeze", arity = 1) public static Object freeze(ExecutionContext cx, Object thisValue, Object o) { /* step 1 */ if (!Type.isObject(o)) { return o; } /* steps 2-3 */ boolean status = SetIntegrityLevel(cx, Type.objectValue(o), IntegrityLevel.Frozen); /* step 4 */ if (!status) { throw newTypeError(cx, Messages.Key.ObjectFreezeFailed); } /* step 5 */ return o; } /** * 19.1.2.15 Object.preventExtensions ( O ) * * @param cx * the execution context * @param thisValue * the function this-value * @param o * the script object * @return the script object */ @Function(name = "preventExtensions", arity = 1) public static Object preventExtensions(ExecutionContext cx, Object thisValue, Object o) { /* step 1 */ if (!Type.isObject(o)) { return o; } /* steps 2-3 */ boolean status = Type.objectValue(o).preventExtensions(cx); /* step 4 */ if (!status) { throw newTypeError(cx, Messages.Key.ObjectPreventExtensionsFailed); } /* step 5 */ return o; } /** * 19.1.2.13 Object.isSealed ( O ) * * @param cx * the execution context * @param thisValue * the function this-value * @param o * the script object * @return {@code true} if the object is sealed */ @Function(name = "isSealed", arity = 1) public static Object isSealed(ExecutionContext cx, Object thisValue, Object o) { /* step 1 */ if (!Type.isObject(o)) { return true; } /* step 2 */ return TestIntegrityLevel(cx, Type.objectValue(o), IntegrityLevel.Sealed); } /** * 19.1.2.12 Object.isFrozen ( O ) * * @param cx * the execution context * @param thisValue * the function this-value * @param o * the script object * @return {@code true} if the object is frozen */ @Function(name = "isFrozen", arity = 1) public static Object isFrozen(ExecutionContext cx, Object thisValue, Object o) { /* step 1 */ if (!Type.isObject(o)) { return true; } /* step 2 */ return TestIntegrityLevel(cx, Type.objectValue(o), IntegrityLevel.Frozen); } /** * 19.1.2.11 Object.isExtensible ( O ) * * @param cx * the execution context * @param thisValue * the function this-value * @param o * the script object * @return {@code true} if the object is extensible */ @Function(name = "isExtensible", arity = 1) public static Object isExtensible(ExecutionContext cx, Object thisValue, Object o) { /* step 1 */ if (!Type.isObject(o)) { return false; } /* step 2 */ return IsExtensible(cx, Type.objectValue(o)); } /** * 19.1.2.14 Object.keys ( O ) * * @param cx * the execution context * @param thisValue * the function this-value * @param o * the script object * @return the object keys array */ @Function(name = "keys", arity = 1) public static Object keys(ExecutionContext cx, Object thisValue, Object o) { /* steps 1-2 */ ScriptObject obj = ToObject(cx, o); /* steps 3-4 */ List<String> nameList = EnumerableOwnNames(cx, obj); /* step 5 */ return CreateArrayFromList(cx, nameList); } /** * 19.1.2.10 Object.is ( value1, value2 ) * * @param cx * the execution context * @param thisValue * the function this-value * @param value1 * the first value * @param value2 * the second value * @return {@code true} if both operands have the same value */ @Function(name = "is", arity = 2) public static Object is(ExecutionContext cx, Object thisValue, Object value1, Object value2) { /* step 1 */ return SameValue(value1, value2); } /** * 19.1.2.1 Object.assign ( target, ...sources ) * * @param cx * the execution context * @param thisValue * the function this-value * @param target * the target object * @param sources * the source objects * @return the target object */ @Function(name = "assign", arity = 2) public static Object assign(ExecutionContext cx, Object thisValue, Object target, Object... sources) { /* steps 1-2 */ ScriptObject to = ToObject(cx, target); /* step 3 */ if (sources.length == 0) { return to; } /* steps 4-5 */ for (Object nextSource : sources) { /* step 5.a */ if (Type.isUndefinedOrNull(nextSource)) { continue; } /* step 5.b.i-ii */ ScriptObject from = ToObject(cx, nextSource); /* steps 5.b.iii-iv */ List<?> keys = from.ownPropertyKeys(cx); /* step 5.c */ for (Object nextKey : keys) { Property desc = from.getOwnProperty(cx, nextKey); if (desc != null && desc.isEnumerable()) { Object propValue = Get(cx, from, nextKey); Set(cx, to, nextKey, propValue, true); } } } /* step 6 */ return to; } /** * 19.1.2.18 Object.setPrototypeOf ( O, proto ) * * @param cx * the execution context * @param thisValue * the function this-value * @param o * the script object * @param proto * the new prototype object * @return the script object */ @Function(name = "setPrototypeOf", arity = 2) public static Object setPrototypeOf(ExecutionContext cx, Object thisValue, Object o, Object proto) { /* steps 1-2 */ RequireObjectCoercible(cx, o); /* step 3 */ if (!Type.isObjectOrNull(proto)) { throw newTypeError(cx, Messages.Key.NotObjectOrNull); } /* step 4 */ if (!Type.isObject(o)) { return o; } /* steps 5-6 */ ScriptObject obj = Type.objectValue(o); boolean status = obj.setPrototypeOf(cx, Type.objectValueOrNull(proto)); /* step 7 */ if (!status) { // provide better error messages for ordinary objects if (obj instanceof OrdinaryObject && !(obj instanceof ImmutablePrototypeObject)) { if (!obj.isExtensible(cx)) { throw newTypeError(cx, Messages.Key.NotExtensible); } throw newTypeError(cx, Messages.Key.CyclicProto); } throw newTypeError(cx, Messages.Key.ObjectSetPrototypeFailed); } /* step 8 */ return obj; } } @CompatibilityExtension(CompatibilityOption.ObjectValuesEntries) public enum ValuesEntriesFunctions { ; /** * Object.values( O ) * * @param cx * the execution context * @param thisValue * the function this-value * @param o * the script object * @return the object values array */ @Function(name = "values", arity = 1) public static Object values(ExecutionContext cx, Object thisValue, Object o) { /* steps 1-2 */ ScriptObject obj = ToObject(cx, o); /* steps 3-4 */ List<Object> valueList = EnumerableOwnProperties(cx, obj, PropertyKind.Value); /* step 5 */ return CreateArrayFromList(cx, valueList); } /** * Object.entries( O ) * * @param cx * the execution context * @param thisValue * the function this-value * @param o * the script object * @return the object entries array */ @Function(name = "entries", arity = 1) public static Object entries(ExecutionContext cx, Object thisValue, Object o) { /* steps 1-2 */ ScriptObject obj = ToObject(cx, o); /* steps 3-4 */ List<Object> entryList = EnumerableOwnProperties(cx, obj, PropertyKind.KeyValue); /* step 5 */ return CreateArrayFromList(cx, entryList); } } @CompatibilityExtension(CompatibilityOption.ObjectGetOwnPropertyDescriptors) public enum GetOwnPropertyDescriptors { ; /** * Object.getOwnPropertyDescriptors( O ) * * @param cx * the execution context * @param thisValue * the function this-value * @param o * the script object * @return the object property descriptors array */ @Function(name = "getOwnPropertyDescriptors", arity = 1) public static Object getOwnPropertyDescriptors(ExecutionContext cx, Object thisValue, Object o) { /* step 1 */ ScriptObject obj = ToObject(cx, o); /* step 2 */ List<?> ownKeys = obj.ownPropertyKeys(cx); /* step 3 */ OrdinaryObject descriptors = ObjectCreate(cx, Intrinsics.ObjectPrototype); /* step 4 */ for (Object key : ownKeys) { /* step 4.a */ Property desc = obj.getOwnProperty(cx, key); /* step 4.b */ Object descriptor = FromPropertyDescriptor(cx, desc); /* step 4.c */ CreateDataProperty(cx, descriptors, key, descriptor); } /* step 5 */ return descriptors; } } /** * 19.1.2.3.1 Runtime Semantics: ObjectDefineProperties ( O, Properties ) * * @param cx * the execution context * @param o * the script object * @param properties * the properties object * @return the script object */ public static ScriptObject ObjectDefineProperties(ExecutionContext cx, Object o, Object properties) { /* step 1 */ if (!Type.isObject(o)) { throw newTypeError(cx, Messages.Key.NotObjectType); } ScriptObject obj = Type.objectValue(o); /* steps 2-3 */ ScriptObject props = ToObject(cx, properties); /* steps 4-5 */ List<?> keys = props.ownPropertyKeys(cx); /* step 6 */ int initialSize = Math.min(32, keys.size()); ArrayList<PropertyDescriptor> descriptors = new ArrayList<>(initialSize); ArrayList<Object> names = new ArrayList<>(initialSize); /* step 7 */ for (Object nextKey : keys) { Property propDesc = props.getOwnProperty(cx, nextKey); if (propDesc != null && propDesc.isEnumerable()) { Object descObj = Get(cx, props, nextKey); PropertyDescriptor desc = ToPropertyDescriptor(cx, descObj); descriptors.add(desc); names.add(nextKey); } } /* step 8 */ for (int i = 0, size = names.size(); i < size; ++i) { Object p = names.get(i); PropertyDescriptor desc = descriptors.get(i); DefinePropertyOrThrow(cx, obj, p, desc); } /* step 9 */ return obj; } /** * 19.1.2.8.1 Runtime Semantics: GetOwnPropertyKeys ( O, Type ), with Type = String * * @param cx * the execution context * @param o * the script object * @return the own string-valued property keys of <var>o</var> */ public static ArrayObject GetOwnPropertyNames(ExecutionContext cx, Object o) { /* steps 1-2 */ ScriptObject obj = ToObject(cx, o); /* steps 3-4 */ List<?> keys = obj.ownPropertyKeys(cx); /* step 5 */ int initialSize = Math.min(32, keys.size()); ArrayList<String> nameList = new ArrayList<>(initialSize); /* step 6 */ for (Object key : keys) { if (key instanceof String) { nameList.add((String) key); } } /* step 7 */ return CreateArrayFromList(cx, nameList); } /** * 19.1.2.8.1 Runtime Semantics: GetOwnPropertyKeys ( O, Type ), with Type = Symbol * * @param cx * the execution context * @param o * the script object * @return the own symbol-valued property keys of <var>o</var> */ public static ArrayObject GetOwnPropertySymbols(ExecutionContext cx, Object o) { /* steps 1-2 */ ScriptObject obj = ToObject(cx, o); /* steps 3-4 */ List<?> keys = obj.ownPropertyKeys(cx); /* step 5 */ int initialSize = Math.min(8, keys.size()); ArrayList<Symbol> nameList = new ArrayList<>(initialSize); /* step 6 */ for (Object key : keys) { if (key instanceof Symbol) { nameList.add((Symbol) key); } } /* step 7 */ return CreateArrayFromList(cx, nameList); } enum PropertyKind { Key, Value, KeyValue } /** * EnumerableOwnProperties (O) * * @param cx * the execution context * @param object * the script object * @param kind * the property kind * @return <var>object</var>'s own enumerable properties */ static List<Object> EnumerableOwnProperties(ExecutionContext cx, ScriptObject object, PropertyKind kind) { /* step 1 (not applicable) */ /* steps 2-3 */ List<?> ownKeys = object.ownPropertyKeys(cx); /* step 4 */ int initialSize = Math.min(16, ownKeys.size()); ArrayList<Object> properties = new ArrayList<>(initialSize); /* step 5 */ for (Object key : ownKeys) { if (key instanceof String) { String skey = (String) key; Property desc = object.getOwnProperty(cx, skey); if (desc != null && desc.isEnumerable()) { if (kind == PropertyKind.Key) { properties.add(skey); } else { Object value = Get(cx, object, skey); if (kind == PropertyKind.Value) { properties.add(value); } else { ArrayObject entry = CreateArrayFromList(cx, key, value); properties.add(entry); } } } } } /* step 6 (sort keys - not applicable) */ /* step 7 */ return properties; } }