diff --git a/js/rhino/src/org/mozilla/javascript/IdScriptable.java b/js/rhino/src/org/mozilla/javascript/IdScriptable.java
index 585fa147d0bb..c25cc351d1aa 100644
--- a/js/rhino/src/org/mozilla/javascript/IdScriptable.java
+++ b/js/rhino/src/org/mozilla/javascript/IdScriptable.java
@@ -69,7 +69,7 @@ public abstract class IdScriptable extends ScriptableObject
public boolean has(String name, Scriptable start) {
if (maxId != 0) {
- int id = getId(name);
+ int id = mapNameToId(name);
if (id != 0) {
return hasValue(id);
}
@@ -78,14 +78,24 @@ public abstract class IdScriptable extends ScriptableObject
}
public Object get(String name, Scriptable start) {
- if (maxId != 0) {
- int id = getId(name);
- if (id != 0) {
+ if (CACHE_NAMES) {
+ int maxId = this.maxId;
+ L:if (maxId != 0) {
Object[] data = idMapData;
- if (data == null) {
- return getIdValue(id);
+ if (data == null) {
+ int id = mapNameToId(name);
+ if (id != 0) {
+ return getIdValue(id);
+ }
}
else {
+ int id = lastIdCache;
+ if (data[id - 1 + maxId] != name) {
+ id = mapNameToId(name);
+ if (id == 0) { break L; }
+ data[id - 1 + maxId] = name;
+ lastIdCache = id;
+ }
Object value = data[id - 1];
if (value == null) {
value = getIdValue(id);
@@ -97,12 +107,33 @@ public abstract class IdScriptable extends ScriptableObject
}
}
}
+ else {
+ if (maxId != 0) {
+ int id = mapNameToId(name);
+ if (id != 0) {
+ Object[] data = idMapData;
+ if (data == null) {
+ return getIdValue(id);
+ }
+ else {
+ Object value = data[id - 1];
+ if (value == null) {
+ value = getIdValue(id);
+ }
+ else if (value == NULL_TAG) {
+ value = null;
+ }
+ return value;
+ }
+ }
+ }
+ }
return super.get(name, start);
}
public void put(String name, Scriptable start, Object value) {
if (maxId != 0) {
- int id = getId(name);
+ int id = mapNameToId(name);
if (id != 0) {
int attr = getAttributes(id);
if ((attr & READONLY) == 0) {
@@ -121,7 +152,7 @@ public abstract class IdScriptable extends ScriptableObject
public void delete(String name) {
if (maxId != 0) {
- int id = getId(name);
+ int id = mapNameToId(name);
if (id != 0) {
// Let the super class to throw exceptions for sealed objects
if (!isSealed()) {
@@ -140,7 +171,7 @@ public abstract class IdScriptable extends ScriptableObject
throws PropertyException
{
if (maxId != 0) {
- int id = getId(name);
+ int id = mapNameToId(name);
if (id != 0) {
if (hasValue(id)) {
return getAttributes(id);
@@ -156,7 +187,7 @@ public abstract class IdScriptable extends ScriptableObject
throws PropertyException
{
if (maxId != 0) {
- int id = getId(name);
+ int id = mapNameToId(name);
if (id != 0) {
if (hasValue(id)) {
synchronized (this) {
@@ -171,10 +202,37 @@ public abstract class IdScriptable extends ScriptableObject
}
synchronized void addPropertyAttribute(int attribute) {
- extraIdAttributes |= attribute;
+ extraIdAttributes |= (byte)attribute;
super.addPropertyAttribute(attribute);
}
+ /**
+ * Redefine ScriptableObject.defineProperty to allow changing
+ * values/attributes of id-based properties unless
+ * getIdDefaultAttributes contains the READONLY attribute.
+ * @see #getIdDefaultAttributes
+ * @see org.mozilla.javascript.ScriptableObject#defineProperty
+ */
+ public void defineProperty(String propertyName, Object value,
+ int attributes)
+ {
+ if (maxId != 0) {
+ int id = mapNameToId(propertyName);
+ if (id != 0) {
+ int default_attributes = getIdDefaultAttributes(id);
+ if ((default_attributes & READONLY) != 0) {
+ // It is a bug to redefine id with readonly attributes
+ throw new RuntimeException
+ ("Attempt to redefine read-only id " + propertyName);
+ }
+ setAttributes(id, attributes);
+ setIdValue(id, value);
+ return;
+ }
+ }
+ super.defineProperty(propertyName, value, attributes);
+ }
+
Object[] getIds(boolean getAll) {
Object[] result = super.getIds(getAll);
@@ -249,15 +307,23 @@ public abstract class IdScriptable extends ScriptableObject
return cacheIdValue(id, f);
}
- /** Set id value. */
+ /**
+ * Set id value.
+ * IdScriptable never calls this method if result of
+ * getIdDefaultAttributes(id)
contains READONLY attribute.
+ * Descendants can overwrite this method to provide custom handler for
+ * property assignments.
+ */
protected void setIdValue(int id, Object value) {
synchronized (this) {
ensureIdData()[id - 1] = (value != null) ? value : NULL_TAG;
}
}
- /** Store value in a permamnet cache.
- ** After this call hasIdValue and getIdValue will never be called for id.
+ /**
+ * Store value in permanent cache unless value was already assigned to id.
+ * After this call IdScriptable never calls hasIdValue and getIdValue
+ * for the given id.
*/
protected Object cacheIdValue(int id, Object value) {
synchronized (this) {
@@ -273,8 +339,12 @@ public abstract class IdScriptable extends ScriptableObject
return value;
}
- /** Delete value represented by id so hasIdValue return false.
- ** This will be called only for id without PERMANENT attribute.
+ /**
+ * Delete value represented by id so hasIdValue return false.
+ * IdScriptable never calls this method if result of
+ * getIdDefaultAttributes(id)
contains PERMANENT attribute.
+ * Descendants can overwrite this method to provide custom handler for
+ * property delete.
*/
protected void deleteIdValue(int id) {
synchronized (this) {
@@ -306,8 +376,7 @@ public abstract class IdScriptable extends ScriptableObject
/** Sets whether newly constructed function objects should be sealed */
protected void setSealFunctionsFlag(boolean sealed) {
- if (sealed) { setupFlags |= SEAL_FUNCTIONS_FLAG; }
- else { setupFlags &= ~SEAL_FUNCTIONS_FLAG; }
+ setSetupFlag(SEAL_FUNCTIONS_FLAG, sealed);
}
/**
@@ -318,9 +387,12 @@ public abstract class IdScriptable extends ScriptableObject
* @see org.mozilla.javascript.Context#hasCompileFunctionsWithDynamicScope
*/
protected void setFunctionParametrs(Context cx) {
- if (cx.hasCompileFunctionsWithDynamicScope()) {
- setupFlags |= USE_DYNAMIC_SCOPE_FLAG;
- }
+ setSetupFlag(USE_DYNAMIC_SCOPE_FLAG,
+ cx.hasCompileFunctionsWithDynamicScope());
+ }
+
+ private void setSetupFlag(int flag, boolean value) {
+ setupFlags = (byte)(value ? setupFlags | flag : setupFlags & ~flag);
}
/**
@@ -437,22 +509,6 @@ public abstract class IdScriptable extends ScriptableObject
return x ? Boolean.TRUE : Boolean.FALSE;
}
- private int getId(String name) {
- Object[] data = idMapData;
- if (data == null || !CACHE_NAMES) { return mapNameToId(name); }
- else {
- int id = lastIdCache;
- if (data[id - 1 + maxId] != name) {
- id = mapNameToId(name);
- if (id != 0) {
- data[id - 1 + maxId] = name;
- lastIdCache = id;
- }
- }
- return id;
- }
- }
-
private boolean hasValue(int id) {
Object value;
Object[] data = idMapData;
@@ -505,18 +561,17 @@ public abstract class IdScriptable extends ScriptableObject
}
}
- private static final boolean CACHE_NAMES = true;
-
private int maxId;
private Object[] idMapData;
private byte[] attributesArray;
- private int extraIdAttributes;
+ private static final boolean CACHE_NAMES = true;
private int lastIdCache;
private static final int USE_DYNAMIC_SCOPE_FLAG = 1 << 0;
private static final int SEAL_FUNCTIONS_FLAG = 1 << 1;
- private int setupFlags;
+ private byte setupFlags;
+ private byte extraIdAttributes;
}