diff --git a/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java b/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java new file mode 100644 index 0000000000..0e7261481e --- /dev/null +++ b/src/org/mozilla/javascript/AbstractEcmaObjectOperations.java @@ -0,0 +1,126 @@ +package org.mozilla.javascript; + +/** + * Abstract Object Operations as defined by EcmaScript + * + * @see Abstract + * Operations - Operations on Objects + *

Notes + *

+ */ +class AbstractEcmaObjectOperations { + static enum INTEGRITY_LEVEL { + FROZEN, + SEALED + } + + /** + * Implementation of Abstract Object operation testIntegrityLevel as defined by EcmaScript + * + * @param cx + * @param o + * @param level + * @return boolean + * @see TestIntegrityLevel + */ + static boolean testIntegrityLevel(Context cx, Object o, INTEGRITY_LEVEL level) { + ScriptableObject obj = ScriptableObject.ensureScriptableObject(o); + + if (obj.isExtensible()) return false; + + for (Object name : obj.getIds(true, true)) { + ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, name); + if (Boolean.TRUE.equals(desc.get("configurable"))) return false; + + if (level == INTEGRITY_LEVEL.FROZEN + && desc.isDataDescriptor(desc) + && Boolean.TRUE.equals(desc.get("writable"))) return false; + } + + return true; + } + + /** + * Implementation of Abstract Object operation setIntegrityLevel as defined by EcmaScript + * + * @param cx + * @param o + * @param level + * @return boolean + * @see SetIntegrityLevel + */ + static boolean setIntegrityLevel(Context cx, Object o, INTEGRITY_LEVEL level) { + /* + 1. Assert: Type(O) is Object. + 2. Assert: level is either sealed or frozen. + 3. Let status be ? O.[[PreventExtensions]](). + 4. If status is false, return false. + 5. Let keys be ? O.[[OwnPropertyKeys]](). + 6. If level is sealed, then + a. For each element k of keys, do + i. Perform ? DefinePropertyOrThrow(O, k, PropertyDescriptor { [[Configurable]]: false }). + 7. Else, + a. Assert: level is frozen. + b. For each element k of keys, do + i. Let currentDesc be ? O.[[GetOwnProperty]](k). + ii. If currentDesc is not undefined, then + 1. If IsAccessorDescriptor(currentDesc) is true, then + a. Let desc be the PropertyDescriptor { [[Configurable]]: false }. + 2. Else, + a. Let desc be the PropertyDescriptor { [[Configurable]]: false, [[Writable]]: false }. + 3. Perform ? DefinePropertyOrThrow(O, k, desc). + 8. Return true. + + NOTES + - While steps 6.a.i and 7.b.ii.3 call for the Abstract DefinePropertyOrThrow operation, + the conditions under which a throw would occur aren't applicable when freezing or sealing an object, + see https://262.ecma-international.org/11.0/#sec-validateandapplypropertydescriptor: + 1. n/a + 2. current cannot be undefined, because the logic operates only on existing properties + 3. n/a + 4. this code doesn't ever set configurable to true or modifies the enumerable property + 5. n/a + 6. as current and desc start out the same and the writable property is set only after checking if isDataDescriptor == true, this condition cannot occur + 7. both conditions under which false would be returned cannot occur here + 8. both conditions under which false would be returned cannot occur here + */ + ScriptableObject obj = ScriptableObject.ensureScriptableObject(o); + + // TODO check .preventExtensions() return value once implemented and act accordingly to spec + obj.preventExtensions(); + + for (Object key : obj.getIds(true, true)) { + ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, key); + + if (level == INTEGRITY_LEVEL.SEALED) { + if (Boolean.TRUE.equals(desc.get("configurable"))) { + desc.put("configurable", desc, false); + + obj.defineOwnProperty(cx, key, desc, false); + } + } else { + if (obj.isDataDescriptor(desc) && Boolean.TRUE.equals(desc.get("writable"))) { + desc.put("writable", desc, false); + } + if (Boolean.TRUE.equals(desc.get("configurable"))) { + desc.put("configurable", desc, false); + } + obj.defineOwnProperty(cx, key, desc, false); + } + } + + return true; + } +} diff --git a/src/org/mozilla/javascript/BaseFunction.java b/src/org/mozilla/javascript/BaseFunction.java index 93b14fc855..30ad662076 100644 --- a/src/org/mozilla/javascript/BaseFunction.java +++ b/src/org/mozilla/javascript/BaseFunction.java @@ -223,7 +223,6 @@ protected void setInstanceIdAttributes(int id, int attr) { argumentsAttributes = attr; return; } - super.setInstanceIdAttributes(id, attr); } @Override diff --git a/src/org/mozilla/javascript/IdScriptableObject.java b/src/org/mozilla/javascript/IdScriptableObject.java index e819ee6ffc..bc7f963780 100644 --- a/src/org/mozilla/javascript/IdScriptableObject.java +++ b/src/org/mozilla/javascript/IdScriptableObject.java @@ -12,20 +12,17 @@ import java.io.Serializable; /** - * Base class for native object implementation that uses IdFunctionObject to - * export its methods to script via <class-name>.prototype object. + * Base class for native object implementation that uses IdFunctionObject to export its methods to + * script via <class-name>.prototype object. * - * Any descendant should implement at least the following methods: - * findInstanceIdInfo getInstanceIdName execIdCall methodArity + *

Any descendant should implement at least the following methods: findInstanceIdInfo + * getInstanceIdName execIdCall methodArity * - * To define non-function properties, the descendant should override - * getInstanceIdValue setInstanceIdValue to get/set property value and provide - * its default attributes. - * - * - * To customize initialization of constructor and prototype objects, descendant - * may override scopeInit or fillConstructorProperties methods. + *

To define non-function properties, the descendant should override getInstanceIdValue + * setInstanceIdValue to get/set property value and provide its default attributes. * + *

To customize initialization of constructor and prototype objects, descendant may override + * scopeInit or fillConstructorProperties methods. */ public abstract class IdScriptableObject extends ScriptableObject implements IdFunctionCall { private static final long serialVersionUID = -3744239272168621609L; @@ -48,73 +45,60 @@ private static final class PrototypeValues implements Serializable { private IdFunctionObject constructor; private short constructorAttrs; - PrototypeValues(IdScriptableObject obj, int maxId) - { + PrototypeValues(IdScriptableObject obj, int maxId) { if (obj == null) throw new IllegalArgumentException(); if (maxId < 1) throw new IllegalArgumentException(); this.obj = obj; this.maxId = maxId; } - final int getMaxId() - { + final int getMaxId() { return maxId; } - final void initValue(int id, String name, Object value, int attributes) - { - if (!(1 <= id && id <= maxId)) - throw new IllegalArgumentException(); - if (name == null) - throw new IllegalArgumentException(); - if (value == NOT_FOUND) - throw new IllegalArgumentException(); + final void initValue(int id, String name, Object value, int attributes) { + if (!(1 <= id && id <= maxId)) throw new IllegalArgumentException(); + if (name == null) throw new IllegalArgumentException(); + if (value == NOT_FOUND) throw new IllegalArgumentException(); ScriptableObject.checkValidAttributes(attributes); - if (obj.findPrototypeId(name) != id) - throw new IllegalArgumentException(name); + if (obj.findPrototypeId(name) != id) throw new IllegalArgumentException(name); if (id == constructorId) { if (!(value instanceof IdFunctionObject)) { - throw new IllegalArgumentException("consructor should be initialized with IdFunctionObject"); + throw new IllegalArgumentException( + "consructor should be initialized with IdFunctionObject"); } - constructor = (IdFunctionObject)value; - constructorAttrs = (short)attributes; + constructor = (IdFunctionObject) value; + constructorAttrs = (short) attributes; return; } initSlot(id, name, value, attributes); } - final void initValue(int id, Symbol key, Object value, int attributes) - { - if (!(1 <= id && id <= maxId)) - throw new IllegalArgumentException(); - if (key == null) - throw new IllegalArgumentException(); - if (value == NOT_FOUND) - throw new IllegalArgumentException(); + final void initValue(int id, Symbol key, Object value, int attributes) { + if (!(1 <= id && id <= maxId)) throw new IllegalArgumentException(); + if (key == null) throw new IllegalArgumentException(); + if (value == NOT_FOUND) throw new IllegalArgumentException(); ScriptableObject.checkValidAttributes(attributes); - if (obj.findPrototypeId(key) != id) - throw new IllegalArgumentException(key.toString()); + if (obj.findPrototypeId(key) != id) throw new IllegalArgumentException(key.toString()); if (id == constructorId) { if (!(value instanceof IdFunctionObject)) { - throw new IllegalArgumentException("consructor should be initialized with IdFunctionObject"); + throw new IllegalArgumentException( + "consructor should be initialized with IdFunctionObject"); } - constructor = (IdFunctionObject)value; - constructorAttrs = (short)attributes; + constructor = (IdFunctionObject) value; + constructorAttrs = (short) attributes; return; } initSlot(id, key, value, attributes); } - private void initSlot(int id, Object name, Object value, - int attributes) - { + private void initSlot(int id, Object name, Object value, int attributes) { Object[] array = valueArray; - if (array == null) - throw new IllegalStateException(); + if (array == null) throw new IllegalStateException(); if (value == null) { value = UniqueTag.NULL_VALUE; @@ -125,52 +109,47 @@ private void initSlot(int id, Object name, Object value, if (value2 == null) { array[index] = value; array[index + NAME_SLOT] = name; - attributeArray[id - 1] = (short)attributes; + attributeArray[id - 1] = (short) attributes; } else { - if (!name.equals(array[index + NAME_SLOT])) - throw new IllegalStateException(); + if (!name.equals(array[index + NAME_SLOT])) throw new IllegalStateException(); } } } - final IdFunctionObject createPrecachedConstructor() - { + final IdFunctionObject createPrecachedConstructor() { if (constructorId != 0) throw new IllegalStateException(); constructorId = obj.findPrototypeId("constructor"); if (constructorId == 0) { - throw new IllegalStateException( - "No id for constructor property"); + throw new IllegalStateException("No id for constructor property"); } obj.initPrototypeId(constructorId); if (constructor == null) { throw new IllegalStateException( - obj.getClass().getName()+".initPrototypeId() did not " - +"initialize id="+constructorId); + obj.getClass().getName() + + ".initPrototypeId() did not " + + "initialize id=" + + constructorId); } - constructor.initFunction(obj.getClassName(), - ScriptableObject.getTopLevelScope(obj)); + constructor.initFunction(obj.getClassName(), ScriptableObject.getTopLevelScope(obj)); constructor.markAsConstructor(obj); return constructor; } - final int findId(String name) - { + final int findId(String name) { return obj.findPrototypeId(name); } - final int findId(Symbol key) - { + final int findId(Symbol key) { return obj.findPrototypeId(key); } - final boolean has(int id) - { + final boolean has(int id) { Object[] array = valueArray; if (array == null) { // Not yet initialized, assume all exists return true; } - int valueSlot = (id - 1) * SLOT_SPAN; + int valueSlot = (id - 1) * SLOT_SPAN; Object value = array[valueSlot]; if (value == null) { // The particular entry has not been yet initialized @@ -179,8 +158,7 @@ final boolean has(int id) return value != NOT_FOUND; } - final Object get(int id) - { + final Object get(int id) { Object value = ensureId(id); if (value == UniqueTag.NULL_VALUE) { value = null; @@ -188,8 +166,7 @@ final Object get(int id) return value; } - final void set(int id, Scriptable start, Object value) - { + final void set(int id, Scriptable start, Object value) { if (value == NOT_FOUND) throw new IllegalArgumentException(); ensureId(id); int attr = attributeArray[id - 1]; @@ -198,39 +175,38 @@ final void set(int id, Scriptable start, Object value) if (value == null) { value = UniqueTag.NULL_VALUE; } - int valueSlot = (id - 1) * SLOT_SPAN; + int valueSlot = (id - 1) * SLOT_SPAN; synchronized (this) { valueArray[valueSlot] = value; } - } - else { - int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; + } else { + int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; Object name = valueArray[nameSlot]; if (name instanceof Symbol) { if (start instanceof SymbolScriptable) { - ((SymbolScriptable)start).put((Symbol) name, start, value); + ((SymbolScriptable) start).put((Symbol) name, start, value); } } else { - start.put((String)name, start, value); + start.put((String) name, start, value); } } } } - final void delete(int id) - { + final void delete(int id) { ensureId(id); int attr = attributeArray[id - 1]; // non-configurable if ((attr & PERMANENT) != 0) { Context cx = Context.getContext(); if (cx.isStrictMode()) { - int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; - String name = (String)valueArray[nameSlot]; - throw ScriptRuntime.typeErrorById("msg.delete.property.with.configurable.false", name); + int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; + String name = (String) valueArray[nameSlot]; + throw ScriptRuntime.typeErrorById( + "msg.delete.property.with.configurable.false", name); } } else { - int valueSlot = (id - 1) * SLOT_SPAN; + int valueSlot = (id - 1) * SLOT_SPAN; synchronized (this) { valueArray[valueSlot] = NOT_FOUND; attributeArray[id - 1] = EMPTY; @@ -238,30 +214,27 @@ final void delete(int id) } } - final int getAttributes(int id) - { + final int getAttributes(int id) { ensureId(id); return attributeArray[id - 1]; } - final void setAttributes(int id, int attributes) - { + final void setAttributes(int id, int attributes) { ScriptableObject.checkValidAttributes(attributes); ensureId(id); synchronized (this) { - attributeArray[id - 1] = (short)attributes; + attributeArray[id - 1] = (short) attributes; } } - final Object[] getNames(boolean getAll, boolean getSymbols, Object[] extraEntries) - { + final Object[] getNames(boolean getAll, boolean getSymbols, Object[] extraEntries) { Object[] names = null; int count = 0; for (int id = 1; id <= maxId; ++id) { Object value = ensureId(id); if (getAll || (attributeArray[id - 1] & DONTENUM) == 0) { if (value != NOT_FOUND) { - int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; + int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; Object name = valueArray[nameSlot]; if (name instanceof String) { if (names == null) { @@ -295,8 +268,7 @@ final Object[] getNames(boolean getAll, boolean getSymbols, Object[] extraEntrie } } - private Object ensureId(int id) - { + private Object ensureId(int id) { Object[] array = valueArray; if (array == null) { synchronized (this) { @@ -308,12 +280,11 @@ private Object ensureId(int id) } } } - int valueSlot = (id - 1) * SLOT_SPAN; + int valueSlot = (id - 1) * SLOT_SPAN; Object value = array[valueSlot]; if (value == null) { if (id == constructorId) { - initSlot(constructorId, "constructor", - constructor, constructorAttrs); + initSlot(constructorId, "constructor", constructor, constructorAttrs); constructor = null; // no need to refer it any longer } else { obj.initPrototypeId(id); @@ -321,41 +292,36 @@ private Object ensureId(int id) value = array[valueSlot]; if (value == null) { throw new IllegalStateException( - obj.getClass().getName()+".initPrototypeId(int id) " - +"did not initialize id="+id); + obj.getClass().getName() + + ".initPrototypeId(int id) " + + "did not initialize id=" + + id); } } return value; } } - public IdScriptableObject() - { - } + public IdScriptableObject() {} - public IdScriptableObject(Scriptable scope, Scriptable prototype) - { + public IdScriptableObject(Scriptable scope, Scriptable prototype) { super(scope, prototype); } - protected final boolean defaultHas(String name) - { + protected final boolean defaultHas(String name) { return super.has(name, this); } - protected final Object defaultGet(String name) - { + protected final Object defaultGet(String name) { return super.get(name, this); } - protected final void defaultPut(String name, Object value) - { + protected final void defaultPut(String name, Object value) { super.put(name, this, value); } @Override - public boolean has(String name, Scriptable start) - { + public boolean has(String name, Scriptable start) { int info = findInstanceIdInfo(name); if (info != 0) { int attr = (info >>> 16); @@ -374,10 +340,8 @@ public boolean has(String name, Scriptable start) return super.has(name, start); } - @Override - public boolean has(Symbol key, Scriptable start) - { + public boolean has(Symbol key, Scriptable start) { int info = findInstanceIdInfo(key); if (info != 0) { int attr = (info >>> 16); @@ -397,8 +361,7 @@ public boolean has(Symbol key, Scriptable start) } @Override - public Object get(String name, Scriptable start) - { + public Object get(String name, Scriptable start) { // Check for slot first for performance. This is a very hot code // path that should be further optimized. Object value = super.get(name, start); @@ -422,8 +385,7 @@ public Object get(String name, Scriptable start) } @Override - public Object get(Symbol key, Scriptable start) - { + public Object get(Symbol key, Scriptable start) { Object value = super.get(key, start); if (value != NOT_FOUND) { return value; @@ -445,21 +407,18 @@ public Object get(Symbol key, Scriptable start) } @Override - public void put(String name, Scriptable start, Object value) - { + public void put(String name, Scriptable start, Object value) { int info = findInstanceIdInfo(name); if (info != 0) { if (start == this && isSealed()) { - throw Context.reportRuntimeErrorById("msg.modify.sealed", - name); + throw Context.reportRuntimeErrorById("msg.modify.sealed", name); } int attr = (info >>> 16); if ((attr & READONLY) == 0) { if (start == this) { int id = (info & 0xFFFF); setInstanceIdValue(id, value); - } - else { + } else { start.put(name, start, value); } } @@ -469,8 +428,7 @@ public void put(String name, Scriptable start, Object value) int id = prototypeValues.findId(name); if (id != 0) { if (start == this && isSealed()) { - throw Context.reportRuntimeErrorById("msg.modify.sealed", - name); + throw Context.reportRuntimeErrorById("msg.modify.sealed", name); } prototypeValues.set(id, start, value); return; @@ -480,8 +438,7 @@ public void put(String name, Scriptable start, Object value) } @Override - public void put(Symbol key, Scriptable start, Object value) - { + public void put(Symbol key, Scriptable start, Object value) { int info = findInstanceIdInfo(key); if (info != 0) { if (start == this && isSealed()) { @@ -492,8 +449,7 @@ public void put(Symbol key, Scriptable start, Object value) if (start == this) { int id = (info & 0xFFFF); setInstanceIdValue(id, value); - } - else { + } else { ensureSymbolScriptable(start).put(key, start, value); } } @@ -513,8 +469,7 @@ public void put(Symbol key, Scriptable start, Object value) } @Override - public void delete(String name) - { + public void delete(String name) { int info = findInstanceIdInfo(name); if (info != 0) { // Let the super class to throw exceptions for sealed objects @@ -524,7 +479,8 @@ public void delete(String name) if ((attr & PERMANENT) != 0) { Context cx = Context.getContext(); if (cx.isStrictMode()) { - throw ScriptRuntime.typeErrorById("msg.delete.property.with.configurable.false", name); + throw ScriptRuntime.typeErrorById( + "msg.delete.property.with.configurable.false", name); } } else { int id = (info & 0xFFFF); @@ -546,8 +502,7 @@ public void delete(String name) } @Override - public void delete(Symbol key) - { + public void delete(Symbol key) { int info = findInstanceIdInfo(key); if (info != 0) { // Let the super class to throw exceptions for sealed objects @@ -557,7 +512,8 @@ public void delete(Symbol key) if ((attr & PERMANENT) != 0) { Context cx = Context.getContext(); if (cx.isStrictMode()) { - throw ScriptRuntime.typeErrorById("msg.delete.property.with.configurable.false"); + throw ScriptRuntime.typeErrorById( + "msg.delete.property.with.configurable.false"); } } else { int id = (info & 0xFFFF); @@ -579,8 +535,7 @@ public void delete(Symbol key) } @Override - public int getAttributes(String name) - { + public int getAttributes(String name) { int info = findInstanceIdInfo(name); if (info != 0) { int attr = (info >>> 16); @@ -596,8 +551,7 @@ public int getAttributes(String name) } @Override - public int getAttributes(Symbol key) - { + public int getAttributes(Symbol key) { int info = findInstanceIdInfo(key); if (info != 0) { int attr = (info >>> 16); @@ -613,8 +567,7 @@ public int getAttributes(Symbol key) } @Override - public void setAttributes(String name, int attributes) - { + public void setAttributes(String name, int attributes) { ScriptableObject.checkValidAttributes(attributes); int info = findInstanceIdInfo(name); if (info != 0) { @@ -636,8 +589,7 @@ public void setAttributes(String name, int attributes) } @Override - Object[] getIds(boolean getNonEnumerable, boolean getSymbols) - { + Object[] getIds(boolean getNonEnumerable, boolean getSymbols) { Object[] result = super.getIds(getNonEnumerable, getSymbols); if (prototypeValues != null) { @@ -671,8 +623,7 @@ Object[] getIds(boolean getNonEnumerable, boolean getSymbols) if (count != 0) { if (result.length == 0 && ids.length == count) { result = ids; - } - else { + } else { Object[] tmp = new Object[result.length + count]; System.arraycopy(result, 0, tmp, 0, result.length); System.arraycopy(ids, 0, tmp, result.length, count); @@ -683,93 +634,83 @@ Object[] getIds(boolean getNonEnumerable, boolean getSymbols) return result; } - /** - * Get maximum id findInstanceIdInfo can generate. - */ - protected int getMaxInstanceId() - { + /** Get maximum id findInstanceIdInfo can generate. */ + protected int getMaxInstanceId() { return 0; } - protected static int instanceIdInfo(int attributes, int id) - { + protected static int instanceIdInfo(int attributes, int id) { return (attributes << 16) | id; } /** - * Map name to id of instance property. - * Should return 0 if not found or the result of - * {@link #instanceIdInfo(int, int)}. + * Map name to id of instance property. Should return 0 if not found or the result of {@link + * #instanceIdInfo(int, int)}. */ - protected int findInstanceIdInfo(String name) - { + protected int findInstanceIdInfo(String name) { return 0; } /** - * Map name to id of instance property. - * Should return 0 if not found or the result of - * {@link #instanceIdInfo(int, int)}. + * Map name to id of instance property. Should return 0 if not found or the result of {@link + * #instanceIdInfo(int, int)}. */ - protected int findInstanceIdInfo(Symbol key) - { + protected int findInstanceIdInfo(Symbol key) { return 0; } - /** Map id back to property name it defines. - */ - protected String getInstanceIdName(int id) - { + /** Map id back to property name it defines. */ + protected String getInstanceIdName(int id) { throw new IllegalArgumentException(String.valueOf(id)); } - /** Get id value. - ** If id value is constant, descendant can call cacheIdValue to store - ** value in the permanent cache. - ** Default implementation creates IdFunctionObject instance for given id - ** and cache its value + /** + * Get id value. * If id value is constant, descendant can call cacheIdValue to store * value in + * the permanent cache. * Default implementation creates IdFunctionObject instance for given id + * * and cache its value */ - protected Object getInstanceIdValue(int id) - { + protected Object getInstanceIdValue(int id) { throw new IllegalStateException(String.valueOf(id)); } /** - * Set or delete id value. If value == NOT_FOUND , the implementation - * should make sure that the following getInstanceIdValue return NOT_FOUND. + * Set or delete id value. If value == NOT_FOUND , the implementation should make sure that the + * following getInstanceIdValue return NOT_FOUND. */ - protected void setInstanceIdValue(int id, Object value) - { + protected void setInstanceIdValue(int id, Object value) { throw new IllegalStateException(String.valueOf(id)); } /** - * Update the attributes of the given instance property. Classes which - * want to support changing property attributes via Object.defineProperty - * must override this method. The default implementation throws - * InternalError. + * Update the attributes of the given instance property. Classes which want to support changing + * property attributes via Object.defineProperty must override this method. The default + * implementation throws InternalError. + * * @param id the instance property id * @param attr the new attribute bitset */ protected void setInstanceIdAttributes(int id, int attr) { - throw ScriptRuntime.constructError("InternalError", - "Changing attributes not supported for " + getClassName() - + " " + getInstanceIdName(id) + " property"); + throw ScriptRuntime.constructError( + "InternalError", + "Changing attributes not supported for " + + getClassName() + + " " + + getInstanceIdName(id) + + " property"); } - /** 'thisObj' will be null if invoked as constructor, in which case - ** instance of Scriptable should be returned. */ + /** + * 'thisObj' will be null if invoked as constructor, in which case * instance of Scriptable + * should be returned. + */ @Override - public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) - { + public Object execIdCall( + IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { throw f.unknown(); } - public final IdFunctionObject exportAsJSClass(int maxPrototypeId, - Scriptable scope, - boolean sealed) - { + public final IdFunctionObject exportAsJSClass( + int maxPrototypeId, Scriptable scope, boolean sealed) { // Set scope and prototype unless this is top level scope itself if (scope != this && scope != null) { setParentScope(scope); @@ -789,107 +730,90 @@ public final IdFunctionObject exportAsJSClass(int maxPrototypeId, return ctor; } - public final boolean hasPrototypeMap() - { + public final boolean hasPrototypeMap() { return prototypeValues != null; } - public final void activatePrototypeMap(int maxPrototypeId) - { + public final void activatePrototypeMap(int maxPrototypeId) { PrototypeValues values = new PrototypeValues(this, maxPrototypeId); synchronized (this) { - if (prototypeValues != null) - throw new IllegalStateException(); + if (prototypeValues != null) throw new IllegalStateException(); prototypeValues = values; } } - public final IdFunctionObject initPrototypeMethod(Object tag, int id, String name, - int arity) - { + public final IdFunctionObject initPrototypeMethod(Object tag, int id, String name, int arity) { return initPrototypeMethod(tag, id, name, name, arity); } - public final IdFunctionObject initPrototypeMethod(Object tag, int id, String propertyName, String functionName, - int arity) - { + public final IdFunctionObject initPrototypeMethod( + Object tag, int id, String propertyName, String functionName, int arity) { Scriptable scope = ScriptableObject.getTopLevelScope(this); - IdFunctionObject function = newIdFunction(tag, id, - functionName != null ? functionName : propertyName, - arity, scope); + IdFunctionObject function = + newIdFunction( + tag, id, functionName != null ? functionName : propertyName, arity, scope); prototypeValues.initValue(id, propertyName, function, DONTENUM); return function; } - public final IdFunctionObject initPrototypeMethod(Object tag, int id, Symbol key, String functionName, - int arity) - { + public final IdFunctionObject initPrototypeMethod( + Object tag, int id, Symbol key, String functionName, int arity) { Scriptable scope = ScriptableObject.getTopLevelScope(this); IdFunctionObject function = newIdFunction(tag, id, functionName, arity, scope); prototypeValues.initValue(id, key, function, DONTENUM); return function; } - public final void initPrototypeConstructor(IdFunctionObject f) - { + public final void initPrototypeConstructor(IdFunctionObject f) { int id = prototypeValues.constructorId; - if (id == 0) - throw new IllegalStateException(); - if (f.methodId() != id) - throw new IllegalArgumentException(); - if (isSealed()) { f.sealObject(); } + if (id == 0) throw new IllegalStateException(); + if (f.methodId() != id) throw new IllegalArgumentException(); + if (isSealed()) { + f.sealObject(); + } prototypeValues.initValue(id, "constructor", f, DONTENUM); } - public final void initPrototypeValue(int id, String name, Object value, - int attributes) - { + public final void initPrototypeValue(int id, String name, Object value, int attributes) { prototypeValues.initValue(id, name, value, attributes); } - public final void initPrototypeValue(int id, Symbol key, Object value, - int attributes) - { + public final void initPrototypeValue(int id, Symbol key, Object value, int attributes) { prototypeValues.initValue(id, key, value, attributes); } - protected void initPrototypeId(int id) - { + protected void initPrototypeId(int id) { throw new IllegalStateException(String.valueOf(id)); } - protected int findPrototypeId(String name) - { + protected int findPrototypeId(String name) { throw new IllegalStateException(name); } - protected int findPrototypeId(Symbol key) - { + protected int findPrototypeId(Symbol key) { return 0; } - protected void fillConstructorProperties(IdFunctionObject ctor) - { - } + protected void fillConstructorProperties(IdFunctionObject ctor) {} - protected void addIdFunctionProperty(Scriptable obj, Object tag, int id, - String name, int arity) - { + protected void addIdFunctionProperty( + Scriptable obj, Object tag, int id, String name, int arity) { Scriptable scope = ScriptableObject.getTopLevelScope(obj); IdFunctionObject f = newIdFunction(tag, id, name, arity, scope); f.addAsProperty(obj); } /** - * Utility method to check the type and do the cast or throw an incompatible call - * error. + * Utility method to check the type and do the cast or throw an incompatible call error. * Possible usage would be to have a private function like realThis: + * *

      *  private static NativeSomething realThis(Scriptable thisObj, IdFunctionObject f)
      *  {
      *      return ensureType(thisObj, NativeSomething.class, f);
      * }
      * 
+ * * @param obj the object to check/cast * @param clazz the target type * @param f function that is attempting to convert 'this' object. @@ -897,20 +821,23 @@ protected void addIdFunctionProperty(Scriptable obj, Object tag, int id, * @throws EcmaError if the cast failed. */ @SuppressWarnings("unchecked") - protected static T ensureType(Object obj, Class clazz, IdFunctionObject f) - { + protected static T ensureType(Object obj, Class clazz, IdFunctionObject f) { if (clazz.isInstance(obj)) { return (T) obj; } if (obj == null) { - throw ScriptRuntime.typeErrorById("msg.incompat.call.details", f.getFunctionName(), "null", clazz.getName()); + throw ScriptRuntime.typeErrorById( + "msg.incompat.call.details", f.getFunctionName(), "null", clazz.getName()); } - throw ScriptRuntime.typeErrorById("msg.incompat.call.details", f.getFunctionName(), obj.getClass().getName(), clazz.getName()); + throw ScriptRuntime.typeErrorById( + "msg.incompat.call.details", + f.getFunctionName(), + obj.getClass().getName(), + clazz.getName()); } - private IdFunctionObject newIdFunction(Object tag, int id, String name, - int arity, Scriptable scope) - { + private IdFunctionObject newIdFunction( + Object tag, int id, String name, int arity, Scriptable scope) { IdFunctionObject function = null; if (Context.getContext().getLanguageVersion() < Context.VERSION_ES6) { function = new IdFunctionObject(this, tag, id, name, arity, scope); @@ -918,132 +845,134 @@ private IdFunctionObject newIdFunction(Object tag, int id, String name, function = new IdFunctionObjectES6(this, tag, id, name, arity, scope); } - if (isSealed()) { function.sealObject(); } + if (isSealed()) { + function.sealObject(); + } return function; } @Override - public void defineOwnProperty(Context cx, Object key, ScriptableObject desc) { - if (key instanceof String) { - String name = (String) key; - int info = findInstanceIdInfo(name); - if (info != 0) { - int id = (info & 0xFFFF); - if (isAccessorDescriptor(desc)) { - delete(id); // it will be replaced with a slot - } else { - checkPropertyDefinition(desc); - ScriptableObject current = getOwnPropertyDescriptor(cx, key); - checkPropertyChange(name, current, desc); - int attr = (info >>> 16); - Object value = getProperty(desc, "value"); - if (value != NOT_FOUND && (attr & READONLY) == 0) { - Object currentValue = getInstanceIdValue(id); - if (!sameValue(value, currentValue)) { - setInstanceIdValue(id, value); + protected void defineOwnProperty( + Context cx, Object key, ScriptableObject desc, boolean checkValid) { + if (key instanceof String) { + String name = (String) key; + int info = findInstanceIdInfo(name); + if (info != 0) { + int id = (info & 0xFFFF); + if (isAccessorDescriptor(desc)) { + delete(id); // it will be replaced with a slot + } else { + checkPropertyDefinition(desc); + ScriptableObject current = getOwnPropertyDescriptor(cx, key); + checkPropertyChange(name, current, desc); + int attr = (info >>> 16); + Object value = getProperty(desc, "value"); + if (value != NOT_FOUND && (attr & READONLY) == 0) { + Object currentValue = getInstanceIdValue(id); + if (!sameValue(value, currentValue)) { + setInstanceIdValue(id, value); + } + } + setAttributes(name, applyDescriptorToAttributeBitset(attr, desc)); + return; } - } - setAttributes(name, applyDescriptorToAttributeBitset(attr, desc)); - return; } - } - if (prototypeValues != null) { - int id = prototypeValues.findId(name); - if (id != 0) { - if (isAccessorDescriptor(desc)) { - prototypeValues.delete(id); // it will be replaced with a slot - } else { - checkPropertyDefinition(desc); - ScriptableObject current = getOwnPropertyDescriptor(cx, key); - checkPropertyChange(name, current, desc); - int attr = prototypeValues.getAttributes(id); - Object value = getProperty(desc, "value"); - if (value != NOT_FOUND && (attr & READONLY) == 0) { - Object currentValue = prototypeValues.get(id); - if (!sameValue(value, currentValue)) { - prototypeValues.set(id, this, value); - } - } - prototypeValues.setAttributes(id, applyDescriptorToAttributeBitset(attr, desc)); + if (prototypeValues != null) { + int id = prototypeValues.findId(name); + if (id != 0) { + if (isAccessorDescriptor(desc)) { + prototypeValues.delete(id); // it will be replaced with a slot + } else { + checkPropertyDefinition(desc); + ScriptableObject current = getOwnPropertyDescriptor(cx, key); + checkPropertyChange(name, current, desc); + int attr = prototypeValues.getAttributes(id); + Object value = getProperty(desc, "value"); + if (value != NOT_FOUND && (attr & READONLY) == 0) { + Object currentValue = prototypeValues.get(id); + if (!sameValue(value, currentValue)) { + prototypeValues.set(id, this, value); + } + } + prototypeValues.setAttributes( + id, applyDescriptorToAttributeBitset(attr, desc)); + + // Handle the regular slot that was created if this property was previously + // replaced + // with an accessor descriptor. + if (super.has(name, this)) { + super.delete(name); + } - // Handle the regular slot that was created if this property was previously replaced - // with an accessor descriptor. - if (super.has(name, this)) { - super.delete(name); + return; + } } - - return; - } } } - } - super.defineOwnProperty(cx, key, desc); + super.defineOwnProperty(cx, key, desc, checkValid); } - @Override protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { - ScriptableObject desc = super.getOwnPropertyDescriptor(cx, id); - if (desc == null) { - if (id instanceof String) { - desc = getBuiltInDescriptor((String) id); - } else if (ScriptRuntime.isSymbol(id)) { - desc = getBuiltInDescriptor(((NativeSymbol)id).getKey()); - } - } - return desc; + ScriptableObject desc = super.getOwnPropertyDescriptor(cx, id); + if (desc == null) { + if (id instanceof String) { + desc = getBuiltInDescriptor((String) id); + } else if (ScriptRuntime.isSymbol(id)) { + desc = getBuiltInDescriptor(((NativeSymbol) id).getKey()); + } + } + return desc; } private ScriptableObject getBuiltInDescriptor(String name) { - Object value = null; - int attr = EMPTY; - - Scriptable scope = getParentScope(); - if (scope == null) { - scope = this; - } - - int info = findInstanceIdInfo(name); - if (info != 0) { - int id = (info & 0xFFFF); - value = getInstanceIdValue(id); - attr = (info >>> 16); - return buildDataDescriptor(scope, value, attr); - } - if (prototypeValues != null) { - int id = prototypeValues.findId(name); - if (id != 0) { - value = prototypeValues.get(id); - attr = prototypeValues.getAttributes(id); - return buildDataDescriptor(scope, value, attr); + Object value = null; + int attr = EMPTY; + + Scriptable scope = getParentScope(); + if (scope == null) { + scope = this; } - } - return null; + + int info = findInstanceIdInfo(name); + if (info != 0) { + int id = (info & 0xFFFF); + value = getInstanceIdValue(id); + attr = (info >>> 16); + return buildDataDescriptor(scope, value, attr); + } + if (prototypeValues != null) { + int id = prototypeValues.findId(name); + if (id != 0) { + value = prototypeValues.get(id); + attr = prototypeValues.getAttributes(id); + return buildDataDescriptor(scope, value, attr); + } + } + return null; } private ScriptableObject getBuiltInDescriptor(Symbol key) { - Object value = null; - int attr = EMPTY; - - Scriptable scope = getParentScope(); - if (scope == null) { - scope = this; - } - - if (prototypeValues != null) { - int id = prototypeValues.findId(key); - if (id != 0) { - value = prototypeValues.get(id); - attr = prototypeValues.getAttributes(id); - return buildDataDescriptor(scope, value, attr); + Object value = null; + int attr = EMPTY; + + Scriptable scope = getParentScope(); + if (scope == null) { + scope = this; } - } - return null; + + if (prototypeValues != null) { + int id = prototypeValues.findId(key); + if (id != 0) { + value = prototypeValues.get(id); + attr = prototypeValues.getAttributes(id); + return buildDataDescriptor(scope, value, attr); + } + } + return null; } - private void readObject(ObjectInputStream stream) - throws IOException, ClassNotFoundException - { + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); int maxPrototypeId = stream.readInt(); if (maxPrototypeId != 0) { @@ -1051,9 +980,7 @@ private void readObject(ObjectInputStream stream) } } - private void writeObject(ObjectOutputStream stream) - throws IOException - { + private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); int maxPrototypeId = 0; if (prototypeValues != null) { @@ -1061,6 +988,4 @@ private void writeObject(ObjectOutputStream stream) } stream.writeInt(maxPrototypeId); } - } - diff --git a/src/org/mozilla/javascript/NativeArray.java b/src/org/mozilla/javascript/NativeArray.java index 5d7ed4ca12..bd35bcbff6 100644 --- a/src/org/mozilla/javascript/NativeArray.java +++ b/src/org/mozilla/javascript/NativeArray.java @@ -682,22 +682,35 @@ protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { @Override protected void defineOwnProperty( Context cx, Object id, ScriptableObject desc, boolean checkValid) { - if (dense != null) { + long index = toArrayIndex(id); + if (index >= length) { + length = index + 1; + modCount++; + } + + if (index != -1 && dense != null) { Object[] values = dense; dense = null; denseOnly = false; for (int i = 0; i < values.length; i++) { if (values[i] != NOT_FOUND) { + if (!isExtensible()) { + // Force creating a slot, before calling .put(...) on the next line, which + // would otherwise fail on a array on which preventExtensions() has been + // called + setAttributes(i, 0); + } put(i, this, values[i]); } } } - long index = toArrayIndex(id); - if (index >= length) { - length = index + 1; - modCount++; - } + super.defineOwnProperty(cx, id, desc, checkValid); + + if (id instanceof String && ((String) id).equals("length")) { + lengthAttr = + getAttributes("length"); // Update cached attributes value for length property + } } /** See ECMA 15.4.1,2 */ diff --git a/src/org/mozilla/javascript/NativeObject.java b/src/org/mozilla/javascript/NativeObject.java index e4a62f29ec..76578564f1 100644 --- a/src/org/mozilla/javascript/NativeObject.java +++ b/src/org/mozilla/javascript/NativeObject.java @@ -506,17 +506,8 @@ public Object execIdCall( return Boolean.TRUE; } - ScriptableObject obj = ensureScriptableObject(arg); - - if (obj.isExtensible()) return Boolean.FALSE; - - for (Object name : obj.getAllIds()) { - Object configurable = - obj.getOwnPropertyDescriptor(cx, name).get("configurable"); - if (Boolean.TRUE.equals(configurable)) return Boolean.FALSE; - } - - return Boolean.TRUE; + return AbstractEcmaObjectOperations.testIntegrityLevel( + cx, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.SEALED); } case ConstructorId_isFrozen: { @@ -526,18 +517,8 @@ public Object execIdCall( return Boolean.TRUE; } - ScriptableObject obj = ensureScriptableObject(arg); - - if (obj.isExtensible()) return Boolean.FALSE; - - for (Object name : obj.getAllIds()) { - ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, name); - if (Boolean.TRUE.equals(desc.get("configurable"))) return Boolean.FALSE; - if (isDataDescriptor(desc) && Boolean.TRUE.equals(desc.get("writable"))) - return Boolean.FALSE; - } - - return Boolean.TRUE; + return AbstractEcmaObjectOperations.testIntegrityLevel( + cx, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.FROZEN); } case ConstructorId_seal: { @@ -547,18 +528,10 @@ public Object execIdCall( return arg; } - ScriptableObject obj = ensureScriptableObject(arg); + AbstractEcmaObjectOperations.setIntegrityLevel( + cx, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.SEALED); - for (Object name : obj.getAllIds()) { - ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, name); - if (Boolean.TRUE.equals(desc.get("configurable"))) { - desc.put("configurable", desc, Boolean.FALSE); - obj.defineOwnProperty(cx, name, desc, false); - } - } - obj.preventExtensions(); - - return obj; + return arg; } case ConstructorId_freeze: { @@ -568,21 +541,10 @@ public Object execIdCall( return arg; } - ScriptableObject obj = ensureScriptableObject(arg); + AbstractEcmaObjectOperations.setIntegrityLevel( + cx, arg, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.FROZEN); - for (Object name : obj.getIds(true, true)) { - ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, name); - if (isDataDescriptor(desc) && Boolean.TRUE.equals(desc.get("writable"))) { - desc.put("writable", desc, Boolean.FALSE); - } - if (Boolean.TRUE.equals(desc.get("configurable"))) { - desc.put("configurable", desc, Boolean.FALSE); - } - obj.defineOwnProperty(cx, name, desc, false); - } - obj.preventExtensions(); - - return obj; + return arg; } case ConstructorId_assign: diff --git a/src/org/mozilla/javascript/ScriptableObject.java b/src/org/mozilla/javascript/ScriptableObject.java index 0a9264b286..301d719ff4 100644 --- a/src/org/mozilla/javascript/ScriptableObject.java +++ b/src/org/mozilla/javascript/ScriptableObject.java @@ -700,13 +700,11 @@ public Object[] getIds() { } /** - * Returns an array of ids for the properties of the object. - * - *

All properties, even those with attribute DONTENUM, are listed. + * Returns an array of ids of all non-Symbol properties of the object. * - *

+ *

All non-Symbol properties, even those with attribute DONTENUM, are listed. * - * @return an array of java.lang.Objects with an entry for every listed property. Properties + * @return an array of java.lang.Objects with an entry for every non-Symbol property. Properties * accessed via an integer index will have a corresponding Integer entry in the returned * array. Properties accessed by a String will have a String entry in the returned array. */ @@ -2077,6 +2075,7 @@ public static void redefineProperty(Scriptable obj, String name, boolean isConst } if (isConst) throw ScriptRuntime.typeErrorById("msg.var.redecl", name); } + /** * Returns whether an indexed property is defined in an object or any object in its prototype * chain. diff --git a/testsrc/test262.properties b/testsrc/test262.properties index cea2ffd5a6..4e107c5153 100644 --- a/testsrc/test262.properties +++ b/testsrc/test262.properties @@ -819,8 +819,6 @@ built-ins/Object ! prototype/toString/symbol-tag-str.js ! prototype/valueOf/S15.2.4.4_A14.js ! prototype/valueOf/S15.2.4.4_A15.js - ! seal/symbol-object-contains-symbol-properties-non-strict.js - ! seal/symbol-object-contains-symbol-properties-strict.js ! setPrototypeOf/set-error.js ! values/exception-during-enumeration.js ! values/function-length.js