diff options
author | Egil Moeller <egil.moller@freecode.no> | 2010-03-22 00:16:53 +0100 |
---|---|---|
committer | Egil Moeller <egil.moller@freecode.no> | 2010-03-22 00:16:53 +0100 |
commit | fc00cfedd6c25d1634a1e0d28a7b7bc5f42e1cd7 (patch) | |
tree | 928419d6bfe794865e4b8f0634bd792f795c4b8a /trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptableObject.java | |
parent | 0fdab3db005c4fa31fb8306b573c0b35073d3e56 (diff) | |
parent | d56b9b3b82cdebcaeb00eec0fcb4326ad21adaa8 (diff) | |
download | etherpad-fc00cfedd6c25d1634a1e0d28a7b7bc5f42e1cd7.tar.gz etherpad-fc00cfedd6c25d1634a1e0d28a7b7bc5f42e1cd7.tar.xz etherpad-fc00cfedd6c25d1634a1e0d28a7b7bc5f42e1cd7.zip |
Merge branch 'master' into plugin-framework
Diffstat (limited to 'trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptableObject.java')
-rw-r--r-- | trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptableObject.java | 2428 |
1 files changed, 0 insertions, 2428 deletions
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptableObject.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptableObject.java deleted file mode 100644 index 53de1fc..0000000 --- a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptableObject.java +++ /dev/null @@ -1,2428 +0,0 @@ -/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; 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): - * Norris Boyd - * Igor Bukanov - * Bob Jervis - * Roger Lawrence - * Steve Weiss - * - * 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 ***** */ - -// API class - -package org.mozilla.javascript; - -import java.lang.reflect.*; -import java.util.Hashtable; -import java.io.*; -import org.mozilla.javascript.debug.DebuggableObject; - -/** - * This is the default implementation of the Scriptable interface. This - * class provides convenient default behavior that makes it easier to - * define host objects. - * <p> - * Various properties and methods of JavaScript objects can be conveniently - * defined using methods of ScriptableObject. - * <p> - * Classes extending ScriptableObject must define the getClassName method. - * - * @see org.mozilla.javascript.Scriptable - * @author Norris Boyd - */ - -public abstract class ScriptableObject implements Scriptable, Serializable, - DebuggableObject, - ConstProperties -{ - - /** - * The empty property attribute. - * - * Used by getAttributes() and setAttributes(). - * - * @see org.mozilla.javascript.ScriptableObject#getAttributes(String) - * @see org.mozilla.javascript.ScriptableObject#setAttributes(String, int) - */ - public static final int EMPTY = 0x00; - - /** - * Property attribute indicating assignment to this property is ignored. - * - * @see org.mozilla.javascript.ScriptableObject - * #put(String, Scriptable, Object) - * @see org.mozilla.javascript.ScriptableObject#getAttributes(String) - * @see org.mozilla.javascript.ScriptableObject#setAttributes(String, int) - */ - public static final int READONLY = 0x01; - - /** - * Property attribute indicating property is not enumerated. - * - * Only enumerated properties will be returned by getIds(). - * - * @see org.mozilla.javascript.ScriptableObject#getIds() - * @see org.mozilla.javascript.ScriptableObject#getAttributes(String) - * @see org.mozilla.javascript.ScriptableObject#setAttributes(String, int) - */ - public static final int DONTENUM = 0x02; - - /** - * Property attribute indicating property cannot be deleted. - * - * @see org.mozilla.javascript.ScriptableObject#delete(String) - * @see org.mozilla.javascript.ScriptableObject#getAttributes(String) - * @see org.mozilla.javascript.ScriptableObject#setAttributes(String, int) - */ - public static final int PERMANENT = 0x04; - - /** - * Property attribute indicating that this is a const property that has not - * been assigned yet. The first 'const' assignment to the property will - * clear this bit. - */ - public static final int UNINITIALIZED_CONST = 0x08; - - public static final int CONST = PERMANENT|READONLY|UNINITIALIZED_CONST; - /** - * The prototype of this object. - */ - private Scriptable prototypeObject; - - /** - * The parent scope of this object. - */ - private Scriptable parentScopeObject; - - private static final Slot REMOVED = new Slot(null, 0, READONLY); - - static { - REMOVED.wasDeleted = 1; - } - - private transient Slot[] slots; - // If count >= 0, it gives number of keys or if count < 0, - // it indicates sealed object where ~count gives number of keys - private int count; - - // cache; may be removed for smaller memory footprint - private transient Slot lastAccess = REMOVED; - - // associated values are not serialized - private transient volatile Hashtable associatedValues; - - private static final int SLOT_QUERY = 1; - private static final int SLOT_MODIFY = 2; - private static final int SLOT_REMOVE = 3; - private static final int SLOT_MODIFY_GETTER_SETTER = 4; - private static final int SLOT_MODIFY_CONST = 5; - - private static class Slot implements Serializable - { - static final long serialVersionUID = -3539051633409902634L; - - String name; // This can change due to caching - int indexOrHash; - private volatile short attributes; - transient volatile byte wasDeleted; - volatile Object value; - transient volatile Slot next; - - Slot(String name, int indexOrHash, int attributes) - { - this.name = name; - this.indexOrHash = indexOrHash; - this.attributes = (short)attributes; - } - - private void readObject(ObjectInputStream in) - throws IOException, ClassNotFoundException - { - in.defaultReadObject(); - if (name != null) { - indexOrHash = name.hashCode(); - } - } - - final int getAttributes() - { - return attributes; - } - - final synchronized void setAttributes(int value) - { - checkValidAttributes(value); - attributes = (short)value; - } - - final void checkNotReadonly() - { - if ((attributes & READONLY) != 0) { - String str = (name != null ? name - : Integer.toString(indexOrHash)); - throw Context.reportRuntimeError1("msg.modify.readonly", str); - } - } - - } - - private static final class GetterSlot extends Slot - { - static final long serialVersionUID = -4900574849788797588L; - - Object getter; - Object setter; - - GetterSlot(String name, int indexOrHash, int attributes) - { - super(name, indexOrHash, attributes); - } - } - - static void checkValidAttributes(int attributes) - { - final int mask = READONLY | DONTENUM | PERMANENT | UNINITIALIZED_CONST; - if ((attributes & ~mask) != 0) { - throw new IllegalArgumentException(String.valueOf(attributes)); - } - } - - public ScriptableObject() - { - } - - public ScriptableObject(Scriptable scope, Scriptable prototype) - { - if (scope == null) - throw new IllegalArgumentException(); - - parentScopeObject = scope; - prototypeObject = prototype; - } - - /** - * Return the name of the class. - * - * This is typically the same name as the constructor. - * Classes extending ScriptableObject must implement this abstract - * method. - */ - public abstract String getClassName(); - - /** - * Returns true if the named property is defined. - * - * @param name the name of the property - * @param start the object in which the lookup began - * @return true if and only if the property was found in the object - */ - public boolean has(String name, Scriptable start) - { - return null != getSlot(name, 0, SLOT_QUERY); - } - - /** - * Returns true if the property index is defined. - * - * @param index the numeric index for the property - * @param start the object in which the lookup began - * @return true if and only if the property was found in the object - */ - public boolean has(int index, Scriptable start) - { - return null != getSlot(null, index, SLOT_QUERY); - } - - /** - * Returns the value of the named property or NOT_FOUND. - * - * If the property was created using defineProperty, the - * appropriate getter method is called. - * - * @param name the name of the property - * @param start the object in which the lookup began - * @return the value of the property (may be null), or NOT_FOUND - */ - public Object get(String name, Scriptable start) - { - return getImpl(name, 0, start); - } - - /** - * Returns the value of the indexed property or NOT_FOUND. - * - * @param index the numeric index for the property - * @param start the object in which the lookup began - * @return the value of the property (may be null), or NOT_FOUND - */ - public Object get(int index, Scriptable start) - { - return getImpl(null, index, start); - } - - /** - * Sets the value of the named property, creating it if need be. - * - * If the property was created using defineProperty, the - * appropriate setter method is called. <p> - * - * If the property's attributes include READONLY, no action is - * taken. - * This method will actually set the property in the start - * object. - * - * @param name the name of the property - * @param start the object whose property is being set - * @param value value to set the property to - */ - public void put(String name, Scriptable start, Object value) - { - if (putImpl(name, 0, start, value, EMPTY)) - return; - - if (start == this) throw Kit.codeBug(); - start.put(name, start, value); - } - - /** - * Sets the value of the indexed property, creating it if need be. - * - * @param index the numeric index for the property - * @param start the object whose property is being set - * @param value value to set the property to - */ - public void put(int index, Scriptable start, Object value) - { - if (putImpl(null, index, start, value, EMPTY)) - return; - - if (start == this) throw Kit.codeBug(); - start.put(index, start, value); - } - - /** - * Removes a named property from the object. - * - * If the property is not found, or it has the PERMANENT attribute, - * no action is taken. - * - * @param name the name of the property - */ - public void delete(String name) - { - checkNotSealed(name, 0); - accessSlot(name, 0, SLOT_REMOVE); - } - - /** - * Removes the indexed property from the object. - * - * If the property is not found, or it has the PERMANENT attribute, - * no action is taken. - * - * @param index the numeric index for the property - */ - public void delete(int index) - { - checkNotSealed(null, index); - accessSlot(null, index, SLOT_REMOVE); - } - - /** - * Sets the value of the named const property, creating it if need be. - * - * If the property was created using defineProperty, the - * appropriate setter method is called. <p> - * - * If the property's attributes include READONLY, no action is - * taken. - * This method will actually set the property in the start - * object. - * - * @param name the name of the property - * @param start the object whose property is being set - * @param value value to set the property to - */ - public void putConst(String name, Scriptable start, Object value) - { - if (putImpl(name, 0, start, value, READONLY)) - return; - - if (start == this) throw Kit.codeBug(); - if (start instanceof ConstProperties) - ((ConstProperties)start).putConst(name, start, value); - else - start.put(name, start, value); - } - - public void defineConst(String name, Scriptable start) - { - if (putImpl(name, 0, start, Undefined.instance, UNINITIALIZED_CONST)) - return; - - if (start == this) throw Kit.codeBug(); - if (start instanceof ConstProperties) - ((ConstProperties)start).defineConst(name, start); - } - /** - * Returns true if the named property is defined as a const on this object. - * @param name - * @return true if the named property is defined as a const, false - * otherwise. - */ - public boolean isConst(String name) - { - Slot slot = getSlot(name, 0, SLOT_QUERY); - if (slot == null) { - return false; - } - return (slot.getAttributes() & (PERMANENT|READONLY)) == - (PERMANENT|READONLY); - - } - /** - * @deprecated Use {@link #getAttributes(String name)}. The engine always - * ignored the start argument. - */ - public final int getAttributes(String name, Scriptable start) - { - return getAttributes(name); - } - - /** - * @deprecated Use {@link #getAttributes(int index)}. The engine always - * ignored the start argument. - */ - public final int getAttributes(int index, Scriptable start) - { - return getAttributes(index); - } - - /** - * @deprecated Use {@link #setAttributes(String name, int attributes)}. - * The engine always ignored the start argument. - */ - public final void setAttributes(String name, Scriptable start, - int attributes) - { - setAttributes(name, attributes); - } - - /** - * @deprecated Use {@link #setAttributes(int index, int attributes)}. - * The engine always ignored the start argument. - */ - public void setAttributes(int index, Scriptable start, - int attributes) - { - setAttributes(index, attributes); - } - - /** - * Get the attributes of a named property. - * - * The property is specified by <code>name</code> - * as defined for <code>has</code>.<p> - * - * @param name the identifier for the property - * @return the bitset of attributes - * @exception EvaluatorException if the named property is not found - * @see org.mozilla.javascript.ScriptableObject#has(String, Scriptable) - * @see org.mozilla.javascript.ScriptableObject#READONLY - * @see org.mozilla.javascript.ScriptableObject#DONTENUM - * @see org.mozilla.javascript.ScriptableObject#PERMANENT - * @see org.mozilla.javascript.ScriptableObject#EMPTY - */ - public int getAttributes(String name) - { - return findAttributeSlot(name, 0, SLOT_QUERY).getAttributes(); - } - - /** - * Get the attributes of an indexed property. - * - * @param index the numeric index for the property - * @exception EvaluatorException if the named property is not found - * is not found - * @return the bitset of attributes - * @see org.mozilla.javascript.ScriptableObject#has(String, Scriptable) - * @see org.mozilla.javascript.ScriptableObject#READONLY - * @see org.mozilla.javascript.ScriptableObject#DONTENUM - * @see org.mozilla.javascript.ScriptableObject#PERMANENT - * @see org.mozilla.javascript.ScriptableObject#EMPTY - */ - public int getAttributes(int index) - { - return findAttributeSlot(null, index, SLOT_QUERY).getAttributes(); - } - - /** - * Set the attributes of a named property. - * - * The property is specified by <code>name</code> - * as defined for <code>has</code>.<p> - * - * The possible attributes are READONLY, DONTENUM, - * and PERMANENT. Combinations of attributes - * are expressed by the bitwise OR of attributes. - * EMPTY is the state of no attributes set. Any unused - * bits are reserved for future use. - * - * @param name the name of the property - * @param attributes the bitset of attributes - * @exception EvaluatorException if the named property is not found - * @see org.mozilla.javascript.Scriptable#has(String, Scriptable) - * @see org.mozilla.javascript.ScriptableObject#READONLY - * @see org.mozilla.javascript.ScriptableObject#DONTENUM - * @see org.mozilla.javascript.ScriptableObject#PERMANENT - * @see org.mozilla.javascript.ScriptableObject#EMPTY - */ - public void setAttributes(String name, int attributes) - { - checkNotSealed(name, 0); - findAttributeSlot(name, 0, SLOT_MODIFY).setAttributes(attributes); - } - - /** - * Set the attributes of an indexed property. - * - * @param index the numeric index for the property - * @param attributes the bitset of attributes - * @exception EvaluatorException if the named property is not found - * @see org.mozilla.javascript.Scriptable#has(String, Scriptable) - * @see org.mozilla.javascript.ScriptableObject#READONLY - * @see org.mozilla.javascript.ScriptableObject#DONTENUM - * @see org.mozilla.javascript.ScriptableObject#PERMANENT - * @see org.mozilla.javascript.ScriptableObject#EMPTY - */ - public void setAttributes(int index, int attributes) - { - checkNotSealed(null, index); - findAttributeSlot(null, index, SLOT_MODIFY).setAttributes(attributes); - } - - /** - * XXX: write docs. - */ - public void setGetterOrSetter(String name, int index, - Callable getterOrSeter, boolean isSetter) - { - if (name != null && index != 0) - throw new IllegalArgumentException(name); - - checkNotSealed(name, index); - GetterSlot gslot = (GetterSlot)getSlot(name, index, - SLOT_MODIFY_GETTER_SETTER); - gslot.checkNotReadonly(); - if (isSetter) { - gslot.setter = getterOrSeter; - } else { - gslot.getter = getterOrSeter; - } - gslot.value = Undefined.instance; - } - - /** - * Get the getter or setter for a given property. Used by __lookupGetter__ - * and __lookupSetter__. - * - * @param name Name of the object. If nonnull, index must be 0. - * @param index Index of the object. If nonzero, name must be null. - * @param isSetter If true, return the setter, otherwise return the getter. - * @exception IllegalArgumentException if both name and index are nonnull - * and nonzero respectively. - * @return Null if the property does not exist. Otherwise returns either - * the getter or the setter for the property, depending on - * the value of isSetter (may be undefined if unset). - */ - public Object getGetterOrSetter(String name, int index, boolean isSetter) - { - if (name != null && index != 0) - throw new IllegalArgumentException(name); - Slot slot = getSlot(name, index, SLOT_QUERY); - if (slot == null) - return null; - if (slot instanceof GetterSlot) { - GetterSlot gslot = (GetterSlot)slot; - Object result = isSetter ? gslot.setter : gslot.getter; - return result != null ? result : Undefined.instance; - } else - return Undefined.instance; - } - - /** - * Returns whether a property is a getter or a setter - * @param name property name - * @param index property index - * @param setter true to check for a setter, false for a getter - * @return whether the property is a getter or a setter - */ - protected boolean isGetterOrSetter(String name, int index, boolean setter) { - Slot slot = getSlot(name, index, SLOT_QUERY); - if (slot instanceof GetterSlot) { - if (setter && ((GetterSlot)slot).setter != null) return true; - if (!setter && ((GetterSlot)slot).getter != null) return true; - } - return false; - } - - void addLazilyInitializedValue(String name, int index, - LazilyLoadedCtor init, int attributes) - { - if (name != null && index != 0) - throw new IllegalArgumentException(name); - checkNotSealed(name, index); - GetterSlot gslot = (GetterSlot)getSlot(name, index, - SLOT_MODIFY_GETTER_SETTER); - gslot.setAttributes(attributes); - gslot.getter = null; - gslot.setter = null; - gslot.value = init; - } - - /** - * Returns the prototype of the object. - */ - public Scriptable getPrototype() - { - return prototypeObject; - } - - /** - * Sets the prototype of the object. - */ - public void setPrototype(Scriptable m) - { - prototypeObject = m; - } - - /** - * Returns the parent (enclosing) scope of the object. - */ - public Scriptable getParentScope() - { - return parentScopeObject; - } - - /** - * Sets the parent (enclosing) scope of the object. - */ - public void setParentScope(Scriptable m) - { - parentScopeObject = m; - } - - /** - * Returns an array of ids for the properties of the object. - * - * <p>Any properties with the attribute DONTENUM are not listed. <p> - * - * @return an array of java.lang.Objects with an entry for every - * listed 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. - */ - public Object[] getIds() { - return getIds(false); - } - - /** - * Returns an array of ids for the properties of the object. - * - * <p>All properties, even those with attribute DONTENUM, are listed. <p> - * - * @return an array of java.lang.Objects with an entry for every - * listed 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. - */ - public Object[] getAllIds() { - return getIds(true); - } - - /** - * Implements the [[DefaultValue]] internal method. - * - * <p>Note that the toPrimitive conversion is a no-op for - * every type other than Object, for which [[DefaultValue]] - * is called. See ECMA 9.1.<p> - * - * A <code>hint</code> of null means "no hint". - * - * @param typeHint the type hint - * @return the default value for the object - * - * See ECMA 8.6.2.6. - */ - public Object getDefaultValue(Class typeHint) - { - return getDefaultValue(this, typeHint); - } - - public static Object getDefaultValue(Scriptable object, Class typeHint) - { - Context cx = null; - for (int i=0; i < 2; i++) { - boolean tryToString; - if (typeHint == ScriptRuntime.StringClass) { - tryToString = (i == 0); - } else { - tryToString = (i == 1); - } - - String methodName; - Object[] args; - if (tryToString) { - methodName = "toString"; - args = ScriptRuntime.emptyArgs; - } else { - methodName = "valueOf"; - args = new Object[1]; - String hint; - if (typeHint == null) { - hint = "undefined"; - } else if (typeHint == ScriptRuntime.StringClass) { - hint = "string"; - } else if (typeHint == ScriptRuntime.ScriptableClass) { - hint = "object"; - } else if (typeHint == ScriptRuntime.FunctionClass) { - hint = "function"; - } else if (typeHint == ScriptRuntime.BooleanClass - || typeHint == Boolean.TYPE) - { - hint = "boolean"; - } else if (typeHint == ScriptRuntime.NumberClass || - typeHint == ScriptRuntime.ByteClass || - typeHint == Byte.TYPE || - typeHint == ScriptRuntime.ShortClass || - typeHint == Short.TYPE || - typeHint == ScriptRuntime.IntegerClass || - typeHint == Integer.TYPE || - typeHint == ScriptRuntime.FloatClass || - typeHint == Float.TYPE || - typeHint == ScriptRuntime.DoubleClass || - typeHint == Double.TYPE) - { - hint = "number"; - } else { - throw Context.reportRuntimeError1( - "msg.invalid.type", typeHint.toString()); - } - args[0] = hint; - } - Object v = getProperty(object, methodName); - if (!(v instanceof Function)) - continue; - Function fun = (Function) v; - if (cx == null) - cx = Context.getContext(); - v = fun.call(cx, fun.getParentScope(), object, args); - if (v != null) { - if (!(v instanceof Scriptable)) { - return v; - } - if (typeHint == ScriptRuntime.ScriptableClass - || typeHint == ScriptRuntime.FunctionClass) - { - return v; - } - if (tryToString && v instanceof Wrapper) { - // Let a wrapped java.lang.String pass for a primitive - // string. - Object u = ((Wrapper)v).unwrap(); - if (u instanceof String) - return u; - } - } - } - // fall through to error - String arg = (typeHint == null) ? "undefined" : typeHint.getName(); - throw ScriptRuntime.typeError1("msg.default.value", arg); - } - - /** - * Implements the instanceof operator. - * - * <p>This operator has been proposed to ECMA. - * - * @param instance The value that appeared on the LHS of the instanceof - * operator - * @return true if "this" appears in value's prototype chain - * - */ - public boolean hasInstance(Scriptable instance) { - // Default for JS objects (other than Function) is to do prototype - // chasing. This will be overridden in NativeFunction and non-JS - // objects. - - return ScriptRuntime.jsDelegatesTo(instance, this); - } - - /** - * Emulate the SpiderMonkey (and Firefox) feature of allowing - * custom objects to avoid detection by normal "object detection" - * code patterns. This is used to implement document.all. - * See https://bugzilla.mozilla.org/show_bug.cgi?id=412247. - * This is an analog to JOF_DETECTING from SpiderMonkey; see - * https://bugzilla.mozilla.org/show_bug.cgi?id=248549. - * Other than this special case, embeddings should return false. - * @return true if this object should avoid object detection - * @since 1.7R1 - */ - public boolean avoidObjectDetection() { - return false; - } - - /** - * Custom <tt>==</tt> operator. - * Must return {@link Scriptable#NOT_FOUND} if this object does not - * have custom equality operator for the given value, - * <tt>Boolean.TRUE</tt> if this object is equivalent to <tt>value</tt>, - * <tt>Boolean.FALSE</tt> if this object is not equivalent to - * <tt>value</tt>. - * <p> - * The default implementation returns Boolean.TRUE - * if <tt>this == value</tt> or {@link Scriptable#NOT_FOUND} otherwise. - * It indicates that by default custom equality is available only if - * <tt>value</tt> is <tt>this</tt> in which case true is returned. - */ - protected Object equivalentValues(Object value) - { - return (this == value) ? Boolean.TRUE : Scriptable.NOT_FOUND; - } - - /** - * Defines JavaScript objects from a Java class that implements Scriptable. - * - * If the given class has a method - * <pre> - * static void init(Context cx, Scriptable scope, boolean sealed);</pre> - * - * or its compatibility form - * <pre> - * static void init(Scriptable scope);</pre> - * - * then it is invoked and no further initialization is done.<p> - * - * However, if no such a method is found, then the class's constructors and - * methods are used to initialize a class in the following manner.<p> - * - * First, the zero-parameter constructor of the class is called to - * create the prototype. If no such constructor exists, - * a {@link EvaluatorException} is thrown. <p> - * - * Next, all methods are scanned for special prefixes that indicate that they - * have special meaning for defining JavaScript objects. - * These special prefixes are - * <ul> - * <li><code>jsFunction_</code> for a JavaScript function - * <li><code>jsStaticFunction_</code> for a JavaScript function that - * is a property of the constructor - * <li><code>jsGet_</code> for a getter of a JavaScript property - * <li><code>jsSet_</code> for a setter of a JavaScript property - * <li><code>jsConstructor</code> for a JavaScript function that - * is the constructor - * </ul><p> - * - * If the method's name begins with "jsFunction_", a JavaScript function - * is created with a name formed from the rest of the Java method name - * following "jsFunction_". So a Java method named "jsFunction_foo" will - * define a JavaScript method "foo". Calling this JavaScript function - * will cause the Java method to be called. The parameters of the method - * must be of number and types as defined by the FunctionObject class. - * The JavaScript function is then added as a property - * of the prototype. <p> - * - * If the method's name begins with "jsStaticFunction_", it is handled - * similarly except that the resulting JavaScript function is added as a - * property of the constructor object. The Java method must be static. - * - * If the method's name begins with "jsGet_" or "jsSet_", the method is - * considered to define a property. Accesses to the defined property - * will result in calls to these getter and setter methods. If no - * setter is defined, the property is defined as READONLY.<p> - * - * If the method's name is "jsConstructor", the method is - * considered to define the body of the constructor. Only one - * method of this name may be defined. - * If no method is found that can serve as constructor, a Java - * constructor will be selected to serve as the JavaScript - * constructor in the following manner. If the class has only one - * Java constructor, that constructor is used to define - * the JavaScript constructor. If the the class has two constructors, - * one must be the zero-argument constructor (otherwise an - * {@link EvaluatorException} would have already been thrown - * when the prototype was to be created). In this case - * the Java constructor with one or more parameters will be used - * to define the JavaScript constructor. If the class has three - * or more constructors, an {@link EvaluatorException} - * will be thrown.<p> - * - * Finally, if there is a method - * <pre> - * static void finishInit(Scriptable scope, FunctionObject constructor, - * Scriptable prototype)</pre> - * - * it will be called to finish any initialization. The <code>scope</code> - * argument will be passed, along with the newly created constructor and - * the newly created prototype.<p> - * - * @param scope The scope in which to define the constructor. - * @param clazz The Java class to use to define the JavaScript objects - * and properties. - * @exception IllegalAccessException if access is not available - * to a reflected class member - * @exception InstantiationException if unable to instantiate - * the named class - * @exception InvocationTargetException if an exception is thrown - * during execution of methods of the named class - * @see org.mozilla.javascript.Function - * @see org.mozilla.javascript.FunctionObject - * @see org.mozilla.javascript.ScriptableObject#READONLY - * @see org.mozilla.javascript.ScriptableObject - * #defineProperty(String, Class, int) - */ - public static void defineClass(Scriptable scope, Class clazz) - throws IllegalAccessException, InstantiationException, - InvocationTargetException - { - defineClass(scope, clazz, false, false); - } - - /** - * Defines JavaScript objects from a Java class, optionally - * allowing sealing. - * - * Similar to <code>defineClass(Scriptable scope, Class clazz)</code> - * except that sealing is allowed. An object that is sealed cannot have - * properties added or removed. Note that sealing is not allowed in - * the current ECMA/ISO language specification, but is likely for - * the next version. - * - * @param scope The scope in which to define the constructor. - * @param clazz The Java class to use to define the JavaScript objects - * and properties. The class must implement Scriptable. - * @param sealed Whether or not to create sealed standard objects that - * cannot be modified. - * @exception IllegalAccessException if access is not available - * to a reflected class member - * @exception InstantiationException if unable to instantiate - * the named class - * @exception InvocationTargetException if an exception is thrown - * during execution of methods of the named class - * @since 1.4R3 - */ - public static void defineClass(Scriptable scope, Class clazz, - boolean sealed) - throws IllegalAccessException, InstantiationException, - InvocationTargetException - { - defineClass(scope, clazz, sealed, false); - } - - /** - * Defines JavaScript objects from a Java class, optionally - * allowing sealing and mapping of Java inheritance to JavaScript - * prototype-based inheritance. - * - * Similar to <code>defineClass(Scriptable scope, Class clazz)</code> - * except that sealing and inheritance mapping are allowed. An object - * that is sealed cannot have properties added or removed. Note that - * sealing is not allowed in the current ECMA/ISO language specification, - * but is likely for the next version. - * - * @param scope The scope in which to define the constructor. - * @param clazz The Java class to use to define the JavaScript objects - * and properties. The class must implement Scriptable. - * @param sealed Whether or not to create sealed standard objects that - * cannot be modified. - * @param mapInheritance Whether or not to map Java inheritance to - * JavaScript prototype-based inheritance. - * @return the class name for the prototype of the specified class - * @exception IllegalAccessException if access is not available - * to a reflected class member - * @exception InstantiationException if unable to instantiate - * the named class - * @exception InvocationTargetException if an exception is thrown - * during execution of methods of the named class - * @since 1.6R2 - */ - public static String defineClass(Scriptable scope, Class clazz, - boolean sealed, boolean mapInheritance) - throws IllegalAccessException, InstantiationException, - InvocationTargetException - { - BaseFunction ctor = buildClassCtor(scope, clazz, sealed, - mapInheritance); - if (ctor == null) - return null; - String name = ctor.getClassPrototype().getClassName(); - defineProperty(scope, name, ctor, ScriptableObject.DONTENUM); - return name; - } - - static BaseFunction buildClassCtor(Scriptable scope, Class clazz, - boolean sealed, - boolean mapInheritance) - throws IllegalAccessException, InstantiationException, - InvocationTargetException - { - Method[] methods = FunctionObject.getMethodList(clazz); - for (int i=0; i < methods.length; i++) { - Method method = methods[i]; - if (!method.getName().equals("init")) - continue; - Class[] parmTypes = method.getParameterTypes(); - if (parmTypes.length == 3 && - parmTypes[0] == ScriptRuntime.ContextClass && - parmTypes[1] == ScriptRuntime.ScriptableClass && - parmTypes[2] == Boolean.TYPE && - Modifier.isStatic(method.getModifiers())) - { - Object args[] = { Context.getContext(), scope, - sealed ? Boolean.TRUE : Boolean.FALSE }; - method.invoke(null, args); - return null; - } - if (parmTypes.length == 1 && - parmTypes[0] == ScriptRuntime.ScriptableClass && - Modifier.isStatic(method.getModifiers())) - { - Object args[] = { scope }; - method.invoke(null, args); - return null; - } - - } - - // If we got here, there isn't an "init" method with the right - // parameter types. - - Constructor[] ctors = clazz.getConstructors(); - Constructor protoCtor = null; - for (int i=0; i < ctors.length; i++) { - if (ctors[i].getParameterTypes().length == 0) { - protoCtor = ctors[i]; - break; - } - } - if (protoCtor == null) { - throw Context.reportRuntimeError1( - "msg.zero.arg.ctor", clazz.getName()); - } - - Scriptable proto = (Scriptable) protoCtor.newInstance(ScriptRuntime.emptyArgs); - String className = proto.getClassName(); - - // Set the prototype's prototype, trying to map Java inheritance to JS - // prototype-based inheritance if requested to do so. - Scriptable superProto = null; - if (mapInheritance) { - Class superClass = clazz.getSuperclass(); - if (ScriptRuntime.ScriptableClass.isAssignableFrom(superClass) - && !Modifier.isAbstract(superClass.getModifiers())) { - String name = ScriptableObject.defineClass(scope, superClass, sealed, mapInheritance); - if (name != null) { - superProto = ScriptableObject.getClassPrototype(scope, name); - } - } - } - if (superProto == null) { - superProto = ScriptableObject.getObjectPrototype(scope); - } - proto.setPrototype(superProto); - - // Find out whether there are any methods that begin with - // "js". If so, then only methods that begin with special - // prefixes will be defined as JavaScript entities. - final String functionPrefix = "jsFunction_"; - final String staticFunctionPrefix = "jsStaticFunction_"; - final String getterPrefix = "jsGet_"; - final String setterPrefix = "jsSet_"; - final String ctorName = "jsConstructor"; - - Member ctorMember = FunctionObject.findSingleMethod(methods, ctorName); - - if (ctorMember == null) { - if (ctors.length == 1) { - ctorMember = ctors[0]; - } else if (ctors.length == 2) { - if (ctors[0].getParameterTypes().length == 0) - ctorMember = ctors[1]; - else if (ctors[1].getParameterTypes().length == 0) - ctorMember = ctors[0]; - } - if (ctorMember == null) { - throw Context.reportRuntimeError1( - "msg.ctor.multiple.parms", clazz.getName()); - } - } - - FunctionObject ctor = new FunctionObject(className, ctorMember, scope); - if (ctor.isVarArgsMethod()) { - throw Context.reportRuntimeError1 - ("msg.varargs.ctor", ctorMember.getName()); - } - ctor.initAsConstructor(scope, proto); - - Method finishInit = null; - for (int i=0; i < methods.length; i++) { - if (methods[i] == ctorMember) { - continue; - } - String name = methods[i].getName(); - if (name.equals("finishInit")) { - Class[] parmTypes = methods[i].getParameterTypes(); - if (parmTypes.length == 3 && - parmTypes[0] == ScriptRuntime.ScriptableClass && - parmTypes[1] == FunctionObject.class && - parmTypes[2] == ScriptRuntime.ScriptableClass && - Modifier.isStatic(methods[i].getModifiers())) - { - finishInit = methods[i]; - continue; - } - } - // ignore any compiler generated methods. - if (name.indexOf('$') != -1) - continue; - if (name.equals(ctorName)) - continue; - - String prefix = null; - if (name.startsWith(functionPrefix)) { - prefix = functionPrefix; - } else if (name.startsWith(staticFunctionPrefix)) { - prefix = staticFunctionPrefix; - if (!Modifier.isStatic(methods[i].getModifiers())) { - throw Context.reportRuntimeError( - "jsStaticFunction must be used with static method."); - } - } else if (name.startsWith(getterPrefix)) { - prefix = getterPrefix; - } else if (name.startsWith(setterPrefix)) { - prefix = setterPrefix; - } else { - continue; - } - name = name.substring(prefix.length()); - if (prefix == setterPrefix) - continue; // deal with set when we see get - if (prefix == getterPrefix) { - if (!(proto instanceof ScriptableObject)) { - throw Context.reportRuntimeError2( - "msg.extend.scriptable", - proto.getClass().toString(), name); - } - Method setter = FunctionObject.findSingleMethod( - methods, - setterPrefix + name); - int attr = ScriptableObject.PERMANENT | - ScriptableObject.DONTENUM | - (setter != null ? 0 - : ScriptableObject.READONLY); - ((ScriptableObject) proto).defineProperty(name, null, - methods[i], setter, - attr); - continue; - } - - FunctionObject f = new FunctionObject(name, methods[i], proto); - if (f.isVarArgsConstructor()) { - throw Context.reportRuntimeError1 - ("msg.varargs.fun", ctorMember.getName()); - } - Scriptable dest = prefix == staticFunctionPrefix - ? ctor - : proto; - defineProperty(dest, name, f, DONTENUM); - if (sealed) { - f.sealObject(); - } - } - - // Call user code to complete initialization if necessary. - if (finishInit != null) { - Object[] finishArgs = { scope, ctor, proto }; - finishInit.invoke(null, finishArgs); - } - - // Seal the object if necessary. - if (sealed) { - ctor.sealObject(); - if (proto instanceof ScriptableObject) { - ((ScriptableObject) proto).sealObject(); - } - } - - return ctor; - } - - /** - * Define a JavaScript property. - * - * Creates the property with an initial value and sets its attributes. - * - * @param propertyName the name of the property to define. - * @param value the initial value of the property - * @param attributes the attributes of the JavaScript property - * @see org.mozilla.javascript.Scriptable#put(String, Scriptable, Object) - */ - public void defineProperty(String propertyName, Object value, - int attributes) - { - checkNotSealed(propertyName, 0); - put(propertyName, this, value); - setAttributes(propertyName, attributes); - } - - /** - * Utility method to add properties to arbitrary Scriptable object. - * If destination is instance of ScriptableObject, calls - * defineProperty there, otherwise calls put in destination - * ignoring attributes - */ - public static void defineProperty(Scriptable destination, - String propertyName, Object value, - int attributes) - { - if (!(destination instanceof ScriptableObject)) { - destination.put(propertyName, destination, value); - return; - } - ScriptableObject so = (ScriptableObject)destination; - so.defineProperty(propertyName, value, attributes); - } - - /** - * Utility method to add properties to arbitrary Scriptable object. - * If destination is instance of ScriptableObject, calls - * defineProperty there, otherwise calls put in destination - * ignoring attributes - */ - public static void defineConstProperty(Scriptable destination, - String propertyName) - { - if (destination instanceof ConstProperties) { - ConstProperties cp = (ConstProperties)destination; - cp.defineConst(propertyName, destination); - } else - defineProperty(destination, propertyName, Undefined.instance, CONST); - } - - /** - * Define a JavaScript property with getter and setter side effects. - * - * If the setter is not found, the attribute READONLY is added to - * the given attributes. <p> - * - * The getter must be a method with zero parameters, and the setter, if - * found, must be a method with one parameter.<p> - * - * @param propertyName the name of the property to define. This name - * also affects the name of the setter and getter - * to search for. If the propertyId is "foo", then - * <code>clazz</code> will be searched for "getFoo" - * and "setFoo" methods. - * @param clazz the Java class to search for the getter and setter - * @param attributes the attributes of the JavaScript property - * @see org.mozilla.javascript.Scriptable#put(String, Scriptable, Object) - */ - public void defineProperty(String propertyName, Class clazz, - int attributes) - { - int length = propertyName.length(); - if (length == 0) throw new IllegalArgumentException(); - char[] buf = new char[3 + length]; - propertyName.getChars(0, length, buf, 3); - buf[3] = Character.toUpperCase(buf[3]); - buf[0] = 'g'; - buf[1] = 'e'; - buf[2] = 't'; - String getterName = new String(buf); - buf[0] = 's'; - String setterName = new String(buf); - - Method[] methods = FunctionObject.getMethodList(clazz); - Method getter = FunctionObject.findSingleMethod(methods, getterName); - Method setter = FunctionObject.findSingleMethod(methods, setterName); - if (setter == null) - attributes |= ScriptableObject.READONLY; - defineProperty(propertyName, null, getter, - setter == null ? null : setter, attributes); - } - - /** - * Define a JavaScript property. - * - * Use this method only if you wish to define getters and setters for - * a given property in a ScriptableObject. To create a property without - * special getter or setter side effects, use - * <code>defineProperty(String,int)</code>. - * - * If <code>setter</code> is null, the attribute READONLY is added to - * the given attributes.<p> - * - * Several forms of getters or setters are allowed. In all cases the - * type of the value parameter can be any one of the following types: - * Object, String, boolean, Scriptable, byte, short, int, long, float, - * or double. The runtime will perform appropriate conversions based - * upon the type of the parameter (see description in FunctionObject). - * The first forms are nonstatic methods of the class referred to - * by 'this': - * <pre> - * Object getFoo(); - * void setFoo(SomeType value);</pre> - * Next are static methods that may be of any class; the object whose - * property is being accessed is passed in as an extra argument: - * <pre> - * static Object getFoo(Scriptable obj); - * static void setFoo(Scriptable obj, SomeType value);</pre> - * Finally, it is possible to delegate to another object entirely using - * the <code>delegateTo</code> parameter. In this case the methods are - * nonstatic methods of the class delegated to, and the object whose - * property is being accessed is passed in as an extra argument: - * <pre> - * Object getFoo(Scriptable obj); - * void setFoo(Scriptable obj, SomeType value);</pre> - * - * @param propertyName the name of the property to define. - * @param delegateTo an object to call the getter and setter methods on, - * or null, depending on the form used above. - * @param getter the method to invoke to get the value of the property - * @param setter the method to invoke to set the value of the property - * @param attributes the attributes of the JavaScript property - */ - public void defineProperty(String propertyName, Object delegateTo, - Method getter, Method setter, int attributes) - { - MemberBox getterBox = null; - if (getter != null) { - getterBox = new MemberBox(getter); - - boolean delegatedForm; - if (!Modifier.isStatic(getter.getModifiers())) { - delegatedForm = (delegateTo != null); - getterBox.delegateTo = delegateTo; - } else { - delegatedForm = true; - // Ignore delegateTo for static getter but store - // non-null delegateTo indicator. - getterBox.delegateTo = Void.TYPE; - } - - String errorId = null; - Class[] parmTypes = getter.getParameterTypes(); - if (parmTypes.length == 0) { - if (delegatedForm) { - errorId = "msg.obj.getter.parms"; - } - } else if (parmTypes.length == 1) { - Object argType = parmTypes[0]; - // Allow ScriptableObject for compatibility - if (!(argType == ScriptRuntime.ScriptableClass || - argType == ScriptRuntime.ScriptableObjectClass)) - { - errorId = "msg.bad.getter.parms"; - } else if (!delegatedForm) { - errorId = "msg.bad.getter.parms"; - } - } else { - errorId = "msg.bad.getter.parms"; - } - if (errorId != null) { - throw Context.reportRuntimeError1(errorId, getter.toString()); - } - } - - MemberBox setterBox = null; - if (setter != null) { - if (setter.getReturnType() != Void.TYPE) - throw Context.reportRuntimeError1("msg.setter.return", - setter.toString()); - - setterBox = new MemberBox(setter); - - boolean delegatedForm; - if (!Modifier.isStatic(setter.getModifiers())) { - delegatedForm = (delegateTo != null); - setterBox.delegateTo = delegateTo; - } else { - delegatedForm = true; - // Ignore delegateTo for static setter but store - // non-null delegateTo indicator. - setterBox.delegateTo = Void.TYPE; - } - - String errorId = null; - Class[] parmTypes = setter.getParameterTypes(); - if (parmTypes.length == 1) { - if (delegatedForm) { - errorId = "msg.setter2.expected"; - } - } else if (parmTypes.length == 2) { - Object argType = parmTypes[0]; - // Allow ScriptableObject for compatibility - if (!(argType == ScriptRuntime.ScriptableClass || - argType == ScriptRuntime.ScriptableObjectClass)) - { - errorId = "msg.setter2.parms"; - } else if (!delegatedForm) { - errorId = "msg.setter1.parms"; - } - } else { - errorId = "msg.setter.parms"; - } - if (errorId != null) { - throw Context.reportRuntimeError1(errorId, setter.toString()); - } - } - - GetterSlot gslot = (GetterSlot)getSlot(propertyName, 0, - SLOT_MODIFY_GETTER_SETTER); - gslot.setAttributes(attributes); - gslot.getter = getterBox; - gslot.setter = setterBox; - } - - /** - * Search for names in a class, adding the resulting methods - * as properties. - * - * <p> Uses reflection to find the methods of the given names. Then - * FunctionObjects are constructed from the methods found, and - * are added to this object as properties with the given names. - * - * @param names the names of the Methods to add as function properties - * @param clazz the class to search for the Methods - * @param attributes the attributes of the new properties - * @see org.mozilla.javascript.FunctionObject - */ - public void defineFunctionProperties(String[] names, Class clazz, - int attributes) - { - Method[] methods = FunctionObject.getMethodList(clazz); - for (int i=0; i < names.length; i++) { - String name = names[i]; - Method m = FunctionObject.findSingleMethod(methods, name); - if (m == null) { - throw Context.reportRuntimeError2( - "msg.method.not.found", name, clazz.getName()); - } - FunctionObject f = new FunctionObject(name, m, this); - defineProperty(name, f, attributes); - } - } - - /** - * Get the Object.prototype property. - * See ECMA 15.2.4. - */ - public static Scriptable getObjectPrototype(Scriptable scope) { - return getClassPrototype(scope, "Object"); - } - - /** - * Get the Function.prototype property. - * See ECMA 15.3.4. - */ - public static Scriptable getFunctionPrototype(Scriptable scope) { - return getClassPrototype(scope, "Function"); - } - - /** - * Get the prototype for the named class. - * - * For example, <code>getClassPrototype(s, "Date")</code> will first - * walk up the parent chain to find the outermost scope, then will - * search that scope for the Date constructor, and then will - * return Date.prototype. If any of the lookups fail, or - * the prototype is not a JavaScript object, then null will - * be returned. - * - * @param scope an object in the scope chain - * @param className the name of the constructor - * @return the prototype for the named class, or null if it - * cannot be found. - */ - public static Scriptable getClassPrototype(Scriptable scope, - String className) - { - scope = getTopLevelScope(scope); - Object ctor = getProperty(scope, className); - Object proto; - if (ctor instanceof BaseFunction) { - proto = ((BaseFunction)ctor).getPrototypeProperty(); - } else if (ctor instanceof Scriptable) { - Scriptable ctorObj = (Scriptable)ctor; - proto = ctorObj.get("prototype", ctorObj); - } else { - return null; - } - if (proto instanceof Scriptable) { - return (Scriptable)proto; - } - return null; - } - - /** - * Get the global scope. - * - * <p>Walks the parent scope chain to find an object with a null - * parent scope (the global object). - * - * @param obj a JavaScript object - * @return the corresponding global scope - */ - public static Scriptable getTopLevelScope(Scriptable obj) - { - for (;;) { - Scriptable parent = obj.getParentScope(); - if (parent == null) { - return obj; - } - obj = parent; - } - } - - // APPJET - public static Scriptable getVeryTopLevelScope(Scriptable obj) { - return ScriptRuntime.getLibraryScopeOrNull(obj); - } - - /** - * Seal this object. - * - * A sealed object may not have properties added or removed. Once - * an object is sealed it may not be unsealed. - * - * @since 1.4R3 - */ - public synchronized void sealObject() { - if (count >= 0) { - count = ~count; - } - } - - /** - * Return true if this object is sealed. - * - * It is an error to attempt to add or remove properties to - * a sealed object. - * - * @return true if sealed, false otherwise. - * @since 1.4R3 - */ - public final boolean isSealed() { - return count < 0; - } - - private void checkNotSealed(String name, int index) - { - if (!isSealed()) - return; - - String str = (name != null) ? name : Integer.toString(index); - throw Context.reportRuntimeError1("msg.modify.sealed", str); - } - - /** - * Gets a named property from an object or any object in its prototype chain. - * <p> - * Searches the prototype chain for a property named <code>name</code>. - * <p> - * @param obj a JavaScript object - * @param name a property name - * @return the value of a property with name <code>name</code> found in - * <code>obj</code> or any object in its prototype chain, or - * <code>Scriptable.NOT_FOUND</code> if not found - * @since 1.5R2 - */ - public static Object getProperty(Scriptable obj, String name) - { - Scriptable start = obj; - Object result; - do { - result = obj.get(name, start); - if (result != Scriptable.NOT_FOUND) - break; - obj = obj.getPrototype(); - } while (obj != null); - return result; - } - - /** - * Gets an indexed property from an object or any object in its prototype chain. - * <p> - * Searches the prototype chain for a property with integral index - * <code>index</code>. Note that if you wish to look for properties with numerical - * but non-integral indicies, you should use getProperty(Scriptable,String) with - * the string value of the index. - * <p> - * @param obj a JavaScript object - * @param index an integral index - * @return the value of a property with index <code>index</code> found in - * <code>obj</code> or any object in its prototype chain, or - * <code>Scriptable.NOT_FOUND</code> if not found - * @since 1.5R2 - */ - public static Object getProperty(Scriptable obj, int index) - { - Scriptable start = obj; - Object result; - do { - result = obj.get(index, start); - if (result != Scriptable.NOT_FOUND) - break; - obj = obj.getPrototype(); - } while (obj != null); - return result; - } - - /** - * Returns whether a named property is defined in an object or any object - * in its prototype chain. - * <p> - * Searches the prototype chain for a property named <code>name</code>. - * <p> - * @param obj a JavaScript object - * @param name a property name - * @return the true if property was found - * @since 1.5R2 - */ - public static boolean hasProperty(Scriptable obj, String name) - { - return null != getBase(obj, name); - } - - /** - * If hasProperty(obj, name) would return true, then if the property that - * was found is compatible with the new property, this method just returns. - * If the property is not compatible, then an exception is thrown. - * - * A property redefinition is incompatible if the first definition was a - * const declaration or if this one is. They are compatible only if neither - * was const. - */ - public static void redefineProperty(Scriptable obj, String name, - boolean isConst) - { - Scriptable base = getBase(obj, name); - if (base == null) - return; - if (base instanceof ConstProperties) { - ConstProperties cp = (ConstProperties)base; - - if (cp.isConst(name)) - throw Context.reportRuntimeError1("msg.const.redecl", name); - } - if (isConst) - throw Context.reportRuntimeError1("msg.var.redecl", name); - } - /** - * Returns whether an indexed property is defined in an object or any object - * in its prototype chain. - * <p> - * Searches the prototype chain for a property with index <code>index</code>. - * <p> - * @param obj a JavaScript object - * @param index a property index - * @return the true if property was found - * @since 1.5R2 - */ - public static boolean hasProperty(Scriptable obj, int index) - { - return null != getBase(obj, index); - } - - /** - * Puts a named property in an object or in an object in its prototype chain. - * <p> - * Searches for the named property in the prototype chain. If it is found, - * the value of the property in <code>obj</code> is changed through a call - * to {@link Scriptable#put(String, Scriptable, Object)} on the - * prototype passing <code>obj</code> as the <code>start</code> argument. - * This allows the prototype to veto the property setting in case the - * prototype defines the property with [[ReadOnly]] attribute. If the - * property is not found, it is added in <code>obj</code>. - * @param obj a JavaScript object - * @param name a property name - * @param value any JavaScript value accepted by Scriptable.put - * @since 1.5R2 - */ - public static void putProperty(Scriptable obj, String name, Object value) - { - Scriptable base = getBase(obj, name); - if (base == null) - base = obj; - base.put(name, obj, value); - } - - /** - * Puts a named property in an object or in an object in its prototype chain. - * <p> - * Searches for the named property in the prototype chain. If it is found, - * the value of the property in <code>obj</code> is changed through a call - * to {@link Scriptable#put(String, Scriptable, Object)} on the - * prototype passing <code>obj</code> as the <code>start</code> argument. - * This allows the prototype to veto the property setting in case the - * prototype defines the property with [[ReadOnly]] attribute. If the - * property is not found, it is added in <code>obj</code>. - * @param obj a JavaScript object - * @param name a property name - * @param value any JavaScript value accepted by Scriptable.put - * @since 1.5R2 - */ - public static void putConstProperty(Scriptable obj, String name, Object value) - { - Scriptable base = getBase(obj, name); - if (base == null) - base = obj; - if (base instanceof ConstProperties) - ((ConstProperties)base).putConst(name, obj, value); - } - - /** - * Puts an indexed property in an object or in an object in its prototype chain. - * <p> - * Searches for the indexed property in the prototype chain. If it is found, - * the value of the property in <code>obj</code> is changed through a call - * to {@link Scriptable#put(int, Scriptable, Object)} on the prototype - * passing <code>obj</code> as the <code>start</code> argument. This allows - * the prototype to veto the property setting in case the prototype defines - * the property with [[ReadOnly]] attribute. If the property is not found, - * it is added in <code>obj</code>. - * @param obj a JavaScript object - * @param index a property index - * @param value any JavaScript value accepted by Scriptable.put - * @since 1.5R2 - */ - public static void putProperty(Scriptable obj, int index, Object value) - { - Scriptable base = getBase(obj, index); - if (base == null) - base = obj; - base.put(index, obj, value); - } - - /** - * Removes the property from an object or its prototype chain. - * <p> - * Searches for a property with <code>name</code> in obj or - * its prototype chain. If it is found, the object's delete - * method is called. - * @param obj a JavaScript object - * @param name a property name - * @return true if the property doesn't exist or was successfully removed - * @since 1.5R2 - */ - public static boolean deleteProperty(Scriptable obj, String name) - { - Scriptable base = getBase(obj, name); - if (base == null) - return true; - base.delete(name); - return !base.has(name, obj); - } - - /** - * Removes the property from an object or its prototype chain. - * <p> - * Searches for a property with <code>index</code> in obj or - * its prototype chain. If it is found, the object's delete - * method is called. - * @param obj a JavaScript object - * @param index a property index - * @return true if the property doesn't exist or was successfully removed - * @since 1.5R2 - */ - public static boolean deleteProperty(Scriptable obj, int index) - { - Scriptable base = getBase(obj, index); - if (base == null) - return true; - base.delete(index); - return !base.has(index, obj); - } - - /** - * Returns an array of all ids from an object and its prototypes. - * <p> - * @param obj a JavaScript object - * @return an array of all ids from all object in the prototype chain. - * If a given id occurs multiple times in the prototype chain, - * it will occur only once in this list. - * @since 1.5R2 - */ - public static Object[] getPropertyIds(Scriptable obj) - { - if (obj == null) { - return ScriptRuntime.emptyArgs; - } - Object[] result = obj.getIds(); - ObjToIntMap map = null; - for (;;) { - obj = obj.getPrototype(); - if (obj == null) { - break; - } - Object[] ids = obj.getIds(); - if (ids.length == 0) { - continue; - } - if (map == null) { - if (result.length == 0) { - result = ids; - continue; - } - map = new ObjToIntMap(result.length + ids.length); - for (int i = 0; i != result.length; ++i) { - map.intern(result[i]); - } - result = null; // Allow to GC the result - } - for (int i = 0; i != ids.length; ++i) { - map.intern(ids[i]); - } - } - if (map != null) { - result = map.getKeys(); - } - return result; - } - - /** - * Call a method of an object. - * @param obj the JavaScript object - * @param methodName the name of the function property - * @param args the arguments for the call - * - * @see Context#getCurrentContext() - */ - public static Object callMethod(Scriptable obj, String methodName, - Object[] args) - { - return callMethod(null, obj, methodName, args); - } - - /** - * Call a method of an object. - * @param cx the Context object associated with the current thread. - * @param obj the JavaScript object - * @param methodName the name of the function property - * @param args the arguments for the call - */ - public static Object callMethod(Context cx, Scriptable obj, - String methodName, - Object[] args) - { - Object funObj = getProperty(obj, methodName); - if (!(funObj instanceof Function)) { - throw ScriptRuntime.notFunctionError(obj, methodName); - } - Function fun = (Function)funObj; - // XXX: What should be the scope when calling funObj? - // The following favor scope stored in the object on the assumption - // that is more useful especially under dynamic scope setup. - // An alternative is to check for dynamic scope flag - // and use ScriptableObject.getTopLevelScope(fun) if the flag is not - // set. But that require access to Context and messy code - // so for now it is not checked. - Scriptable scope = ScriptableObject.getTopLevelScope(obj); - if (cx != null) { - return fun.call(cx, scope, obj, args); - } else { - return Context.call(null, fun, scope, obj, args); - } - } - - private static Scriptable getBase(Scriptable obj, String name) - { - do { - if (obj.has(name, obj)) - break; - obj = obj.getPrototype(); - } while(obj != null); - return obj; - } - - private static Scriptable getBase(Scriptable obj, int index) - { - do { - if (obj.has(index, obj)) - break; - obj = obj.getPrototype(); - } while(obj != null); - return obj; - } - - /** - * Get arbitrary application-specific value associated with this object. - * @param key key object to select particular value. - * @see #associateValue(Object key, Object value) - */ - public final Object getAssociatedValue(Object key) - { - Hashtable h = associatedValues; - if (h == null) - return null; - return h.get(key); - } - - /** - * Get arbitrary application-specific value associated with the top scope - * of the given scope. - * The method first calls {@link #getTopLevelScope(Scriptable scope)} - * and then searches the prototype chain of the top scope for the first - * object containing the associated value with the given key. - * - * @param scope the starting scope. - * @param key key object to select particular value. - * @see #getAssociatedValue(Object key) - */ - public static Object getTopScopeValue(Scriptable scope, Object key) - { - scope = ScriptableObject.getTopLevelScope(scope); - for (;;) { - if (scope instanceof ScriptableObject) { - ScriptableObject so = (ScriptableObject)scope; - Object value = so.getAssociatedValue(key); - if (value != null) { - return value; - } - } - scope = scope.getPrototype(); - if (scope == null) { - return null; - } - } - } - - /** - * Associate arbitrary application-specific value with this object. - * Value can only be associated with the given object and key only once. - * The method ignores any subsequent attempts to change the already - * associated value. - * <p> The associated values are not serialized. - * @param key key object to select particular value. - * @param value the value to associate - * @return the passed value if the method is called first time for the - * given key or old value for any subsequent calls. - * @see #getAssociatedValue(Object key) - */ - public final Object associateValue(Object key, Object value) - { - if (value == null) throw new IllegalArgumentException(); - Hashtable h = associatedValues; - if (h == null) { - synchronized (this) { - h = associatedValues; - if (h == null) { - h = new Hashtable(); - associatedValues = h; - } - } - } - return Kit.initHash(h, key, value); - } - - private Object getImpl(String name, int index, Scriptable start) - { - Slot slot = getSlot(name, index, SLOT_QUERY); - if (slot == null) { - return Scriptable.NOT_FOUND; - } - if (!(slot instanceof GetterSlot)) { - return slot.value; - } - Object getterObj = ((GetterSlot)slot).getter; - if (getterObj != null) { - if (getterObj instanceof MemberBox) { - MemberBox nativeGetter = (MemberBox)getterObj; - Object getterThis; - Object[] args; - if (nativeGetter.delegateTo == null) { - getterThis = start; - args = ScriptRuntime.emptyArgs; - } else { - getterThis = nativeGetter.delegateTo; - args = new Object[] { start }; - } - return nativeGetter.invoke(getterThis, args); - } else { - Function f = (Function)getterObj; - Context cx = Context.getContext(); - return f.call(cx, f.getParentScope(), start, - ScriptRuntime.emptyArgs); - } - } - Object value = slot.value; - if (value instanceof LazilyLoadedCtor) { - LazilyLoadedCtor initializer = (LazilyLoadedCtor)value; - try { - initializer.init(); - } finally { - value = initializer.getValue(); - slot.value = value; - } - } - return value; - } - - /** - * - * @param name - * @param index - * @param start - * @param value - * @param constFlag EMPTY means normal put. UNINITIALIZED_CONST means - * defineConstProperty. READONLY means const initialization expression. - * @return false if this != start and no slot was found. true if this == start - * or this != start and a READONLY slot was found. - */ - private boolean putImpl(String name, int index, Scriptable start, - Object value, int constFlag) - { - Slot slot; - if (this != start) { - slot = getSlot(name, index, SLOT_QUERY); - if (slot == null) { - return false; - } - } else { - checkNotSealed(name, index); - // either const hoisted declaration or initialization - if (constFlag != EMPTY) { - slot = getSlot(name, index, SLOT_MODIFY_CONST); - int attr = slot.getAttributes(); - if ((attr & READONLY) == 0) - throw Context.reportRuntimeError1("msg.var.redecl", name); - if ((attr & UNINITIALIZED_CONST) != 0) { - slot.value = value; - // clear the bit on const initialization - if (constFlag != UNINITIALIZED_CONST) - slot.setAttributes(attr & ~UNINITIALIZED_CONST); - } - return true; - } - slot = getSlot(name, index, SLOT_MODIFY); - } - if ((slot.getAttributes() & READONLY) != 0) - return true; - if (slot instanceof GetterSlot) { - Object setterObj = ((GetterSlot)slot).setter; - if (setterObj != null) { - Context cx = Context.getContext(); - if (setterObj instanceof MemberBox) { - MemberBox nativeSetter = (MemberBox)setterObj; - Class pTypes[] = nativeSetter.argTypes; - // XXX: cache tag since it is already calculated in - // defineProperty ? - Class valueType = pTypes[pTypes.length - 1]; - int tag = FunctionObject.getTypeTag(valueType); - Object actualArg = FunctionObject.convertArg(cx, start, - value, tag); - Object setterThis; - Object[] args; - if (nativeSetter.delegateTo == null) { - setterThis = start; - args = new Object[] { actualArg }; - } else { - setterThis = nativeSetter.delegateTo; - args = new Object[] { start, actualArg }; - } - nativeSetter.invoke(setterThis, args); - } else { - Function f = (Function)setterObj; - f.call(cx, f.getParentScope(), start, - new Object[] { value }); - } - return true; - } - } - if (this == start) { - slot.value = value; - return true; - } else { - return false; - } - } - - private Slot findAttributeSlot(String name, int index, int accessType) - { - Slot slot = getSlot(name, index, accessType); - if (slot == null) { - String str = (name != null ? name : Integer.toString(index)); - throw Context.reportRuntimeError1("msg.prop.not.found", str); - } - return slot; - } - - /** - * Locate the slot with given name or index. - * - * @param name property name or null if slot holds spare array index. - * @param index index or 0 if slot holds property name. - */ - private Slot getSlot(String name, int index, int accessType) - { - Slot slot; - - // Query last access cache and check that it was not deleted. - lastAccessCheck: - { - slot = lastAccess; - if (name != null) { - if (name != slot.name) - break lastAccessCheck; - // No String.equals here as successful slot search update - // name object with fresh reference of the same string. - } else { - if (slot.name != null || index != slot.indexOrHash) - break lastAccessCheck; - } - - if (slot.wasDeleted != 0) - break lastAccessCheck; - - if (accessType == SLOT_MODIFY_GETTER_SETTER && - !(slot instanceof GetterSlot)) - break lastAccessCheck; - - return slot; - } - - slot = accessSlot(name, index, accessType); - if (slot != null) { - // Update the cache - lastAccess = slot; - } - return slot; - } - - private Slot accessSlot(String name, int index, int accessType) - { - int indexOrHash = (name != null ? name.hashCode() : index); - - if (accessType == SLOT_QUERY || - accessType == SLOT_MODIFY || - accessType == SLOT_MODIFY_CONST || - accessType == SLOT_MODIFY_GETTER_SETTER) - { - // Check the hashtable without using synchronization - - Slot[] slotsLocalRef = slots; // Get stable local reference - if (slotsLocalRef == null) { - if (accessType == SLOT_QUERY) - return null; - } else { - int tableSize = slotsLocalRef.length; - int slotIndex = getSlotIndex(tableSize, indexOrHash); - Slot slot = slotsLocalRef[slotIndex]; - while (slot != null) { - String sname = slot.name; - if (sname != null) { - if (sname == name) - break; - if (name != null && indexOrHash == slot.indexOrHash) { - if (name.equals(sname)) { - // This will avoid calling String.equals when - // slot is accessed with same string object - // next time. - slot.name = name; - break; - } - } - } else if (name == null && - indexOrHash == slot.indexOrHash) { - break; - } - slot = slot.next; - } - if (accessType == SLOT_QUERY) { - return slot; - } else if (accessType == SLOT_MODIFY) { - if (slot != null) - return slot; - } else if (accessType == SLOT_MODIFY_GETTER_SETTER) { - if (slot instanceof GetterSlot) - return slot; - } else if (accessType == SLOT_MODIFY_CONST) { - if (slot != null) - return slot; - } - } - - // A new slot has to be inserted or the old has to be replaced - // by GetterSlot. Time to synchronize. - - synchronized (this) { - // Refresh local ref if another thread triggered grow - slotsLocalRef = slots; - int insertPos; - if (count == 0) { - // Always throw away old slots if any on empty insert - slotsLocalRef = new Slot[5]; - slots = slotsLocalRef; - insertPos = getSlotIndex(slotsLocalRef.length, indexOrHash); - } else { - int tableSize = slotsLocalRef.length; - insertPos = getSlotIndex(tableSize, indexOrHash); - Slot prev = slotsLocalRef[insertPos]; - Slot slot = prev; - while (slot != null) { - if (slot.indexOrHash == indexOrHash && - (slot.name == name || - (name != null && name.equals(slot.name)))) - { - break; - } - prev = slot; - slot = slot.next; - } - - if (slot != null) { - // Another thread just added a slot with same - // name/index before this one entered synchronized - // block. This is a race in application code and - // probably indicates bug there. But for the hashtable - // implementation it is harmless with the only - // complication is the need to replace the added slot - // if we need GetterSlot and the old one is not. - if (accessType == SLOT_MODIFY_GETTER_SETTER && - !(slot instanceof GetterSlot)) - { - GetterSlot newSlot = new GetterSlot(name, indexOrHash, - slot.getAttributes()); - newSlot.value = slot.value; - newSlot.next = slot.next; - if (prev == slot) { - slotsLocalRef[insertPos] = newSlot; - } else { - prev.next = newSlot; - } - slot.wasDeleted = (byte)1; - if (slot == lastAccess) { - lastAccess = REMOVED; - } - slot = newSlot; - } else if (accessType == SLOT_MODIFY_CONST) { - return null; - } - return slot; - } - - // Check if the table is not too full before inserting. - if (4 * (count + 1) > 3 * slotsLocalRef.length) { - slotsLocalRef = new Slot[slotsLocalRef.length * 2 + 1]; - copyTable(slots, slotsLocalRef, count); - slots = slotsLocalRef; - insertPos = getSlotIndex(slotsLocalRef.length, - indexOrHash); - } - } - - Slot newSlot = (accessType == SLOT_MODIFY_GETTER_SETTER - ? new GetterSlot(name, indexOrHash, 0) - : new Slot(name, indexOrHash, 0)); - if (accessType == SLOT_MODIFY_CONST) - newSlot.setAttributes(CONST); - ++count; - addKnownAbsentSlot(slotsLocalRef, newSlot, insertPos); - return newSlot; - } - - } else if (accessType == SLOT_REMOVE) { - synchronized (this) { - Slot[] slotsLocalRef = slots; - if (count != 0) { - int tableSize = slots.length; - int slotIndex = getSlotIndex(tableSize, indexOrHash); - Slot prev = slotsLocalRef[slotIndex]; - Slot slot = prev; - while (slot != null) { - if (slot.indexOrHash == indexOrHash && - (slot.name == name || - (name != null && name.equals(slot.name)))) - { - break; - } - prev = slot; - slot = slot.next; - } - if (slot != null && (slot.getAttributes() & PERMANENT) == 0) { - count--; - if (prev == slot) { - slotsLocalRef[slotIndex] = slot.next; - } else { - prev.next = slot.next; - } - // Mark the slot as removed to handle a case when - // another thread manages to put just removed slot - // into lastAccess cache. - slot.wasDeleted = (byte)1; - if (slot == lastAccess) { - lastAccess = REMOVED; - } - } - } - } - return null; - - } else { - throw Kit.codeBug(); - } - } - - private static int getSlotIndex(int tableSize, int indexOrHash) - { - return (indexOrHash & 0x7fffffff) % tableSize; - } - - // Must be inside synchronized (this) - private static void copyTable(Slot[] slots, Slot[] newSlots, int count) - { - if (count == 0) throw Kit.codeBug(); - - int tableSize = newSlots.length; - int i = slots.length; - for (;;) { - --i; - Slot slot = slots[i]; - while (slot != null) { - int insertPos = getSlotIndex(tableSize, slot.indexOrHash); - Slot next = slot.next; - addKnownAbsentSlot(newSlots, slot, insertPos); - slot.next = null; - slot = next; - if (--count == 0) - return; - } - } - } - - /** - * Add slot with keys that are known to absent from the table. - * This is an optimization to use when inserting into empty table, - * after table growth or during deserialization. - */ - private static void addKnownAbsentSlot(Slot[] slots, Slot slot, int insertPos) - { - if (slots[insertPos] == null) { - slots[insertPos] = slot; - } else { - Slot prev = slots[insertPos]; - while (prev.next != null) { - prev = prev.next; - } - prev.next = slot; - } - } - - Object[] getIds(boolean getAll) { - Slot[] s = slots; - Object[] a = ScriptRuntime.emptyArgs; - if (s == null) - return a; - int c = 0; - for (int i=0; i < s.length; i++) { - Slot slot = s[i]; - while (slot != null) { - if (getAll || (slot.getAttributes() & DONTENUM) == 0) { - if (c == 0) - a = new Object[s.length]; - a[c++] = (slot.name != null ? (Object) slot.name - : new Integer(slot.indexOrHash)); - } - slot = slot.next; - } - } - if (c == a.length) - return a; - Object[] result = new Object[c]; - System.arraycopy(a, 0, result, 0, c); - return result; - } - - private synchronized void writeObject(ObjectOutputStream out) - throws IOException - { - out.defaultWriteObject(); - int objectsCount = count; - if (objectsCount < 0) { - // "this" was sealed - objectsCount = ~objectsCount; - } - if (objectsCount == 0) { - out.writeInt(0); - } else { - out.writeInt(slots.length); - for (int i = 0; i < slots.length; ++i) { - Slot slot = slots[i]; - while (slot != null) { - out.writeObject(slot); - slot = slot.next; - if (--objectsCount == 0) - return; - } - } - } - } - - private void readObject(ObjectInputStream in) - throws IOException, ClassNotFoundException - { - in.defaultReadObject(); - lastAccess = REMOVED; - - int tableSize = in.readInt(); - if (tableSize != 0) { - slots = new Slot[tableSize]; - int objectsCount = count; - if (objectsCount < 0) { - // "this" was sealed - objectsCount = ~objectsCount; - } - for (int i = 0; i != objectsCount; ++i) { - Slot slot = (Slot)in.readObject(); - int slotIndex = getSlotIndex(tableSize, slot.indexOrHash); - addKnownAbsentSlot(slots, slot, slotIndex); - } - } - } - -} |