2000-12-29 17:58:10 +00:00
|
|
|
|
// ESObject.java
|
|
|
|
|
// FESI Copyright (c) Jean-Marc Lugrin, 1999
|
|
|
|
|
//
|
|
|
|
|
// This program is free software; you can redistribute it and/or
|
|
|
|
|
// modify it under the terms of the GNU Lesser General Public
|
|
|
|
|
// License as published by the Free Software Foundation; either
|
|
|
|
|
// version 2 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
// Lesser General Public License for more details.
|
|
|
|
|
|
|
|
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
|
|
|
// License along with this library; if not, write to the Free Software
|
|
|
|
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
|
|
|
|
|
|
package FESI.Data;
|
|
|
|
|
|
|
|
|
|
import FESI.Exceptions.*;
|
|
|
|
|
import FESI.Interpreter.Evaluator;
|
|
|
|
|
import FESI.Interpreter.FesiHashtable;
|
|
|
|
|
import FESI.Interpreter.ScopeChain;
|
|
|
|
|
|
|
|
|
|
import java.util.Enumeration;
|
|
|
|
|
|
|
|
|
|
public abstract class ESObject extends ESValue {
|
|
|
|
|
|
|
|
|
|
/** Contains the properties of this object */
|
|
|
|
|
protected FesiHashtable properties;
|
|
|
|
|
|
|
|
|
|
/** The evaluator owning this object */
|
|
|
|
|
protected Evaluator evaluator;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The prototype of this object ([[prototype]] in the standard,
|
|
|
|
|
* not the "prototype" property of functions!)
|
|
|
|
|
*/
|
|
|
|
|
private ESObject prototype = null;
|
|
|
|
|
|
|
|
|
|
// Prepare common names and their hash value
|
|
|
|
|
static private final String TOSTRINGstring = ("toString").intern();
|
|
|
|
|
static private final int TOSTRINGhash = TOSTRINGstring.hashCode();
|
|
|
|
|
static private final String VALUEOFstring = ("valueOf").intern();
|
|
|
|
|
static private final int VALUEOFhash = VALUEOFstring.hashCode();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create an object with a specific prototype (which may be null)
|
|
|
|
|
* in the context of a specific evaluator (which may not be null)
|
|
|
|
|
* Uses the default hashTable size.
|
|
|
|
|
*
|
|
|
|
|
* @param prototype The prototype ESObject - may be null
|
|
|
|
|
* @param evaluator The evaluator - must not be null
|
|
|
|
|
*/
|
|
|
|
|
protected ESObject(ESObject prototype, Evaluator evaluator) {
|
|
|
|
|
this.prototype = prototype;
|
|
|
|
|
this.properties = new FesiHashtable();
|
|
|
|
|
this.evaluator = evaluator; // It will crash somewhere if null...
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create an object with a specific prototype (which may be null)
|
|
|
|
|
* in the context of a specific evaluator (which may not be null)
|
|
|
|
|
* Uses the specified hashTable size, which should be a prime.
|
|
|
|
|
* size is usefull for very small (arguments) or very large objects.
|
|
|
|
|
*
|
|
|
|
|
* @param prototype The prototype ESObject - may be null
|
|
|
|
|
* @param evaluator The evaluator - must not be null
|
|
|
|
|
*/
|
|
|
|
|
protected ESObject(ESObject prototype, Evaluator evaluator, int initialSize) {
|
|
|
|
|
this.prototype = prototype;
|
|
|
|
|
this.properties = new FesiHashtable(initialSize);
|
|
|
|
|
this.evaluator = evaluator; // It will crash somewhere if null...
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the evaluator for this object
|
|
|
|
|
*
|
|
|
|
|
* @return the evaluator
|
|
|
|
|
*/
|
|
|
|
|
public final Evaluator getEvaluator() {
|
|
|
|
|
return evaluator;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* All objects and thir subclasses are non primitive
|
|
|
|
|
*
|
|
|
|
|
* @return false
|
|
|
|
|
*/
|
|
|
|
|
public final boolean isPrimitive() {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Implements the [[prototype]] property (see 8.6.2)
|
|
|
|
|
*
|
|
|
|
|
* @return The prototype object or null
|
|
|
|
|
*/
|
|
|
|
|
public ESObject getPrototype() {
|
|
|
|
|
return prototype;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2001-04-06 17:45:00 +00:00
|
|
|
|
/**
|
|
|
|
|
* Allow the prototype to be set, added 2001-04-05 by Hannes Walln<EFBFBD>fer
|
|
|
|
|
*
|
|
|
|
|
* @param prototype The new prototype object
|
|
|
|
|
*/
|
|
|
|
|
public void setPrototype(ESObject prototype) {
|
|
|
|
|
this.prototype = prototype;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2000-12-29 17:58:10 +00:00
|
|
|
|
/**
|
|
|
|
|
* Return the name of the class of objects ([[class]]), as used in the default toString
|
|
|
|
|
* method of objects (15.2.4.2)
|
|
|
|
|
*
|
|
|
|
|
* @return the [[Class]] property of this object
|
|
|
|
|
*/
|
|
|
|
|
public String getESClassName() {
|
|
|
|
|
return "Object";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return a code indicating the type of the object for the implementation
|
|
|
|
|
* of the "==" operator.
|
|
|
|
|
*
|
|
|
|
|
* @return A type code
|
|
|
|
|
*/
|
|
|
|
|
public int getTypeOf() {
|
|
|
|
|
return EStypeObject;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Either return the property value of the specified property
|
|
|
|
|
* in the current object, or lookup the next object in the scope chain
|
|
|
|
|
* if there is one. If there is nones, generate an error message.
|
|
|
|
|
* <P>This routine must be overriden by subclass which change the
|
|
|
|
|
* implementation of getProperty.
|
|
|
|
|
*
|
|
|
|
|
* @param propertyName The property to look for
|
|
|
|
|
* @param previousScope The previous scope or null
|
|
|
|
|
* @param hash The hashCode of propertyName
|
|
|
|
|
* @return The value of the specified variable
|
|
|
|
|
* @exception EcmaScriptException if not found in any scope
|
|
|
|
|
*/
|
|
|
|
|
public ESValue getPropertyInScope(String propertyName,
|
|
|
|
|
ScopeChain previousScope,
|
|
|
|
|
int hash)
|
|
|
|
|
throws EcmaScriptException {
|
|
|
|
|
ESValue value = (ESValue) properties.get(propertyName, hash);
|
|
|
|
|
if (value == null) {
|
|
|
|
|
if (previousScope == null) {
|
|
|
|
|
// Return null for undefined global variables.
|
|
|
|
|
// throw new EcmaScriptException("global variable '" + propertyName + "' does not have a value");
|
|
|
|
|
value = ESNull.theNull;
|
|
|
|
|
} else {
|
|
|
|
|
value = previousScope.getValue(propertyName, hash);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the property by name (see 8.6.2.1) propagating to
|
|
|
|
|
* the prototype if required
|
|
|
|
|
*
|
|
|
|
|
* @param propertyName The string naming the property
|
|
|
|
|
* @param hash The hashCode of propertyName
|
|
|
|
|
* @return The property or <em>undefined</em>
|
|
|
|
|
* @exception EcmaScriptException Error in host objects ?
|
|
|
|
|
*/
|
|
|
|
|
public ESValue getProperty(String propertyName, int hash)
|
|
|
|
|
throws EcmaScriptException {
|
|
|
|
|
ESValue value = (ESValue) properties.get(propertyName, hash);
|
|
|
|
|
if (value == null) {
|
|
|
|
|
if (prototype == null) {
|
|
|
|
|
value = ESUndefined.theUndefined;
|
|
|
|
|
} else {
|
|
|
|
|
value = prototype.getProperty(propertyName, hash);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the property by index value. By default the index is
|
|
|
|
|
* converted to a string, but this can be optimized for arrays.
|
|
|
|
|
* <P>This is not the same as the indexed properties of the first
|
|
|
|
|
* version of JavaScript and does not allow to access named
|
|
|
|
|
* properties other than the property using the integer string
|
|
|
|
|
* representation as a name.
|
|
|
|
|
*
|
|
|
|
|
* @param index The property name as an integer.
|
|
|
|
|
* @return The property or <em>undefined</em>
|
|
|
|
|
* @exception EcmaScriptException Error in host objects ?
|
|
|
|
|
*/
|
|
|
|
|
public ESValue getProperty(int index)
|
|
|
|
|
throws EcmaScriptException {
|
|
|
|
|
String iString = Integer.toString(index);
|
|
|
|
|
return getProperty(iString, iString.hashCode());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean hasProperty(String propertyName, int hash)
|
|
|
|
|
throws EcmaScriptException {
|
|
|
|
|
boolean found = properties.containsKey(propertyName, hash);
|
|
|
|
|
if (!found && prototype != null) {
|
|
|
|
|
found = prototype.hasProperty(propertyName, hash);
|
|
|
|
|
}
|
|
|
|
|
return found;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean isHiddenProperty(String propertyName, int hash) {
|
|
|
|
|
return properties.isHidden(propertyName, hash);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Indicates that the getProperties return an enumerator to the
|
|
|
|
|
* index rather rather than to the value index (see ESWrapper).
|
|
|
|
|
*
|
|
|
|
|
* @return false
|
|
|
|
|
*/
|
|
|
|
|
public boolean isDirectEnumerator() {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns an enumerator for the key elements of this object,
|
|
|
|
|
* that is all is enumerable properties and the (non hidden)
|
|
|
|
|
* ones of its prototype, etc... As used for the for in
|
|
|
|
|
* statement.
|
|
|
|
|
*
|
|
|
|
|
* @return the enumerator
|
|
|
|
|
*/
|
|
|
|
|
public Enumeration getProperties() {
|
|
|
|
|
return new Enumeration() {
|
|
|
|
|
Enumeration props = properties.keys();
|
|
|
|
|
String currentKey = null;
|
|
|
|
|
int currentHash = 0;
|
|
|
|
|
boolean inside = false;
|
|
|
|
|
public boolean hasMoreElements() {
|
|
|
|
|
if (currentKey != null) return true;
|
|
|
|
|
while (props.hasMoreElements()) {
|
|
|
|
|
currentKey = (String) props.nextElement();
|
|
|
|
|
currentHash = currentKey.hashCode();
|
|
|
|
|
if (inside) {
|
|
|
|
|
if (properties.containsKey(currentKey, currentHash)) continue;
|
|
|
|
|
} else {
|
|
|
|
|
if (isHiddenProperty(currentKey, currentHash)) continue;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (!inside && prototype != null) {
|
|
|
|
|
inside = true;
|
|
|
|
|
props = prototype.getProperties();
|
|
|
|
|
while (props.hasMoreElements()) {
|
|
|
|
|
currentKey = (String) props.nextElement();
|
|
|
|
|
currentHash = currentKey.hashCode();
|
|
|
|
|
if (properties.containsKey(currentKey, currentHash)) continue;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
public Object nextElement() {
|
|
|
|
|
if (hasMoreElements()) {
|
|
|
|
|
String key = currentKey;
|
|
|
|
|
currentKey = null;
|
|
|
|
|
return key;
|
|
|
|
|
} else {
|
|
|
|
|
throw new java.util.NoSuchElementException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get all properties (including hidden ones), for the command
|
|
|
|
|
* @listall of the interpreter. Include the visible properties of the
|
|
|
|
|
* prototype (that is the one added by the user) but not the
|
|
|
|
|
* hidden ones of the prototype (otherwise this would list
|
|
|
|
|
* all functions for any object).
|
|
|
|
|
*
|
|
|
|
|
* @return An enumeration of all properties (visible and hidden).
|
|
|
|
|
*/
|
|
|
|
|
public Enumeration getAllProperties() {
|
|
|
|
|
return new Enumeration() {
|
|
|
|
|
String [] specialProperties = getSpecialPropertyNames();
|
|
|
|
|
int specialEnumerator = 0;
|
|
|
|
|
Enumeration props = properties.keys(); // all of object properties
|
|
|
|
|
String currentKey = null;
|
|
|
|
|
int currentHash = 0;
|
|
|
|
|
boolean inside = false; // true when examing prototypes properties
|
|
|
|
|
public boolean hasMoreElements() {
|
|
|
|
|
// OK if we already checked for a property and one exists
|
|
|
|
|
if (currentKey != null) return true;
|
|
|
|
|
// Loop on special properties first
|
|
|
|
|
if (specialEnumerator < specialProperties.length) {
|
|
|
|
|
currentKey = specialProperties[specialEnumerator];
|
|
|
|
|
currentHash = currentKey.hashCode();
|
|
|
|
|
specialEnumerator++;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
// loop on standard or prototype properties
|
|
|
|
|
while (props.hasMoreElements()) {
|
|
|
|
|
currentKey = (String) props.nextElement();
|
|
|
|
|
currentHash = currentKey.hashCode();
|
|
|
|
|
if (inside) {
|
|
|
|
|
if (properties.containsKey(currentKey, currentHash)) continue;
|
|
|
|
|
// SHOULD CHECK IF NOT IN SPECIAL
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
// If prototype properties have not yet been examined, look for them
|
|
|
|
|
if (!inside && prototype != null) {
|
|
|
|
|
inside = true;
|
|
|
|
|
props = prototype.getProperties();
|
|
|
|
|
while (props.hasMoreElements()) {
|
|
|
|
|
currentKey = (String) props.nextElement();
|
|
|
|
|
currentHash = currentKey.hashCode();
|
|
|
|
|
if (properties.containsKey(currentKey, currentHash)) continue;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
public Object nextElement() {
|
|
|
|
|
if (hasMoreElements()) {
|
|
|
|
|
String key = currentKey;
|
|
|
|
|
currentKey = null;
|
|
|
|
|
return key;
|
|
|
|
|
} else {
|
|
|
|
|
throw new java.util.NoSuchElementException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Put the property by name (see 8.6.2.2), ignoring if
|
|
|
|
|
* read only (integrate functionality of canPut) and
|
|
|
|
|
* creating it if needed and possible.
|
|
|
|
|
* <P>The routine implement the functionality of the canPut attribute.
|
|
|
|
|
*
|
|
|
|
|
* @param propertyName The string naming the property
|
|
|
|
|
* @param propertyValue The value to put
|
|
|
|
|
* @exception EcmaScriptException Error in host objects ?
|
|
|
|
|
*/
|
|
|
|
|
public void putProperty(String propertyName,
|
|
|
|
|
ESValue propertyValue,
|
|
|
|
|
int hash)
|
|
|
|
|
throws EcmaScriptException {
|
|
|
|
|
properties.put(propertyName, hash, false, false, propertyValue);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Put the property by index value. By default the index is
|
|
|
|
|
* converted to a string, but this can be optimized for arrays.
|
|
|
|
|
* <P>This is not the same as the indexed properties of the first
|
|
|
|
|
* version of JavaScript and does not allow to access named
|
|
|
|
|
* properties other than the property using the integer string
|
|
|
|
|
* representation as a name.
|
|
|
|
|
*
|
|
|
|
|
* @param index The property name as an integer.
|
|
|
|
|
* @param propertyValue The value to put
|
|
|
|
|
* @exception EcmaScriptException Error in host objects ?
|
|
|
|
|
*/
|
|
|
|
|
public void putProperty(int index, ESValue propertyValue)
|
|
|
|
|
throws EcmaScriptException {
|
|
|
|
|
String iString = Integer.toString(index);
|
|
|
|
|
putProperty(iString, propertyValue, iString.hashCode());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Put the property as hidden. This is mostly used by initialization
|
|
|
|
|
* code, so a hash value is computed localy and the string is interned.
|
|
|
|
|
*
|
|
|
|
|
* @param propertyName The name of the property
|
|
|
|
|
* @param propertyValue Its value
|
|
|
|
|
* @exception EcmaScriptException Not used
|
|
|
|
|
*/
|
|
|
|
|
public void putHiddenProperty(String propertyName,
|
|
|
|
|
ESValue propertyValue)
|
|
|
|
|
throws EcmaScriptException {
|
|
|
|
|
propertyName = propertyName.intern();
|
|
|
|
|
int hash = propertyName.hashCode();
|
|
|
|
|
properties.put(propertyName, hash, true, false, propertyValue);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Implements the [[delete]] function (8.6.2.5), only
|
|
|
|
|
* called by the DELETE operator. Should return true if
|
|
|
|
|
* the propery does not exist any more (or did not exist
|
|
|
|
|
* at all) after the return.
|
|
|
|
|
* <P>This routine must implement the DontDelete attribue too.
|
|
|
|
|
*
|
|
|
|
|
* @param propertyName The name of the property
|
|
|
|
|
* @return true if the property is not present anymore
|
|
|
|
|
* @exception EcmaScriptException Not used
|
|
|
|
|
*/
|
|
|
|
|
public boolean deleteProperty(String propertyName, int hash)
|
|
|
|
|
throws EcmaScriptException {
|
|
|
|
|
properties.remove(propertyName, hash);
|
|
|
|
|
return true; // either it did not exist or was deleted !
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Implements [[DefaultValue]] with hint
|
|
|
|
|
*
|
|
|
|
|
* @param hint A type hint (only string or number)
|
|
|
|
|
|
|
|
|
|
* @exception EcmaScriptException Propagated or bad hint
|
|
|
|
|
* @return the default value of this object
|
|
|
|
|
*/
|
|
|
|
|
public ESValue getDefaultValue(int hint)
|
|
|
|
|
throws EcmaScriptException {
|
|
|
|
|
ESValue theResult = null;
|
|
|
|
|
ESValue theFunction = null;
|
|
|
|
|
|
|
|
|
|
if (hint == ESValue.EStypeString) {
|
|
|
|
|
theFunction = this.getProperty(TOSTRINGstring,TOSTRINGhash);
|
|
|
|
|
if (theFunction instanceof ESObject) {
|
|
|
|
|
theResult = theFunction.callFunction(this, new ESValue[0]);
|
|
|
|
|
if (theResult.isPrimitive()) {
|
|
|
|
|
return theResult;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
theFunction = this.getProperty(VALUEOFstring,VALUEOFhash);
|
|
|
|
|
if (theFunction instanceof ESObject) {
|
|
|
|
|
theResult = theFunction.callFunction(this, new ESValue[0]);
|
|
|
|
|
if (theResult.isPrimitive()) {
|
|
|
|
|
return theResult;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Throw errror on super to avoid evaluating this with as a string,
|
|
|
|
|
// as this is exactly what we cannot do.
|
|
|
|
|
throw new EcmaScriptException ("No default value for " + super.toString() + " and hint " + hint);
|
|
|
|
|
} else if (hint == ESValue.EStypeNumber) {
|
|
|
|
|
theFunction = this.getProperty(VALUEOFstring,VALUEOFhash);
|
|
|
|
|
if (theFunction instanceof ESObject) {
|
|
|
|
|
theResult = theFunction.callFunction(this, new ESValue[0]);
|
|
|
|
|
if (theResult.isPrimitive()) {
|
|
|
|
|
return theResult;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
theFunction = this.getProperty(TOSTRINGstring,TOSTRINGhash);
|
|
|
|
|
if (theFunction instanceof ESObject) {
|
|
|
|
|
theResult = theFunction.callFunction(this, new ESValue[0]);
|
|
|
|
|
if (theResult.isPrimitive()) {
|
|
|
|
|
return theResult;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
throw new EcmaScriptException ("No default value for " + this + " and hint " + hint);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Implements [[DefaultValue]] with no hint
|
|
|
|
|
* <P> The default is different for dates
|
|
|
|
|
*
|
|
|
|
|
* @exception EcmaScriptException Propagated
|
|
|
|
|
* @return the default value of this object
|
|
|
|
|
*/
|
|
|
|
|
public ESValue getDefaultValue()
|
|
|
|
|
throws EcmaScriptException {
|
|
|
|
|
|
|
|
|
|
return this.getDefaultValue(EStypeNumber);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Call a function object - not implemented for default objecr
|
|
|
|
|
*
|
|
|
|
|
* @param thisObject The current object
|
|
|
|
|
* @param arguments The arguments to the function
|
|
|
|
|
* @return The calculated value
|
|
|
|
|
* @exception EcmaScriptException thrown because the function is not implemented
|
|
|
|
|
*/
|
|
|
|
|
public ESValue callFunction(ESObject thisObject,
|
|
|
|
|
ESValue[] arguments)
|
|
|
|
|
throws EcmaScriptException {
|
|
|
|
|
throw new EcmaScriptException("No function defined on: " + this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A construct as thisObject.functionName() was detected,
|
|
|
|
|
* The functionName is looked up, then a call is made.
|
|
|
|
|
* This avoid creating a dummy function object when one does not
|
|
|
|
|
* exists, like for the ESWrapper objects (where functions are
|
|
|
|
|
* really java methods).
|
|
|
|
|
* <P>Only method which do not use the standard EcmaScript
|
|
|
|
|
* function evaluation mechanism need to override this method.
|
|
|
|
|
*
|
|
|
|
|
* @param evaluator The evaluator
|
|
|
|
|
* @param target The original target (for recursive calls)
|
|
|
|
|
* @param functionName The name of the function property
|
|
|
|
|
* @param arguments The arguments of the function
|
|
|
|
|
* @return The result of calling the function
|
|
|
|
|
* @exception EcmaScriptException Function not defined
|
|
|
|
|
* @exception NoSuchMethodException Method not found
|
|
|
|
|
*/
|
|
|
|
|
public ESValue doIndirectCall(Evaluator evaluator,
|
|
|
|
|
ESObject target,
|
|
|
|
|
String functionName,
|
|
|
|
|
ESValue[] arguments)
|
|
|
|
|
throws EcmaScriptException, NoSuchMethodException {
|
|
|
|
|
ESValue theFunction = (ESValue) properties.get(functionName, functionName.hashCode());
|
|
|
|
|
if (theFunction == null) {
|
|
|
|
|
if (prototype == null) {
|
|
|
|
|
throw new EcmaScriptException("The function '"+functionName+
|
|
|
|
|
"' is not defined for object '"+target.toString()+"'");
|
|
|
|
|
} else {
|
|
|
|
|
return prototype.doIndirectCall(evaluator, target, functionName, arguments);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return theFunction.callFunction(target,arguments);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A routine which may return a function as the value of a builtin
|
|
|
|
|
// property must override this function
|
|
|
|
|
public ESValue doIndirectCallInScope(Evaluator evaluator,
|
|
|
|
|
ScopeChain previousScope,
|
|
|
|
|
ESObject thisObject,
|
|
|
|
|
String functionName,
|
|
|
|
|
int hash,
|
|
|
|
|
ESValue[] arguments)
|
|
|
|
|
throws EcmaScriptException {
|
|
|
|
|
ESValue theFunction = (ESValue) properties.get(functionName, hash);
|
|
|
|
|
if (theFunction == null) {
|
|
|
|
|
if (previousScope == null) {
|
|
|
|
|
throw new EcmaScriptException("no global function named '" + functionName + "'");
|
|
|
|
|
} else {
|
|
|
|
|
return previousScope.doIndirectCall(evaluator, thisObject, functionName, hash, arguments);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return theFunction.callFunction(thisObject,arguments);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Call the constructor - not implemented on default object
|
|
|
|
|
*
|
|
|
|
|
* @param thisObject The current object
|
|
|
|
|
* @param arguments Arguments to new
|
|
|
|
|
* @return The created obbjecr
|
|
|
|
|
* @exception EcmaScriptException thrown because this function is not implemented
|
|
|
|
|
*/
|
|
|
|
|
public ESObject doConstruct(ESObject thisObject,
|
|
|
|
|
ESValue[] arguments)
|
|
|
|
|
throws EcmaScriptException {
|
|
|
|
|
throw new EcmaScriptException("No constructor defined on: " + this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return a double value for this object if possible
|
|
|
|
|
*
|
|
|
|
|
* @return The double value
|
|
|
|
|
* @exception EcmaScriptException If not a suitable primitive
|
|
|
|
|
*/
|
|
|
|
|
public double doubleValue() throws EcmaScriptException {
|
|
|
|
|
ESValue value = ESUndefined.theUndefined;
|
|
|
|
|
double d = Double.NaN;
|
|
|
|
|
try {
|
|
|
|
|
value = toESPrimitive(EStypeNumber);
|
|
|
|
|
d = value.doubleValue();
|
|
|
|
|
} catch (EcmaScriptException e) {
|
|
|
|
|
throw new ProgrammingError(e.getMessage());
|
|
|
|
|
}
|
|
|
|
|
return d;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return the boolean value of this object if possible
|
|
|
|
|
*
|
|
|
|
|
* @return the boolean value
|
|
|
|
|
* @exception EcmaScriptException If not a suitable primitive
|
|
|
|
|
*/
|
|
|
|
|
public boolean booleanValue() throws EcmaScriptException {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public String toString() {
|
|
|
|
|
ESValue value = ESUndefined.theUndefined;
|
|
|
|
|
String string = null;
|
|
|
|
|
try {
|
|
|
|
|
value = toESPrimitive(EStypeString);
|
|
|
|
|
} catch (EcmaScriptException e) {
|
|
|
|
|
return this.toDetailString();
|
|
|
|
|
}
|
|
|
|
|
string = value.toString();
|
|
|
|
|
return string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Convert to an object
|
|
|
|
|
*
|
|
|
|
|
* @param evaluator The evaluator
|
|
|
|
|
* @return This
|
|
|
|
|
* @exception EcmaScriptException not thrown
|
|
|
|
|
*/
|
|
|
|
|
public final ESValue toESObject(Evaluator evaluator) throws EcmaScriptException {
|
|
|
|
|
return this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Convert to a primitive
|
|
|
|
|
*
|
|
|
|
|
* @param preferedType For string or number
|
|
|
|
|
* @return The primitive value
|
|
|
|
|
* @exception EcmaScriptException If no suitable default value
|
|
|
|
|
*/
|
|
|
|
|
public final ESValue toESPrimitive(int preferedType) throws EcmaScriptException {
|
|
|
|
|
return getDefaultValue(preferedType);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Convert to a primitive
|
|
|
|
|
*
|
|
|
|
|
* @return The primitive value
|
|
|
|
|
* @exception EcmaScriptException If no suitable default value
|
|
|
|
|
*/
|
|
|
|
|
public final ESValue toESPrimitive() throws EcmaScriptException {
|
|
|
|
|
return getDefaultValue();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return a Java object which is the object to pass to Java routines
|
|
|
|
|
* called by FESI. By default wrap the ESObject in a wrapper object,
|
|
|
|
|
* used by the jslib. Overriden by subclass if a better type can be found.
|
|
|
|
|
*
|
|
|
|
|
* @return a wrapper object over this ESObject.
|
|
|
|
|
*/
|
|
|
|
|
public Object toJavaObject() {
|
|
|
|
|
return new JSWrapper(this, evaluator);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return the name of the type of the object for the typeof operator
|
|
|
|
|
*
|
|
|
|
|
* @return The name of the type as a String
|
|
|
|
|
*/
|
|
|
|
|
public String getTypeofString() {
|
|
|
|
|
return "object";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public String toDetailString() {
|
|
|
|
|
return "ES:[" + getESClassName() + "]";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return true to indicate that this value is composite.
|
|
|
|
|
*
|
|
|
|
|
* @return true
|
|
|
|
|
*/
|
|
|
|
|
public boolean isComposite() {return true; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return the list of proprties which are not listed by getAll,
|
|
|
|
|
* that is all special properties handled directly by getProperty,
|
|
|
|
|
* which are not in the property hash table (they are considered
|
|
|
|
|
* hidden)
|
|
|
|
|
* Must be overriden by a subclass which overrides getProperty!
|
|
|
|
|
*
|
|
|
|
|
* return The array of special property names
|
|
|
|
|
*/
|
|
|
|
|
public String[] getSpecialPropertyNames() {
|
|
|
|
|
return new String[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get an enumeration of the description of various aspects
|
|
|
|
|
* of the object, including all properties.
|
|
|
|
|
*/
|
|
|
|
|
public Enumeration getAllDescriptions() {
|
|
|
|
|
return new Enumeration() {
|
|
|
|
|
String [] specialProperties = getSpecialPropertyNames();
|
|
|
|
|
int specialEnumerator = 0;
|
|
|
|
|
Enumeration props = properties.keys();
|
|
|
|
|
String currentKey = null;
|
|
|
|
|
int currentHash = 0;
|
|
|
|
|
boolean inside = false;
|
|
|
|
|
boolean inSpecial = true;
|
|
|
|
|
public boolean hasMoreElements() {
|
|
|
|
|
// If we have one already, send it
|
|
|
|
|
if (currentKey != null) return true;
|
|
|
|
|
// Loop on special properties first
|
|
|
|
|
if (specialEnumerator < specialProperties.length) {
|
|
|
|
|
currentKey = specialProperties[specialEnumerator];
|
|
|
|
|
currentHash = currentKey.hashCode();
|
|
|
|
|
specialEnumerator++;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
inSpecial = false;
|
|
|
|
|
// Otherwise check in current enumeration
|
|
|
|
|
while (props.hasMoreElements()) {
|
|
|
|
|
currentKey = (String) props.nextElement();
|
|
|
|
|
currentHash = currentKey.hashCode();
|
|
|
|
|
//if (inside) {
|
|
|
|
|
// if (properties.containsKey(currentKey, currentHash)) continue;
|
|
|
|
|
//}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
// Got to prototype enumeration if needed
|
|
|
|
|
if (!inside && prototype != null) {
|
|
|
|
|
inside = true;
|
|
|
|
|
props = prototype.getProperties();
|
|
|
|
|
while (props.hasMoreElements()) {
|
|
|
|
|
currentKey = (String) props.nextElement();
|
|
|
|
|
currentHash = currentKey.hashCode();
|
|
|
|
|
//if (properties.containsKey(currentKey, currentHash)) continue;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
public Object nextElement() {
|
|
|
|
|
if (hasMoreElements()) {
|
|
|
|
|
String key = currentKey;
|
|
|
|
|
int hash = key.hashCode();
|
|
|
|
|
currentKey = null;
|
|
|
|
|
ESValue value = null;
|
|
|
|
|
try {
|
|
|
|
|
value = ESObject.this.getProperty(key, hash);
|
|
|
|
|
} catch (EcmaScriptException e) {
|
|
|
|
|
throw new ProgrammingError("Unexpected exception " + e);
|
|
|
|
|
}
|
|
|
|
|
String propertyKind;
|
|
|
|
|
if (inSpecial) {
|
|
|
|
|
propertyKind = "HIDDEN";
|
|
|
|
|
} else if (inside && properties.containsKey(key, hash)) {
|
|
|
|
|
propertyKind = "INVISIBLE";
|
|
|
|
|
} else {
|
|
|
|
|
propertyKind = isHiddenProperty(key, hash) ? "HIDDEN" : "VISIBLE";
|
|
|
|
|
}
|
|
|
|
|
propertyKind += (inside ? " PROTOTYPE" : " OBJECT");
|
|
|
|
|
propertyKind += " PROPERTY";
|
|
|
|
|
return new ValueDescription(key,
|
|
|
|
|
propertyKind,
|
|
|
|
|
value.toString());
|
|
|
|
|
} else {
|
|
|
|
|
throw new java.util.NoSuchElementException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns a full description of the value, with the specified name.
|
|
|
|
|
*
|
|
|
|
|
* @param name The name of the value to describe
|
|
|
|
|
*
|
|
|
|
|
* @return the description of this value
|
|
|
|
|
*/
|
|
|
|
|
public ValueDescription getDescription(String name) {
|
|
|
|
|
return new ValueDescription(name,
|
|
|
|
|
"OBJECT",
|
|
|
|
|
this.toString());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|