742 lines
24 KiB
Java
742 lines
24 KiB
Java
|
/* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-
|
||
|
*
|
||
|
* ***** BEGIN LICENSE BLOCK *****
|
||
|
* Version: MPL 1.1/GPL 2.0
|
||
|
*
|
||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||
|
* the License. You may obtain a copy of the License at
|
||
|
* http://www.mozilla.org/MPL/
|
||
|
*
|
||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||
|
* for the specific language governing rights and limitations under the
|
||
|
* License.
|
||
|
*
|
||
|
* The Original Code is Rhino code, released
|
||
|
* May 6, 1999.
|
||
|
*
|
||
|
* The Initial Developer of the Original Code is
|
||
|
* Netscape Communications Corporation.
|
||
|
* Portions created by the Initial Developer are Copyright (C) 1997-1999
|
||
|
* the Initial Developer. All Rights Reserved.
|
||
|
*
|
||
|
* Contributor(s):
|
||
|
* Igor Bukanov
|
||
|
*
|
||
|
* Alternatively, the contents of this file may be used under the terms of
|
||
|
* the GNU General Public License Version 2 or later (the "GPL"), in which
|
||
|
* case the provisions of the GPL are applicable instead of those above. If
|
||
|
* you wish to allow use of your version of this file only under the terms of
|
||
|
* the GPL and not to allow others to use your version of this file under the
|
||
|
* MPL, indicate your decision by deleting the provisions above and replacing
|
||
|
* them with the notice and other provisions required by the GPL. If you do
|
||
|
* not delete the provisions above, a recipient may use your version of this
|
||
|
* file under either the MPL or the GPL.
|
||
|
*
|
||
|
* ***** END LICENSE BLOCK ***** */
|
||
|
|
||
|
package org.mozilla.javascript;
|
||
|
|
||
|
import java.io.*;
|
||
|
|
||
|
/**
|
||
|
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 initializition of constructor and protype objects, descendant
|
||
|
may override scopeInit or fillConstructorProperties methods.
|
||
|
|
||
|
*/
|
||
|
public abstract class IdScriptableObject extends ScriptableObject
|
||
|
implements IdFunctionCall
|
||
|
{
|
||
|
private transient volatile PrototypeValues prototypeValues;
|
||
|
|
||
|
private static final class PrototypeValues implements Serializable
|
||
|
{
|
||
|
static final long serialVersionUID = 3038645279153854371L;
|
||
|
|
||
|
private static final int VALUE_SLOT = 0;
|
||
|
private static final int NAME_SLOT = 1;
|
||
|
private static final int SLOT_SPAN = 2;
|
||
|
|
||
|
private IdScriptableObject obj;
|
||
|
private int maxId;
|
||
|
private volatile Object[] valueArray;
|
||
|
private volatile short[] attributeArray;
|
||
|
private volatile int lastFoundId = 1;
|
||
|
|
||
|
// 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);
|
||
|
}
|
||
|
|
||
|
private void initSlot(int id, String 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 + VALUE_SLOT];
|
||
|
if (value2 == null) {
|
||
|
array[index + VALUE_SLOT] = 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)
|
||
|
{
|
||
|
Object[] array = valueArray;
|
||
|
if (array == null) {
|
||
|
return obj.findPrototypeId(name);
|
||
|
}
|
||
|
int id = lastFoundId;
|
||
|
if (name == array[(id - 1) * SLOT_SPAN + NAME_SLOT]) {
|
||
|
return id;
|
||
|
}
|
||
|
id = obj.findPrototypeId(name);
|
||
|
if (id != 0) {
|
||
|
int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT;
|
||
|
// Make cache to work!
|
||
|
array[nameSlot] = name;
|
||
|
lastFoundId = id;
|
||
|
}
|
||
|
return 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 + VALUE_SLOT;
|
||
|
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 + VALUE_SLOT;
|
||
|
synchronized (this) {
|
||
|
valueArray[valueSlot] = value;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT;
|
||
|
String name = (String)valueArray[nameSlot];
|
||
|
start.put(name, start, value);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
final void delete(int id)
|
||
|
{
|
||
|
ensureId(id);
|
||
|
int attr = attributeArray[id - 1];
|
||
|
if ((attr & PERMANENT) == 0) {
|
||
|
int valueSlot = (id - 1) * SLOT_SPAN + VALUE_SLOT;
|
||
|
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, 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;
|
||
|
String name = (String)valueArray[nameSlot];
|
||
|
if (names == null) {
|
||
|
names = new Object[maxId];
|
||
|
}
|
||
|
names[count++] = name;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
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 + VALUE_SLOT;
|
||
|
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 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 Object get(String name, Scriptable start)
|
||
|
{
|
||
|
int info = findInstanceIdInfo(name);
|
||
|
if (info != 0) {
|
||
|
int id = (info & 0xFFFF);
|
||
|
return getInstanceIdValue(id);
|
||
|
}
|
||
|
if (prototypeValues != null) {
|
||
|
int id = prototypeValues.findId(name);
|
||
|
if (id != 0) {
|
||
|
return prototypeValues.get(id);
|
||
|
}
|
||
|
}
|
||
|
return super.get(name, start);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void put(String name, Scriptable start, Object value)
|
||
|
{
|
||
|
int info = findInstanceIdInfo(name);
|
||
|
if (info != 0) {
|
||
|
if (start == this && isSealed()) {
|
||
|
throw Context.reportRuntimeError1("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.reportRuntimeError1("msg.modify.sealed",
|
||
|
name);
|
||
|
}
|
||
|
prototypeValues.set(id, start, value);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
super.put(name, 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);
|
||
|
if ((attr & PERMANENT) == 0) {
|
||
|
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 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 void setAttributes(String name, int attributes)
|
||
|
{
|
||
|
ScriptableObject.checkValidAttributes(attributes);
|
||
|
int info = findInstanceIdInfo(name);
|
||
|
if (info != 0) {
|
||
|
int currentAttributes = (info >>> 16);
|
||
|
if (attributes != currentAttributes) {
|
||
|
throw new RuntimeException(
|
||
|
"Change of attributes for this id is not supported");
|
||
|
}
|
||
|
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 getAll)
|
||
|
{
|
||
|
Object[] result = super.getIds(getAll);
|
||
|
|
||
|
if (prototypeValues != null) {
|
||
|
result = prototypeValues.getNames(getAll, 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 (getAll || (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 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));
|
||
|
}
|
||
|
|
||
|
/** 'thisObj' will be null if invoked as constructor, in which case
|
||
|
** instance of Scriptable should be returned. */
|
||
|
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 void initPrototypeMethod(Object tag, int id, String name,
|
||
|
int arity)
|
||
|
{
|
||
|
Scriptable scope = ScriptableObject.getTopLevelScope(this);
|
||
|
IdFunctionObject f = newIdFunction(tag, id, name, arity, scope);
|
||
|
prototypeValues.initValue(id, name, f, DONTENUM);
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
protected void initPrototypeId(int id)
|
||
|
{
|
||
|
throw new IllegalStateException(String.valueOf(id));
|
||
|
}
|
||
|
|
||
|
protected int findPrototypeId(String name)
|
||
|
{
|
||
|
throw new IllegalStateException(name);
|
||
|
}
|
||
|
|
||
|
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 construct type error to indicate incompatible call
|
||
|
* when converting script thisObj to a particular type is not possible.
|
||
|
* Possible usage would be to have a private function like realThis:
|
||
|
* <pre>
|
||
|
* private static NativeSomething realThis(Scriptable thisObj,
|
||
|
* IdFunctionObject f)
|
||
|
* {
|
||
|
* if (!(thisObj instanceof NativeSomething))
|
||
|
* throw incompatibleCallError(f);
|
||
|
* return (NativeSomething)thisObj;
|
||
|
* }
|
||
|
* </pre>
|
||
|
* Note that although such function can be implemented universally via
|
||
|
* java.lang.Class.isInstance(), it would be much more slower.
|
||
|
* @param f function that is attempting to convert 'this'
|
||
|
* object.
|
||
|
* @return Scriptable object suitable for a check by the instanceof
|
||
|
* operator.
|
||
|
* @throws RuntimeException if no more instanceof target can be found
|
||
|
*/
|
||
|
protected static EcmaError incompatibleCallError(IdFunctionObject f)
|
||
|
{
|
||
|
throw ScriptRuntime.typeError1("msg.incompat.call",
|
||
|
f.getFunctionName());
|
||
|
}
|
||
|
|
||
|
private IdFunctionObject newIdFunction(Object tag, int id, String name,
|
||
|
int arity, Scriptable scope)
|
||
|
{
|
||
|
IdFunctionObject f = new IdFunctionObject(this, tag, id, name, arity,
|
||
|
scope);
|
||
|
if (isSealed()) { f.sealObject(); }
|
||
|
return f;
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|