Updates from Igor.

This commit is contained in:
nboyd%atg.com 2001-04-11 23:29:23 +00:00
parent e073d5090b
commit c83b74d6be
6 changed files with 536 additions and 604 deletions

View File

@ -39,10 +39,10 @@ package org.mozilla.javascript;
Base class for native object implementation that uses IdFunction to export its methods to script via <class-name>.prototype object.
Any descendant should implement at least the following methods:
getMaxPrototypeMethodId
mapNameToMethodId
execMethod
methodArity
getMaxPrototypeMethodId
mapNameToMethodId
execMethod
methodArity
Any implementation of execMethod and methodArity should assume that if
methodId is 0 (CONSTRUCTOR_ID), it denotes EcmaScript constructor for
@ -51,18 +51,18 @@ methodId is 0 (CONSTRUCTOR_ID), it denotes EcmaScript constructor for
To customize initializition of constructor and protype objects, descendant
may override initForGlobal or fillConstructorProperties methods.
To be less verbose with wrapping/unwrapping of script objects, descaendant
should use to_/wrap_ set of methods.
To wrap primitive java types to script objects, descaendant
should use wrap_<primitive-type> set of methods.
*/
public abstract class IdScriptable extends ScriptableObject
implements IdFunction.Master, ScopeInitializer
{
public static final int CONSTRUCTOR_ID = 0;
public static final int CONSTRUCTOR_ID = 0;
/** 'thisObj' will be null if invoked as constructor, in which case
** instance of Scriptable should be returned.
*/
*/
public abstract Object execMethod(int methodId, IdFunction function,
Context cx, Scriptable scope,
Scriptable thisObj, Object[] args)
@ -70,77 +70,77 @@ public abstract class IdScriptable extends ScriptableObject
public abstract int methodArity(int methodId, IdFunction function);
/** Return maximum method id for prototype function */
protected abstract int getMaxPrototypeMethodId();
/** Map name into id for prototypr method.
** Should return 0 if not name is not prototype method
** or value between 1 and getMaxPrototypeMethodId()
*/
protected abstract int mapNameToMethodId(String name);
/** Return maximum method id for prototype function */
protected abstract int getMaxPrototypeMethodId();
/** Map name into id for prototypr method.
** Should return 0 if not name is not prototype method
** or value between 1 and getMaxPrototypeMethodId()
*/
protected abstract int mapNameToMethodId(String name);
public boolean has(String name, Scriptable start) {
if (prototypeFunctionPool != null) {
int id = mapNameToMethodId(name);
if (id != 0) {
IdFunction f = prototypeFunctionPool[id];
if (f != IdFunction.WAS_OVERWRITTEN) { return true; }
}
else {
if (null != checkConstructor(name)) { return true; }
}
}
int id = mapNameToMethodId(name);
if (id != 0) {
IdFunction f = prototypeFunctionPool[id];
if (f != IdFunction.WAS_OVERWRITTEN) { return true; }
}
else {
if (null != checkConstructor(name)) { return true; }
}
}
return super.has(name, start);
}
public Object get(String name, Scriptable start) {
L: if (prototypeFunctionPool != null) {
IdFunction f = lastFunction;
if (f.methodName == name) {
if (prototypeFunctionPool[f.methodId] == f) {
return f;
}
lastFunction = IdFunction.WAS_OVERWRITTEN;
}
int id = mapNameToMethodId(name);
if (id != 0) {
f = prototypeFunctionPool[id];
if (f == null) { f = wrapMethod(name, id); }
if (f == IdFunction.WAS_OVERWRITTEN) { break L; }
}
else {
f = checkConstructor(name);
if (f == null) { break L; }
}
// Update cache
f.methodName = name;
lastFunction = f;
return f;
}
IdFunction f = lastFunction;
if (f.methodName == name) {
if (prototypeFunctionPool[f.methodId] == f) {
return f;
}
lastFunction = IdFunction.WAS_OVERWRITTEN;
}
int id = mapNameToMethodId(name);
if (id != 0) {
f = prototypeFunctionPool[id];
if (f == null) { f = wrapMethod(name, id); }
if (f == IdFunction.WAS_OVERWRITTEN) { break L; }
}
else {
f = checkConstructor(name);
if (f == null) { break L; }
}
// Update cache
f.methodName = name;
lastFunction = f;
return f;
}
return super.get(name, start);
}
public void put(String name, Scriptable start, Object value) {
if (doOverwrite(name, start)) {
super.put(name, start, value);
}
if (doOverwrite(name, start)) {
super.put(name, start, value);
}
}
public void delete(String name) {
if (doOverwrite(name, this)) {
super.delete(name);
}
super.delete(name);
}
}
public void scopeInit(Context cx, Scriptable scope, boolean sealed) {
useDynamicScope = cx.hasCompileFunctionsWithDynamicScope();
prototypeFunctionPool = new IdFunction[getMaxPrototypeMethodId() + 1];
prototypeFunctionPool = new IdFunction[getMaxPrototypeMethodId() + 1];
seal_functions = sealed;
setPrototype(getObjectPrototype(scope));
seal_functions = sealed;
setPrototype(getObjectPrototype(scope));
String name = getClassName();
IdFunction ctor = newIdFunction(name, CONSTRUCTOR_ID);
@ -151,165 +151,139 @@ public abstract class IdScriptable extends ScriptableObject
ctor.setImmunePrototypeProperty(this);
fillConstructorProperties(cx, ctor, sealed);
fillConstructorProperties(cx, ctor, sealed);
if (!name.equals("With")) {
// A "With" object would delegate these calls to the prototype:
// not the right thing to do here!
prototypeFunctionPool[CONSTRUCTOR_ID] = ctor;
prototypeFunctionPool[CONSTRUCTOR_ID] = ctor;
}
if (sealed) {
ctor.sealObject();
ctor.addPropertyAttribute(READONLY);
sealObject();
}
}
defineProperty(scope, name, ctor, ScriptableObject.DONTENUM);
}
defineProperty(scope, name, ctor, ScriptableObject.DONTENUM);
}
protected void fillConstructorProperties
(Context cx, IdFunction ctor, boolean sealed)
{
}
protected void fillConstructorProperties
(Context cx, IdFunction ctor, boolean sealed)
{
}
protected void addIdFunctionProperty
(Scriptable obj, String name, int id, boolean sealed)
{
IdFunction f = newIdFunction(name, id);
if (sealed) { f.sealObject(); }
defineProperty(obj, name, f, DONTENUM);
}
protected void addIdFunctionProperty
(Scriptable obj, String name, int id, boolean sealed)
{
IdFunction f = newIdFunction(name, id);
if (sealed) { f.sealObject(); }
defineProperty(obj, name, f, DONTENUM);
}
/** Utility method for converting target object into native this.
Possible usage would be to have a private function like realThis:
private NativeSomething realThis(Scriptable thisObj,
IdFunction f, boolean searchPrototype)
{
while (!(thisObj instanceof NativeSomething)) {
thisObj = nextInstanceCheck(thisObj, f, searchPrototype);
}
return (NativeSomething)thisObj;
}
Note that although such function can be implemented universally via
java.lang.Class.isInstance(), it would be much more slower
Possible usage would be to have a private function like realThis:
private NativeSomething realThis(Scriptable thisObj,
IdFunction f, boolean searchPrototype)
{
while (!(thisObj instanceof NativeSomething)) {
thisObj = nextInstanceCheck(thisObj, f, searchPrototype);
}
return (NativeSomething)thisObj;
}
Note that although such function can be implemented universally via
java.lang.Class.isInstance(), it would be much more slower
*/
protected Scriptable nextInstanceCheck(Scriptable thisObj,
IdFunction f,
boolean searchPrototype)
{
if (searchPrototype && useDynamicScope) {
thisObj = thisObj.getPrototype();
if (thisObj != null) { return thisObj; }
}
throw NativeGlobal.typeError1("msg.incompat.call", f.methodName, f);
}
protected IdFunction newIdFunction(String name, int id) {
return new IdFunction(this, name, id);
}
IdFunction f,
boolean searchPrototype)
{
if (searchPrototype && useDynamicScope) {
thisObj = thisObj.getPrototype();
if (thisObj != null) { return thisObj; }
}
throw NativeGlobal.typeError1("msg.incompat.call", f.methodName, f);
}
protected IdFunction newIdFunction(String name, int id) {
return new IdFunction(this, name, id);
}
protected final Object wrap_dbl(double x) {
protected final Object wrap_double(double x) {
return (x == x) ? new Double(x) : ScriptRuntime.NaNobj;
}
}
protected final Object wrap_int(int x) {
byte b = (byte)x;
protected final Object wrap_int(int x) {
byte b = (byte)x;
if (b == x) { return new Byte(b); }
return new Integer(x);
}
protected final Object wrap_boolean(boolean x) {
return x ? Boolean.TRUE : Boolean.FALSE;
}
protected final double to_dbl(Object arg) {
return ScriptRuntime.toNumber(arg);
}
protected final double to_int(Object arg) {
return ScriptRuntime.toInt32(arg);
}
protected final boolean to_boolean(Object arg) {
return ScriptRuntime.toBoolean(arg);
}
protected final String to_str(Object arg) {
return ScriptRuntime.toString(arg);
}
protected final double to_dbl(Object[] args, int index) {
return ScriptRuntime.toNumber(args, index);
}
protected final double to_int(Object[] args, int index) {
return ScriptRuntime.toInt32(args, index);
}
protected final boolean to_boolean(Object[] args, int index) {
return (index < args.length) ? to_boolean(args[index]) : false;
}
protected final String to_str(Object[] args, int index) {
return ScriptRuntime.toString(args, index);
}
return new Integer(x);
}
protected final Object wrap_long(long x) {
int i = (int)x;
if (i == x) { return wrap_int(i); }
return new Long(x);
}
protected final Object wrap_boolean(boolean x) {
return x ? Boolean.TRUE : Boolean.FALSE;
}
// Return true to invoke put/delete in super class
private boolean doOverwrite(String name, Scriptable start) {
// Let the super class to throw exceptions for sealed objects
if (!isSealed() && prototypeFunctionPool != null) {
if (null != checkConstructor(name)) {
// If constructor is present, it is read-only
return false;
}
if (this == start) {
int id = mapNameToMethodId(name);
if (id != 0) {
overwriteMethod(id);
return true;
}
}
}
return true;
}
private IdFunction wrapMethod(String name, int id) {
synchronized (this) {
IdFunction f = prototypeFunctionPool[id];
if (f == null) {
f = newIdFunction(name, id);
if (seal_functions) { f.sealObject(); }
prototypeFunctionPool[id] = f;
}
return f;
}
}
private void overwriteMethod(int id) {
if (prototypeFunctionPool[id] != IdFunction.WAS_OVERWRITTEN) {
// Must be synchronized to avoid clearance of overwritten mark
// by another thread running in wrapMethod
synchronized (this) {
prototypeFunctionPool[id] = IdFunction.WAS_OVERWRITTEN;
}
}
}
private IdFunction checkConstructor(String name) {
return name.equals("constructor")
? prototypeFunctionPool[CONSTRUCTOR_ID]
: null;
}
if (!isSealed() && prototypeFunctionPool != null) {
if (null != checkConstructor(name)) {
// If constructor is present, it is read-only
return false;
}
if (this == start) {
int id = mapNameToMethodId(name);
if (id != 0) {
overwriteMethod(id);
return true;
}
}
}
return true;
}
private IdFunction wrapMethod(String name, int id) {
synchronized (this) {
IdFunction f = prototypeFunctionPool[id];
if (f == null) {
f = newIdFunction(name, id);
if (seal_functions) { f.sealObject(); }
prototypeFunctionPool[id] = f;
}
return f;
}
}
private void overwriteMethod(int id) {
if (prototypeFunctionPool[id] != IdFunction.WAS_OVERWRITTEN) {
// Must be synchronized to avoid clearance of overwritten mark
// by another thread running in wrapMethod
synchronized (this) {
prototypeFunctionPool[id] = IdFunction.WAS_OVERWRITTEN;
}
}
}
private IdFunction checkConstructor(String name) {
return name.equals("constructor")
? prototypeFunctionPool[CONSTRUCTOR_ID]
: null;
}
private IdFunction[] prototypeFunctionPool;
// Not null to simplify logic
private IdFunction lastFunction = IdFunction.WAS_OVERWRITTEN;
private boolean seal_functions;
private IdFunction lastFunction = IdFunction.WAS_OVERWRITTEN;
private boolean seal_functions;
private boolean useDynamicScope;
}

View File

@ -121,11 +121,11 @@ public class NativeDate extends IdScriptable {
throws JavaScriptException
{
switch (methodId) {
case ConstructorId_UTC: return wrap_dbl
case ConstructorId_UTC: return wrap_double
(jsStaticFunction_UTC(cx, thisObj, args, f));
case ConstructorId_parse: return wrap_dbl
(jsStaticFunction_parse(to_str(args, 0)));
case ConstructorId_parse: return wrap_double
(jsStaticFunction_parse(ScriptRuntime.toString(args, 0)));
case CONSTRUCTOR_ID:
return jsConstructor(cx, args, f, thisObj == null);
@ -139,125 +139,127 @@ public class NativeDate extends IdScriptable {
case Id_toDateString: return realThis(thisObj, f, true).
jsFunction_toDateString();
case Id_toLocaleString: return realThis(thisObj, f, true).
case Id_toLocaleString: return realThis(thisObj, f, true).
jsFunction_toLocaleString();
case Id_toLocaleTimeString: return realThis(thisObj, f, true).
case Id_toLocaleTimeString: return realThis(thisObj, f, true).
jsFunction_toLocaleTimeString();
case Id_toLocaleDateString: return realThis(thisObj, f, true).
case Id_toLocaleDateString: return realThis(thisObj, f, true).
jsFunction_toLocaleDateString();
case Id_toUTCString: return realThis(thisObj, f, true).
jsFunction_toUTCString();
case Id_valueOf: return wrap_dbl(realThis(thisObj, f, true).
case Id_valueOf: return wrap_double(realThis(thisObj, f, true).
jsFunction_valueOf());
case Id_getTime: return wrap_dbl(realThis(thisObj, f, true).
case Id_getTime: return wrap_double(realThis(thisObj, f, true).
jsFunction_getTime());
case Id_getYear: return wrap_dbl(realThis(thisObj, f, true).
case Id_getYear: return wrap_double(realThis(thisObj, f, true).
jsFunction_getYear());
case Id_getFullYear: return wrap_dbl(realThis(thisObj, f, true).
case Id_getFullYear: return wrap_double(realThis(thisObj, f, true).
jsFunction_getFullYear());
case Id_getUTCFullYear: return wrap_dbl(realThis(thisObj, f, true).
case Id_getUTCFullYear: return wrap_double(realThis(thisObj, f, true).
jsFunction_getUTCFullYear());
case Id_getMonth: return wrap_dbl(realThis(thisObj, f, true).
case Id_getMonth: return wrap_double(realThis(thisObj, f, true).
jsFunction_getMonth());
case Id_getUTCMonth: return wrap_dbl(realThis(thisObj, f, true).
case Id_getUTCMonth: return wrap_double(realThis(thisObj, f, true).
jsFunction_getUTCMonth());
case Id_getDate: return wrap_dbl(realThis(thisObj, f, true).
case Id_getDate: return wrap_double(realThis(thisObj, f, true).
jsFunction_getDate());
case Id_getUTCDate: return wrap_dbl(realThis(thisObj, f, true).
case Id_getUTCDate: return wrap_double(realThis(thisObj, f, true).
jsFunction_getUTCDate());
case Id_getDay: return wrap_dbl(realThis(thisObj, f, true).
case Id_getDay: return wrap_double(realThis(thisObj, f, true).
jsFunction_getDay());
case Id_getUTCDay: return wrap_dbl(realThis(thisObj, f, true).
case Id_getUTCDay: return wrap_double(realThis(thisObj, f, true).
jsFunction_getUTCDay());
case Id_getHours: return wrap_dbl(realThis(thisObj, f, true).
case Id_getHours: return wrap_double(realThis(thisObj, f, true).
jsFunction_getHours());
case Id_getUTCHours: return wrap_dbl(realThis(thisObj, f, true).
case Id_getUTCHours: return wrap_double(realThis(thisObj, f, true).
jsFunction_getUTCHours());
case Id_getMinutes: return wrap_dbl(realThis(thisObj, f, true).
case Id_getMinutes: return wrap_double(realThis(thisObj, f, true).
jsFunction_getMinutes());
case Id_getUTCMinutes: return wrap_dbl(realThis(thisObj, f, true).
case Id_getUTCMinutes: return wrap_double(realThis(thisObj, f, true).
jsFunction_getUTCMinutes());
case Id_getSeconds: return wrap_dbl(realThis(thisObj, f, true).
case Id_getSeconds: return wrap_double(realThis(thisObj, f, true).
jsFunction_getSeconds());
case Id_getUTCSeconds: return wrap_dbl(realThis(thisObj, f, true).
case Id_getUTCSeconds: return wrap_double(realThis(thisObj, f, true).
jsFunction_getUTCSeconds());
case Id_getMilliseconds: return wrap_dbl(realThis(thisObj, f, true).
case Id_getMilliseconds: return wrap_double(realThis(thisObj, f, true).
jsFunction_getMilliseconds());
case Id_getUTCMilliseconds: return wrap_dbl(realThis(thisObj, f, true).
jsFunction_getUTCMilliseconds());
case Id_getUTCMilliseconds:
return wrap_double(realThis(thisObj, f, true).
jsFunction_getUTCMilliseconds());
case Id_getTimezoneOffset: return wrap_dbl(realThis(thisObj, f, true).
jsFunction_getTimezoneOffset());
case Id_getTimezoneOffset:
return wrap_double(realThis(thisObj, f, true).
jsFunction_getTimezoneOffset());
case Id_setTime: return wrap_dbl(realThis(thisObj, f, true).
jsFunction_setTime(to_dbl(args, 0)));
case Id_setTime: return wrap_double(realThis(thisObj, f, true).
jsFunction_setTime(ScriptRuntime.toNumber(args, 0)));
case Id_setMilliseconds: return wrap_dbl
case Id_setMilliseconds: return wrap_double
(jsFunction_setMilliseconds(cx, thisObj, args, f));
case Id_setUTCMilliseconds: return wrap_dbl(
case Id_setUTCMilliseconds: return wrap_double(
jsFunction_setUTCMilliseconds(cx, thisObj, args, f));
case Id_setSeconds: return wrap_dbl(
case Id_setSeconds: return wrap_double(
jsFunction_setSeconds(cx, thisObj, args, f));
case Id_setUTCSeconds:return wrap_dbl(
case Id_setUTCSeconds: return wrap_double(
jsFunction_setUTCSeconds(cx, thisObj, args, f));
case Id_setMinutes: return wrap_dbl(
case Id_setMinutes: return wrap_double(
jsFunction_setMinutes(cx, thisObj, args, f));
case Id_setUTCMinutes: return wrap_dbl(
case Id_setUTCMinutes: return wrap_double(
jsFunction_setUTCMinutes(cx, thisObj, args, f));
case Id_setHours: return wrap_dbl(
case Id_setHours: return wrap_double(
jsFunction_setHours(cx, thisObj, args, f));
case Id_setUTCHours: return wrap_dbl(
case Id_setUTCHours: return wrap_double(
jsFunction_setUTCHours(cx, thisObj, args, f));
case Id_setDate: return wrap_dbl(
case Id_setDate: return wrap_double(
jsFunction_setDate(cx, thisObj, args, f));
case Id_setUTCDate: return wrap_dbl(
case Id_setUTCDate: return wrap_double(
jsFunction_setUTCDate(cx, thisObj, args, f));
case Id_setMonth: return wrap_dbl(
case Id_setMonth: return wrap_double(
jsFunction_setMonth(cx, thisObj, args, f));
case Id_setUTCMonth: return wrap_dbl(
case Id_setUTCMonth: return wrap_double(
jsFunction_setUTCMonth(cx, thisObj, args, f));
case Id_setFullYear: return wrap_dbl(
case Id_setFullYear: return wrap_double(
jsFunction_setFullYear(cx, thisObj, args, f));
case Id_setUTCFullYear: return wrap_dbl(
case Id_setUTCFullYear: return wrap_double(
jsFunction_setUTCFullYear(cx, thisObj, args, f));
case Id_setYear: return wrap_dbl(realThis(thisObj, f, true).
jsFunction_setYear(to_dbl(args, 0)));
case Id_setYear: return wrap_double(realThis(thisObj, f, true).
jsFunction_setYear(ScriptRuntime.toNumber(args, 0)));
}
return Scriptable.NOT_FOUND;

View File

@ -50,18 +50,62 @@ import java.lang.reflect.Method;
* @author Mike Shaver
*/
public class NativeGlobal implements IdFunction.Master {
public class NativeGlobal implements ScopeInitializer, IdFunction.Master {
public static void init(Scriptable scope)
throws PropertyException,
NotAFunctionException,
JavaScriptException
{
NativeGlobal instance = new NativeGlobal();
Context cx = Context.getContext();
// We can downcast here because Context.initStandardObjects
// takes a ScriptableObject scope.
instance.initForGlobal(cx, (ScriptableObject) scope, false);
public void scopeInit(Context cx, Scriptable scope, boolean sealed) {
for (int id = 1; id <= LAST_METHOD_ID; ++id) {
String name = getMethodName(id);
IdFunction f = new IdFunction(this, name, id);
f.setParentScope(scope);
if (sealed) { f.sealObject(); }
ScriptableObject.defineProperty(scope, name, f,
ScriptableObject.DONTENUM);
}
ScriptableObject.defineProperty(scope, "NaN",
ScriptRuntime.NaNobj,
ScriptableObject.DONTENUM);
ScriptableObject.defineProperty(scope, "Infinity",
new Double(Double.POSITIVE_INFINITY),
ScriptableObject.DONTENUM);
ScriptableObject.defineProperty(scope, "undefined",
Undefined.instance,
ScriptableObject.DONTENUM);
String[] errorMethods = { "ConversionError",
"EvalError",
"RangeError",
"ReferenceError",
"SyntaxError",
"TypeError",
"URIError"
};
/*
Each error constructor gets its own Error object as a prototype,
with the 'name' property set to the name of the error.
*/
for (int i = 0; i < errorMethods.length; i++) {
String name = errorMethods[i];
IdFunction ctor = new IdFunction(this, name, Id_new_CommonError);
ctor.setFunctionType(IdFunction.FUNCTION_AND_CONSTRUCTOR);
ctor.setParentScope(scope);
ScriptableObject.defineProperty(scope, name, ctor,
ScriptableObject.DONTENUM);
Scriptable errorProto = ScriptRuntime.newObject
(cx, scope, "Error", ScriptRuntime.emptyArgs);
errorProto.put("name", errorProto, name);
ctor.put("prototype", ctor, errorProto);
if (sealed) {
ctor.sealObject();
if (errorProto instanceof ScriptableObject) {
((ScriptableObject)errorProto).sealObject();
}
}
}
}
public Object execMethod(int methodId, IdFunction function, Context cx,
@ -112,60 +156,6 @@ public class NativeGlobal implements IdFunction.Master {
return null;
}
public void initForGlobal(Context cx, ScriptableObject global,
boolean sealed)
throws PropertyException,
NotAFunctionException,
JavaScriptException
{
for (int id = 1; id <= LAST_METHOD_ID; ++id) {
String name = getMethodName(id);
IdFunction f = new IdFunction(this, name, id);
f.setParentScope(global);
if (sealed) { f.sealObject(); }
global.defineProperty(name, f, ScriptableObject.DONTENUM);
}
global.defineProperty("NaN", ScriptRuntime.NaNobj,
ScriptableObject.DONTENUM);
global.defineProperty("Infinity", new Double(Double.POSITIVE_INFINITY),
ScriptableObject.DONTENUM);
global.defineProperty("undefined", Undefined.instance,
ScriptableObject.DONTENUM);
String[] errorMethods = { "ConversionError",
"EvalError",
"RangeError",
"ReferenceError",
"SyntaxError",
"TypeError",
"URIError"
};
/*
Each error constructor gets its own Error object as a prototype,
with the 'name' property set to the name of the error.
*/
for (int i = 0; i < errorMethods.length; i++) {
String name = errorMethods[i];
IdFunction ctor = new IdFunction(this, name, Id_new_CommonError);
ctor.setFunctionType(IdFunction.FUNCTION_AND_CONSTRUCTOR);
ctor.setParentScope(global);
global.defineProperty(name, ctor, ScriptableObject.DONTENUM);
Scriptable errorProto = cx.newObject(global, "Error");
errorProto.put("name", errorProto, name);
ctor.put("prototype", ctor, errorProto);
if (sealed) {
ctor.sealObject();
if (errorProto instanceof ScriptableObject) {
((ScriptableObject)errorProto).sealObject();
}
}
}
}
/**
* The global method parseInt, as per ECMA-262 15.1.2.2.
*/

View File

@ -39,10 +39,10 @@ package org.mozilla.javascript;
Base class for native object implementation that uses IdFunction to export its methods to script via <class-name>.prototype object.
Any descendant should implement at least the following methods:
getMaxPrototypeMethodId
mapNameToMethodId
execMethod
methodArity
getMaxPrototypeMethodId
mapNameToMethodId
execMethod
methodArity
Any implementation of execMethod and methodArity should assume that if
methodId is 0 (CONSTRUCTOR_ID), it denotes EcmaScript constructor for
@ -51,18 +51,18 @@ methodId is 0 (CONSTRUCTOR_ID), it denotes EcmaScript constructor for
To customize initializition of constructor and protype objects, descendant
may override initForGlobal or fillConstructorProperties methods.
To be less verbose with wrapping/unwrapping of script objects, descaendant
should use to_/wrap_ set of methods.
To wrap primitive java types to script objects, descaendant
should use wrap_<primitive-type> set of methods.
*/
public abstract class IdScriptable extends ScriptableObject
implements IdFunction.Master, ScopeInitializer
{
public static final int CONSTRUCTOR_ID = 0;
public static final int CONSTRUCTOR_ID = 0;
/** 'thisObj' will be null if invoked as constructor, in which case
** instance of Scriptable should be returned.
*/
*/
public abstract Object execMethod(int methodId, IdFunction function,
Context cx, Scriptable scope,
Scriptable thisObj, Object[] args)
@ -70,77 +70,77 @@ public abstract class IdScriptable extends ScriptableObject
public abstract int methodArity(int methodId, IdFunction function);
/** Return maximum method id for prototype function */
protected abstract int getMaxPrototypeMethodId();
/** Map name into id for prototypr method.
** Should return 0 if not name is not prototype method
** or value between 1 and getMaxPrototypeMethodId()
*/
protected abstract int mapNameToMethodId(String name);
/** Return maximum method id for prototype function */
protected abstract int getMaxPrototypeMethodId();
/** Map name into id for prototypr method.
** Should return 0 if not name is not prototype method
** or value between 1 and getMaxPrototypeMethodId()
*/
protected abstract int mapNameToMethodId(String name);
public boolean has(String name, Scriptable start) {
if (prototypeFunctionPool != null) {
int id = mapNameToMethodId(name);
if (id != 0) {
IdFunction f = prototypeFunctionPool[id];
if (f != IdFunction.WAS_OVERWRITTEN) { return true; }
}
else {
if (null != checkConstructor(name)) { return true; }
}
}
int id = mapNameToMethodId(name);
if (id != 0) {
IdFunction f = prototypeFunctionPool[id];
if (f != IdFunction.WAS_OVERWRITTEN) { return true; }
}
else {
if (null != checkConstructor(name)) { return true; }
}
}
return super.has(name, start);
}
public Object get(String name, Scriptable start) {
L: if (prototypeFunctionPool != null) {
IdFunction f = lastFunction;
if (f.methodName == name) {
if (prototypeFunctionPool[f.methodId] == f) {
return f;
}
lastFunction = IdFunction.WAS_OVERWRITTEN;
}
int id = mapNameToMethodId(name);
if (id != 0) {
f = prototypeFunctionPool[id];
if (f == null) { f = wrapMethod(name, id); }
if (f == IdFunction.WAS_OVERWRITTEN) { break L; }
}
else {
f = checkConstructor(name);
if (f == null) { break L; }
}
// Update cache
f.methodName = name;
lastFunction = f;
return f;
}
IdFunction f = lastFunction;
if (f.methodName == name) {
if (prototypeFunctionPool[f.methodId] == f) {
return f;
}
lastFunction = IdFunction.WAS_OVERWRITTEN;
}
int id = mapNameToMethodId(name);
if (id != 0) {
f = prototypeFunctionPool[id];
if (f == null) { f = wrapMethod(name, id); }
if (f == IdFunction.WAS_OVERWRITTEN) { break L; }
}
else {
f = checkConstructor(name);
if (f == null) { break L; }
}
// Update cache
f.methodName = name;
lastFunction = f;
return f;
}
return super.get(name, start);
}
public void put(String name, Scriptable start, Object value) {
if (doOverwrite(name, start)) {
super.put(name, start, value);
}
if (doOverwrite(name, start)) {
super.put(name, start, value);
}
}
public void delete(String name) {
if (doOverwrite(name, this)) {
super.delete(name);
}
super.delete(name);
}
}
public void scopeInit(Context cx, Scriptable scope, boolean sealed) {
useDynamicScope = cx.hasCompileFunctionsWithDynamicScope();
prototypeFunctionPool = new IdFunction[getMaxPrototypeMethodId() + 1];
prototypeFunctionPool = new IdFunction[getMaxPrototypeMethodId() + 1];
seal_functions = sealed;
setPrototype(getObjectPrototype(scope));
seal_functions = sealed;
setPrototype(getObjectPrototype(scope));
String name = getClassName();
IdFunction ctor = newIdFunction(name, CONSTRUCTOR_ID);
@ -151,165 +151,139 @@ public abstract class IdScriptable extends ScriptableObject
ctor.setImmunePrototypeProperty(this);
fillConstructorProperties(cx, ctor, sealed);
fillConstructorProperties(cx, ctor, sealed);
if (!name.equals("With")) {
// A "With" object would delegate these calls to the prototype:
// not the right thing to do here!
prototypeFunctionPool[CONSTRUCTOR_ID] = ctor;
prototypeFunctionPool[CONSTRUCTOR_ID] = ctor;
}
if (sealed) {
ctor.sealObject();
ctor.addPropertyAttribute(READONLY);
sealObject();
}
}
defineProperty(scope, name, ctor, ScriptableObject.DONTENUM);
}
defineProperty(scope, name, ctor, ScriptableObject.DONTENUM);
}
protected void fillConstructorProperties
(Context cx, IdFunction ctor, boolean sealed)
{
}
protected void fillConstructorProperties
(Context cx, IdFunction ctor, boolean sealed)
{
}
protected void addIdFunctionProperty
(Scriptable obj, String name, int id, boolean sealed)
{
IdFunction f = newIdFunction(name, id);
if (sealed) { f.sealObject(); }
defineProperty(obj, name, f, DONTENUM);
}
protected void addIdFunctionProperty
(Scriptable obj, String name, int id, boolean sealed)
{
IdFunction f = newIdFunction(name, id);
if (sealed) { f.sealObject(); }
defineProperty(obj, name, f, DONTENUM);
}
/** Utility method for converting target object into native this.
Possible usage would be to have a private function like realThis:
private NativeSomething realThis(Scriptable thisObj,
IdFunction f, boolean searchPrototype)
{
while (!(thisObj instanceof NativeSomething)) {
thisObj = nextInstanceCheck(thisObj, f, searchPrototype);
}
return (NativeSomething)thisObj;
}
Note that although such function can be implemented universally via
java.lang.Class.isInstance(), it would be much more slower
Possible usage would be to have a private function like realThis:
private NativeSomething realThis(Scriptable thisObj,
IdFunction f, boolean searchPrototype)
{
while (!(thisObj instanceof NativeSomething)) {
thisObj = nextInstanceCheck(thisObj, f, searchPrototype);
}
return (NativeSomething)thisObj;
}
Note that although such function can be implemented universally via
java.lang.Class.isInstance(), it would be much more slower
*/
protected Scriptable nextInstanceCheck(Scriptable thisObj,
IdFunction f,
boolean searchPrototype)
{
if (searchPrototype && useDynamicScope) {
thisObj = thisObj.getPrototype();
if (thisObj != null) { return thisObj; }
}
throw NativeGlobal.typeError1("msg.incompat.call", f.methodName, f);
}
protected IdFunction newIdFunction(String name, int id) {
return new IdFunction(this, name, id);
}
IdFunction f,
boolean searchPrototype)
{
if (searchPrototype && useDynamicScope) {
thisObj = thisObj.getPrototype();
if (thisObj != null) { return thisObj; }
}
throw NativeGlobal.typeError1("msg.incompat.call", f.methodName, f);
}
protected IdFunction newIdFunction(String name, int id) {
return new IdFunction(this, name, id);
}
protected final Object wrap_dbl(double x) {
protected final Object wrap_double(double x) {
return (x == x) ? new Double(x) : ScriptRuntime.NaNobj;
}
}
protected final Object wrap_int(int x) {
byte b = (byte)x;
protected final Object wrap_int(int x) {
byte b = (byte)x;
if (b == x) { return new Byte(b); }
return new Integer(x);
}
protected final Object wrap_boolean(boolean x) {
return x ? Boolean.TRUE : Boolean.FALSE;
}
protected final double to_dbl(Object arg) {
return ScriptRuntime.toNumber(arg);
}
protected final double to_int(Object arg) {
return ScriptRuntime.toInt32(arg);
}
protected final boolean to_boolean(Object arg) {
return ScriptRuntime.toBoolean(arg);
}
protected final String to_str(Object arg) {
return ScriptRuntime.toString(arg);
}
protected final double to_dbl(Object[] args, int index) {
return ScriptRuntime.toNumber(args, index);
}
protected final double to_int(Object[] args, int index) {
return ScriptRuntime.toInt32(args, index);
}
protected final boolean to_boolean(Object[] args, int index) {
return (index < args.length) ? to_boolean(args[index]) : false;
}
protected final String to_str(Object[] args, int index) {
return ScriptRuntime.toString(args, index);
}
return new Integer(x);
}
protected final Object wrap_long(long x) {
int i = (int)x;
if (i == x) { return wrap_int(i); }
return new Long(x);
}
protected final Object wrap_boolean(boolean x) {
return x ? Boolean.TRUE : Boolean.FALSE;
}
// Return true to invoke put/delete in super class
private boolean doOverwrite(String name, Scriptable start) {
// Let the super class to throw exceptions for sealed objects
if (!isSealed() && prototypeFunctionPool != null) {
if (null != checkConstructor(name)) {
// If constructor is present, it is read-only
return false;
}
if (this == start) {
int id = mapNameToMethodId(name);
if (id != 0) {
overwriteMethod(id);
return true;
}
}
}
return true;
}
private IdFunction wrapMethod(String name, int id) {
synchronized (this) {
IdFunction f = prototypeFunctionPool[id];
if (f == null) {
f = newIdFunction(name, id);
if (seal_functions) { f.sealObject(); }
prototypeFunctionPool[id] = f;
}
return f;
}
}
private void overwriteMethod(int id) {
if (prototypeFunctionPool[id] != IdFunction.WAS_OVERWRITTEN) {
// Must be synchronized to avoid clearance of overwritten mark
// by another thread running in wrapMethod
synchronized (this) {
prototypeFunctionPool[id] = IdFunction.WAS_OVERWRITTEN;
}
}
}
private IdFunction checkConstructor(String name) {
return name.equals("constructor")
? prototypeFunctionPool[CONSTRUCTOR_ID]
: null;
}
if (!isSealed() && prototypeFunctionPool != null) {
if (null != checkConstructor(name)) {
// If constructor is present, it is read-only
return false;
}
if (this == start) {
int id = mapNameToMethodId(name);
if (id != 0) {
overwriteMethod(id);
return true;
}
}
}
return true;
}
private IdFunction wrapMethod(String name, int id) {
synchronized (this) {
IdFunction f = prototypeFunctionPool[id];
if (f == null) {
f = newIdFunction(name, id);
if (seal_functions) { f.sealObject(); }
prototypeFunctionPool[id] = f;
}
return f;
}
}
private void overwriteMethod(int id) {
if (prototypeFunctionPool[id] != IdFunction.WAS_OVERWRITTEN) {
// Must be synchronized to avoid clearance of overwritten mark
// by another thread running in wrapMethod
synchronized (this) {
prototypeFunctionPool[id] = IdFunction.WAS_OVERWRITTEN;
}
}
}
private IdFunction checkConstructor(String name) {
return name.equals("constructor")
? prototypeFunctionPool[CONSTRUCTOR_ID]
: null;
}
private IdFunction[] prototypeFunctionPool;
// Not null to simplify logic
private IdFunction lastFunction = IdFunction.WAS_OVERWRITTEN;
private boolean seal_functions;
private IdFunction lastFunction = IdFunction.WAS_OVERWRITTEN;
private boolean seal_functions;
private boolean useDynamicScope;
}

View File

@ -121,11 +121,11 @@ public class NativeDate extends IdScriptable {
throws JavaScriptException
{
switch (methodId) {
case ConstructorId_UTC: return wrap_dbl
case ConstructorId_UTC: return wrap_double
(jsStaticFunction_UTC(cx, thisObj, args, f));
case ConstructorId_parse: return wrap_dbl
(jsStaticFunction_parse(to_str(args, 0)));
case ConstructorId_parse: return wrap_double
(jsStaticFunction_parse(ScriptRuntime.toString(args, 0)));
case CONSTRUCTOR_ID:
return jsConstructor(cx, args, f, thisObj == null);
@ -139,125 +139,127 @@ public class NativeDate extends IdScriptable {
case Id_toDateString: return realThis(thisObj, f, true).
jsFunction_toDateString();
case Id_toLocaleString: return realThis(thisObj, f, true).
case Id_toLocaleString: return realThis(thisObj, f, true).
jsFunction_toLocaleString();
case Id_toLocaleTimeString: return realThis(thisObj, f, true).
case Id_toLocaleTimeString: return realThis(thisObj, f, true).
jsFunction_toLocaleTimeString();
case Id_toLocaleDateString: return realThis(thisObj, f, true).
case Id_toLocaleDateString: return realThis(thisObj, f, true).
jsFunction_toLocaleDateString();
case Id_toUTCString: return realThis(thisObj, f, true).
jsFunction_toUTCString();
case Id_valueOf: return wrap_dbl(realThis(thisObj, f, true).
case Id_valueOf: return wrap_double(realThis(thisObj, f, true).
jsFunction_valueOf());
case Id_getTime: return wrap_dbl(realThis(thisObj, f, true).
case Id_getTime: return wrap_double(realThis(thisObj, f, true).
jsFunction_getTime());
case Id_getYear: return wrap_dbl(realThis(thisObj, f, true).
case Id_getYear: return wrap_double(realThis(thisObj, f, true).
jsFunction_getYear());
case Id_getFullYear: return wrap_dbl(realThis(thisObj, f, true).
case Id_getFullYear: return wrap_double(realThis(thisObj, f, true).
jsFunction_getFullYear());
case Id_getUTCFullYear: return wrap_dbl(realThis(thisObj, f, true).
case Id_getUTCFullYear: return wrap_double(realThis(thisObj, f, true).
jsFunction_getUTCFullYear());
case Id_getMonth: return wrap_dbl(realThis(thisObj, f, true).
case Id_getMonth: return wrap_double(realThis(thisObj, f, true).
jsFunction_getMonth());
case Id_getUTCMonth: return wrap_dbl(realThis(thisObj, f, true).
case Id_getUTCMonth: return wrap_double(realThis(thisObj, f, true).
jsFunction_getUTCMonth());
case Id_getDate: return wrap_dbl(realThis(thisObj, f, true).
case Id_getDate: return wrap_double(realThis(thisObj, f, true).
jsFunction_getDate());
case Id_getUTCDate: return wrap_dbl(realThis(thisObj, f, true).
case Id_getUTCDate: return wrap_double(realThis(thisObj, f, true).
jsFunction_getUTCDate());
case Id_getDay: return wrap_dbl(realThis(thisObj, f, true).
case Id_getDay: return wrap_double(realThis(thisObj, f, true).
jsFunction_getDay());
case Id_getUTCDay: return wrap_dbl(realThis(thisObj, f, true).
case Id_getUTCDay: return wrap_double(realThis(thisObj, f, true).
jsFunction_getUTCDay());
case Id_getHours: return wrap_dbl(realThis(thisObj, f, true).
case Id_getHours: return wrap_double(realThis(thisObj, f, true).
jsFunction_getHours());
case Id_getUTCHours: return wrap_dbl(realThis(thisObj, f, true).
case Id_getUTCHours: return wrap_double(realThis(thisObj, f, true).
jsFunction_getUTCHours());
case Id_getMinutes: return wrap_dbl(realThis(thisObj, f, true).
case Id_getMinutes: return wrap_double(realThis(thisObj, f, true).
jsFunction_getMinutes());
case Id_getUTCMinutes: return wrap_dbl(realThis(thisObj, f, true).
case Id_getUTCMinutes: return wrap_double(realThis(thisObj, f, true).
jsFunction_getUTCMinutes());
case Id_getSeconds: return wrap_dbl(realThis(thisObj, f, true).
case Id_getSeconds: return wrap_double(realThis(thisObj, f, true).
jsFunction_getSeconds());
case Id_getUTCSeconds: return wrap_dbl(realThis(thisObj, f, true).
case Id_getUTCSeconds: return wrap_double(realThis(thisObj, f, true).
jsFunction_getUTCSeconds());
case Id_getMilliseconds: return wrap_dbl(realThis(thisObj, f, true).
case Id_getMilliseconds: return wrap_double(realThis(thisObj, f, true).
jsFunction_getMilliseconds());
case Id_getUTCMilliseconds: return wrap_dbl(realThis(thisObj, f, true).
jsFunction_getUTCMilliseconds());
case Id_getUTCMilliseconds:
return wrap_double(realThis(thisObj, f, true).
jsFunction_getUTCMilliseconds());
case Id_getTimezoneOffset: return wrap_dbl(realThis(thisObj, f, true).
jsFunction_getTimezoneOffset());
case Id_getTimezoneOffset:
return wrap_double(realThis(thisObj, f, true).
jsFunction_getTimezoneOffset());
case Id_setTime: return wrap_dbl(realThis(thisObj, f, true).
jsFunction_setTime(to_dbl(args, 0)));
case Id_setTime: return wrap_double(realThis(thisObj, f, true).
jsFunction_setTime(ScriptRuntime.toNumber(args, 0)));
case Id_setMilliseconds: return wrap_dbl
case Id_setMilliseconds: return wrap_double
(jsFunction_setMilliseconds(cx, thisObj, args, f));
case Id_setUTCMilliseconds: return wrap_dbl(
case Id_setUTCMilliseconds: return wrap_double(
jsFunction_setUTCMilliseconds(cx, thisObj, args, f));
case Id_setSeconds: return wrap_dbl(
case Id_setSeconds: return wrap_double(
jsFunction_setSeconds(cx, thisObj, args, f));
case Id_setUTCSeconds:return wrap_dbl(
case Id_setUTCSeconds: return wrap_double(
jsFunction_setUTCSeconds(cx, thisObj, args, f));
case Id_setMinutes: return wrap_dbl(
case Id_setMinutes: return wrap_double(
jsFunction_setMinutes(cx, thisObj, args, f));
case Id_setUTCMinutes: return wrap_dbl(
case Id_setUTCMinutes: return wrap_double(
jsFunction_setUTCMinutes(cx, thisObj, args, f));
case Id_setHours: return wrap_dbl(
case Id_setHours: return wrap_double(
jsFunction_setHours(cx, thisObj, args, f));
case Id_setUTCHours: return wrap_dbl(
case Id_setUTCHours: return wrap_double(
jsFunction_setUTCHours(cx, thisObj, args, f));
case Id_setDate: return wrap_dbl(
case Id_setDate: return wrap_double(
jsFunction_setDate(cx, thisObj, args, f));
case Id_setUTCDate: return wrap_dbl(
case Id_setUTCDate: return wrap_double(
jsFunction_setUTCDate(cx, thisObj, args, f));
case Id_setMonth: return wrap_dbl(
case Id_setMonth: return wrap_double(
jsFunction_setMonth(cx, thisObj, args, f));
case Id_setUTCMonth: return wrap_dbl(
case Id_setUTCMonth: return wrap_double(
jsFunction_setUTCMonth(cx, thisObj, args, f));
case Id_setFullYear: return wrap_dbl(
case Id_setFullYear: return wrap_double(
jsFunction_setFullYear(cx, thisObj, args, f));
case Id_setUTCFullYear: return wrap_dbl(
case Id_setUTCFullYear: return wrap_double(
jsFunction_setUTCFullYear(cx, thisObj, args, f));
case Id_setYear: return wrap_dbl(realThis(thisObj, f, true).
jsFunction_setYear(to_dbl(args, 0)));
case Id_setYear: return wrap_double(realThis(thisObj, f, true).
jsFunction_setYear(ScriptRuntime.toNumber(args, 0)));
}
return Scriptable.NOT_FOUND;

View File

@ -50,18 +50,62 @@ import java.lang.reflect.Method;
* @author Mike Shaver
*/
public class NativeGlobal implements IdFunction.Master {
public class NativeGlobal implements ScopeInitializer, IdFunction.Master {
public static void init(Scriptable scope)
throws PropertyException,
NotAFunctionException,
JavaScriptException
{
NativeGlobal instance = new NativeGlobal();
Context cx = Context.getContext();
// We can downcast here because Context.initStandardObjects
// takes a ScriptableObject scope.
instance.initForGlobal(cx, (ScriptableObject) scope, false);
public void scopeInit(Context cx, Scriptable scope, boolean sealed) {
for (int id = 1; id <= LAST_METHOD_ID; ++id) {
String name = getMethodName(id);
IdFunction f = new IdFunction(this, name, id);
f.setParentScope(scope);
if (sealed) { f.sealObject(); }
ScriptableObject.defineProperty(scope, name, f,
ScriptableObject.DONTENUM);
}
ScriptableObject.defineProperty(scope, "NaN",
ScriptRuntime.NaNobj,
ScriptableObject.DONTENUM);
ScriptableObject.defineProperty(scope, "Infinity",
new Double(Double.POSITIVE_INFINITY),
ScriptableObject.DONTENUM);
ScriptableObject.defineProperty(scope, "undefined",
Undefined.instance,
ScriptableObject.DONTENUM);
String[] errorMethods = { "ConversionError",
"EvalError",
"RangeError",
"ReferenceError",
"SyntaxError",
"TypeError",
"URIError"
};
/*
Each error constructor gets its own Error object as a prototype,
with the 'name' property set to the name of the error.
*/
for (int i = 0; i < errorMethods.length; i++) {
String name = errorMethods[i];
IdFunction ctor = new IdFunction(this, name, Id_new_CommonError);
ctor.setFunctionType(IdFunction.FUNCTION_AND_CONSTRUCTOR);
ctor.setParentScope(scope);
ScriptableObject.defineProperty(scope, name, ctor,
ScriptableObject.DONTENUM);
Scriptable errorProto = ScriptRuntime.newObject
(cx, scope, "Error", ScriptRuntime.emptyArgs);
errorProto.put("name", errorProto, name);
ctor.put("prototype", ctor, errorProto);
if (sealed) {
ctor.sealObject();
if (errorProto instanceof ScriptableObject) {
((ScriptableObject)errorProto).sealObject();
}
}
}
}
public Object execMethod(int methodId, IdFunction function, Context cx,
@ -112,60 +156,6 @@ public class NativeGlobal implements IdFunction.Master {
return null;
}
public void initForGlobal(Context cx, ScriptableObject global,
boolean sealed)
throws PropertyException,
NotAFunctionException,
JavaScriptException
{
for (int id = 1; id <= LAST_METHOD_ID; ++id) {
String name = getMethodName(id);
IdFunction f = new IdFunction(this, name, id);
f.setParentScope(global);
if (sealed) { f.sealObject(); }
global.defineProperty(name, f, ScriptableObject.DONTENUM);
}
global.defineProperty("NaN", ScriptRuntime.NaNobj,
ScriptableObject.DONTENUM);
global.defineProperty("Infinity", new Double(Double.POSITIVE_INFINITY),
ScriptableObject.DONTENUM);
global.defineProperty("undefined", Undefined.instance,
ScriptableObject.DONTENUM);
String[] errorMethods = { "ConversionError",
"EvalError",
"RangeError",
"ReferenceError",
"SyntaxError",
"TypeError",
"URIError"
};
/*
Each error constructor gets its own Error object as a prototype,
with the 'name' property set to the name of the error.
*/
for (int i = 0; i < errorMethods.length; i++) {
String name = errorMethods[i];
IdFunction ctor = new IdFunction(this, name, Id_new_CommonError);
ctor.setFunctionType(IdFunction.FUNCTION_AND_CONSTRUCTOR);
ctor.setParentScope(global);
global.defineProperty(name, ctor, ScriptableObject.DONTENUM);
Scriptable errorProto = cx.newObject(global, "Error");
errorProto.put("name", errorProto, name);
ctor.put("prototype", ctor, errorProto);
if (sealed) {
ctor.sealObject();
if (errorProto instanceof ScriptableObject) {
((ScriptableObject)errorProto).sealObject();
}
}
}
}
/**
* The global method parseInt, as per ECMA-262 15.1.2.2.
*/