org.mozilla.javascript.IdScriptableObject Maven / Gradle / Ivy
Show all versions of rhino Show documentation
/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.javascript;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* 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
*
*
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;
private transient PrototypeValues prototypeValues;
private static final class PrototypeValues implements Serializable {
private static final long serialVersionUID = 3038645279153854371L;
private static final int NAME_SLOT = 1;
private static final int SLOT_SPAN = 2;
private IdScriptableObject obj;
private int maxId;
private Object[] valueArray;
private short[] attributeArray;
// The following helps to avoid creation of valueArray during runtime
// initialization for common case of "constructor" property
int constructorId;
private IdFunctionObject constructor;
private short constructorAttrs;
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() {
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();
ScriptableObject.checkValidAttributes(attributes);
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");
}
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();
ScriptableObject.checkValidAttributes(attributes);
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");
}
constructor = (IdFunctionObject) value;
constructorAttrs = (short) attributes;
return;
}
initSlot(id, key, value, attributes);
}
private void initSlot(int id, Object name, Object value, int attributes) {
Object[] array = valueArray;
if (array == null) throw new IllegalStateException();
if (value == null) {
value = UniqueTag.NULL_VALUE;
}
int index = (id - 1) * SLOT_SPAN;
synchronized (this) {
Object value2 = array[index];
if (value2 == null) {
array[index] = value;
array[index + NAME_SLOT] = name;
attributeArray[id - 1] = (short) attributes;
} else {
if (!name.equals(array[index + NAME_SLOT])) throw new IllegalStateException();
}
}
}
final IdFunctionObject createPrecachedConstructor() {
if (constructorId != 0) throw new IllegalStateException();
constructorId = obj.findPrototypeId("constructor");
if (constructorId == 0) {
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);
}
constructor.initFunction(obj.getClassName(), ScriptableObject.getTopLevelScope(obj));
constructor.markAsConstructor(obj);
return constructor;
}
final int findId(String name) {
return obj.findPrototypeId(name);
}
final int findId(Symbol key) {
return obj.findPrototypeId(key);
}
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;
Object value = array[valueSlot];
if (value == null) {
// The particular entry has not been yet initialized
return true;
}
return value != NOT_FOUND;
}
final Object get(int id) {
Object value = ensureId(id);
if (value == UniqueTag.NULL_VALUE) {
value = null;
}
return value;
}
final void set(int id, Scriptable start, Object value) {
if (value == NOT_FOUND) throw new IllegalArgumentException();
ensureId(id);
int attr = attributeArray[id - 1];
if ((attr & READONLY) == 0) {
if (start == obj) {
if (value == null) {
value = UniqueTag.NULL_VALUE;
}
int valueSlot = (id - 1) * SLOT_SPAN;
synchronized (this) {
valueArray[valueSlot] = value;
}
} 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);
}
} else {
start.put((String) name, start, value);
}
}
}
}
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);
}
} else {
int valueSlot = (id - 1) * SLOT_SPAN;
synchronized (this) {
valueArray[valueSlot] = NOT_FOUND;
attributeArray[id - 1] = EMPTY;
}
}
}
final int getAttributes(int id) {
ensureId(id);
return attributeArray[id - 1];
}
final void setAttributes(int id, int attributes) {
ScriptableObject.checkValidAttributes(attributes);
ensureId(id);
synchronized (this) {
attributeArray[id - 1] = (short) attributes;
}
}
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;
Object name = valueArray[nameSlot];
if (name instanceof String) {
if (names == null) {
names = new Object[maxId];
}
names[count++] = name;
} else if (getSymbols && (name instanceof Symbol)) {
if (names == null) {
names = new Object[maxId];
}
names[count++] = name.toString();
}
}
}
}
if (count == 0) {
return extraEntries;
} else if (extraEntries == null || extraEntries.length == 0) {
if (count != names.length) {
Object[] tmp = new Object[count];
System.arraycopy(names, 0, tmp, 0, count);
names = tmp;
}
return names;
} else {
int extra = extraEntries.length;
Object[] tmp = new Object[extra + count];
System.arraycopy(extraEntries, 0, tmp, 0, extra);
System.arraycopy(names, 0, tmp, extra, count);
return tmp;
}
}
private Object ensureId(int id) {
Object[] array = valueArray;
if (array == null) {
synchronized (this) {
array = valueArray;
if (array == null) {
array = new Object[maxId * SLOT_SPAN];
valueArray = array;
attributeArray = new short[maxId];
}
}
}
int valueSlot = (id - 1) * SLOT_SPAN;
Object value = array[valueSlot];
if (value == null) {
if (id == constructorId) {
initSlot(constructorId, "constructor", constructor, constructorAttrs);
constructor = null; // no need to refer it any longer
} else {
obj.initPrototypeId(id);
}
value = array[valueSlot];
if (value == null) {
throw new IllegalStateException(
obj.getClass().getName()
+ ".initPrototypeId(int id) "
+ "did not initialize id="
+ id);
}
}
return value;
}
}
public IdScriptableObject() {}
public IdScriptableObject(Scriptable scope, Scriptable prototype) {
super(scope, prototype);
}
protected final boolean defaultHas(String name) {
return super.has(name, this);
}
protected final Object defaultGet(String name) {
return super.get(name, this);
}
protected final void defaultPut(String name, Object value) {
super.put(name, this, value);
}
@Override
public boolean has(String name, Scriptable start) {
int info = findInstanceIdInfo(name);
if (info != 0) {
int attr = (info >>> 16);
if ((attr & PERMANENT) != 0) {
return true;
}
int id = (info & 0xFFFF);
return NOT_FOUND != getInstanceIdValue(id);
}
if (prototypeValues != null) {
int id = prototypeValues.findId(name);
if (id != 0) {
return prototypeValues.has(id);
}
}
return super.has(name, start);
}
@Override
public boolean has(Symbol key, Scriptable start) {
int info = findInstanceIdInfo(key);
if (info != 0) {
int attr = (info >>> 16);
if ((attr & PERMANENT) != 0) {
return true;
}
int id = (info & 0xFFFF);
return NOT_FOUND != getInstanceIdValue(id);
}
if (prototypeValues != null) {
int id = prototypeValues.findId(key);
if (id != 0) {
return prototypeValues.has(id);
}
}
return super.has(key, start);
}
@Override
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);
if (value != NOT_FOUND) {
return value;
}
int info = findInstanceIdInfo(name);
if (info != 0) {
int id = (info & 0xFFFF);
value = getInstanceIdValue(id);
if (value != NOT_FOUND) return value;
}
if (prototypeValues != null) {
int id = prototypeValues.findId(name);
if (id != 0) {
value = prototypeValues.get(id);
if (value != NOT_FOUND) return value;
}
}
return NOT_FOUND;
}
@Override
public Object get(Symbol key, Scriptable start) {
Object value = super.get(key, start);
if (value != NOT_FOUND) {
return value;
}
int info = findInstanceIdInfo(key);
if (info != 0) {
int id = (info & 0xFFFF);
value = getInstanceIdValue(id);
if (value != NOT_FOUND) return value;
}
if (prototypeValues != null) {
int id = prototypeValues.findId(key);
if (id != 0) {
value = prototypeValues.get(id);
if (value != NOT_FOUND) return value;
}
}
return NOT_FOUND;
}
@Override
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);
}
int attr = (info >>> 16);
if ((attr & READONLY) == 0) {
if (start == this) {
int id = (info & 0xFFFF);
setInstanceIdValue(id, value);
} else {
start.put(name, start, value);
}
}
return;
}
if (prototypeValues != null) {
int id = prototypeValues.findId(name);
if (id != 0) {
if (start == this && isSealed()) {
throw Context.reportRuntimeErrorById("msg.modify.sealed", name);
}
prototypeValues.set(id, start, value);
return;
}
}
super.put(name, start, value);
}
@Override
public void put(Symbol key, Scriptable start, Object value) {
int info = findInstanceIdInfo(key);
if (info != 0) {
if (start == this && isSealed()) {
throw Context.reportRuntimeErrorById("msg.modify.sealed");
}
int attr = (info >>> 16);
if ((attr & READONLY) == 0) {
if (start == this) {
int id = (info & 0xFFFF);
setInstanceIdValue(id, value);
} else {
ensureSymbolScriptable(start).put(key, start, value);
}
}
return;
}
if (prototypeValues != null) {
int id = prototypeValues.findId(key);
if (id != 0) {
if (start == this && isSealed()) {
throw Context.reportRuntimeErrorById("msg.modify.sealed");
}
prototypeValues.set(id, start, value);
return;
}
}
super.put(key, start, value);
}
@Override
public void delete(String name) {
int info = findInstanceIdInfo(name);
if (info != 0) {
// Let the super class to throw exceptions for sealed objects
if (!isSealed()) {
int attr = (info >>> 16);
// non-configurable
if ((attr & PERMANENT) != 0) {
Context cx = Context.getContext();
if (cx.isStrictMode()) {
throw ScriptRuntime.typeErrorById(
"msg.delete.property.with.configurable.false", name);
}
} else {
int id = (info & 0xFFFF);
setInstanceIdValue(id, NOT_FOUND);
}
return;
}
}
if (prototypeValues != null) {
int id = prototypeValues.findId(name);
if (id != 0) {
if (!isSealed()) {
prototypeValues.delete(id);
}
return;
}
}
super.delete(name);
}
@Override
public void delete(Symbol key) {
int info = findInstanceIdInfo(key);
if (info != 0) {
// Let the super class to throw exceptions for sealed objects
if (!isSealed()) {
int attr = (info >>> 16);
// non-configurable
if ((attr & PERMANENT) != 0) {
Context cx = Context.getContext();
if (cx.isStrictMode()) {
throw ScriptRuntime.typeErrorById(
"msg.delete.property.with.configurable.false");
}
} else {
int id = (info & 0xFFFF);
setInstanceIdValue(id, NOT_FOUND);
}
return;
}
}
if (prototypeValues != null) {
int id = prototypeValues.findId(key);
if (id != 0) {
if (!isSealed()) {
prototypeValues.delete(id);
}
return;
}
}
super.delete(key);
}
@Override
public int getAttributes(String name) {
int info = findInstanceIdInfo(name);
if (info != 0) {
int attr = (info >>> 16);
return attr;
}
if (prototypeValues != null) {
int id = prototypeValues.findId(name);
if (id != 0) {
return prototypeValues.getAttributes(id);
}
}
return super.getAttributes(name);
}
@Override
public int getAttributes(Symbol key) {
int info = findInstanceIdInfo(key);
if (info != 0) {
int attr = (info >>> 16);
return attr;
}
if (prototypeValues != null) {
int id = prototypeValues.findId(key);
if (id != 0) {
return prototypeValues.getAttributes(id);
}
}
return super.getAttributes(key);
}
@Override
public void setAttributes(String name, int attributes) {
ScriptableObject.checkValidAttributes(attributes);
int info = findInstanceIdInfo(name);
if (info != 0) {
int id = (info & 0xFFFF);
int currentAttributes = (info >>> 16);
if (attributes != currentAttributes) {
setInstanceIdAttributes(id, attributes);
}
return;
}
if (prototypeValues != null) {
int id = prototypeValues.findId(name);
if (id != 0) {
prototypeValues.setAttributes(id, attributes);
return;
}
}
super.setAttributes(name, attributes);
}
@Override
Object[] getIds(boolean getNonEnumerable, boolean getSymbols) {
Object[] result = super.getIds(getNonEnumerable, getSymbols);
if (prototypeValues != null) {
result = prototypeValues.getNames(getNonEnumerable, getSymbols, result);
}
int maxInstanceId = getMaxInstanceId();
if (maxInstanceId != 0) {
Object[] ids = null;
int count = 0;
for (int id = maxInstanceId; id != 0; --id) {
String name = getInstanceIdName(id);
int info = findInstanceIdInfo(name);
if (info != 0) {
int attr = (info >>> 16);
if ((attr & PERMANENT) == 0) {
if (NOT_FOUND == getInstanceIdValue(id)) {
continue;
}
}
if (getNonEnumerable || (attr & DONTENUM) == 0) {
if (count == 0) {
// Need extra room for no more then [1..id] names
ids = new Object[id];
}
ids[count++] = name;
}
}
}
if (count != 0) {
if (result.length == 0 && ids.length == count) {
result = ids;
} else {
Object[] tmp = new Object[result.length + count];
System.arraycopy(result, 0, tmp, 0, result.length);
System.arraycopy(ids, 0, tmp, result.length, count);
result = tmp;
}
}
}
return result;
}
/** Get maximum id findInstanceIdInfo can generate. */
protected int getMaxInstanceId() {
return 0;
}
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)}.
*/
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)}.
*/
protected int findInstanceIdInfo(Symbol key) {
return 0;
}
/** 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
*/
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.
*/
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.
*
* @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");
}
/**
* '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) {
throw f.unknown();
}
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);
setPrototype(getObjectPrototype(scope));
}
activatePrototypeMap(maxPrototypeId);
IdFunctionObject ctor = prototypeValues.createPrecachedConstructor();
if (sealed) {
sealObject();
}
fillConstructorProperties(ctor);
if (sealed) {
ctor.sealObject();
}
ctor.exportAsScopeProperty();
return ctor;
}
public final boolean hasPrototypeMap() {
return prototypeValues != null;
}
public final void activatePrototypeMap(int maxPrototypeId) {
PrototypeValues values = new PrototypeValues(this, maxPrototypeId);
synchronized (this) {
if (prototypeValues != null) throw new IllegalStateException();
prototypeValues = values;
}
}
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) {
Scriptable scope = ScriptableObject.getTopLevelScope(this);
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) {
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) {
int id = prototypeValues.constructorId;
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) {
prototypeValues.initValue(id, name, value, attributes);
}
public final void initPrototypeValue(int id, Symbol key, Object value, int attributes) {
prototypeValues.initValue(id, key, value, attributes);
}
protected void initPrototypeId(int id) {
throw new IllegalStateException(String.valueOf(id));
}
protected int findPrototypeId(String name) {
throw new IllegalStateException(name);
}
protected int findPrototypeId(Symbol key) {
return 0;
}
protected void fillConstructorProperties(IdFunctionObject ctor) {}
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.
* 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.
* @return obj casted to the target type
* @throws EcmaError if the cast failed.
*/
@SuppressWarnings("unchecked")
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(),
obj.getClass().getName(),
clazz.getName());
}
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);
} else {
function = new IdFunctionObjectES6(this, tag, id, name, arity, scope);
}
if (isSealed()) {
function.sealObject();
}
return function;
}
@Override
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 || (attr & PERMANENT) == 0)) {
Object currentValue = getInstanceIdValue(id);
if (!sameValue(value, currentValue)) {
setInstanceIdValue(id, value);
}
}
attr = applyDescriptorToAttributeBitset(attr, desc);
setAttributes(name, attr);
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));
// 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;
}
}
}
}
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) {
return getBuiltInDescriptor((String) id);
}
if (ScriptRuntime.isSymbol(id)) {
if (id instanceof SymbolKey) {
return getBuiltInDescriptor((SymbolKey) id);
}
return 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);
}
}
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);
}
}
return null;
}
private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
stream.defaultReadObject();
int maxPrototypeId = stream.readInt();
if (maxPrototypeId != 0) {
activatePrototypeMap(maxPrototypeId);
}
}
private void writeObject(ObjectOutputStream stream) throws IOException {
stream.defaultWriteObject();
int maxPrototypeId = 0;
if (prototypeValues != null) {
maxPrototypeId = prototypeValues.getMaxId();
}
stream.writeInt(maxPrototypeId);
}
}