Commit new scheme for builtin objects, courtesy of

Igor Bukanov <igor@icesoft.no>. This new scheme is
faster and consumes less memory.
This commit is contained in:
nboyd%atg.com 2001-02-26 16:16:46 +00:00
Родитель 8bf5b5c07a
Коммит 8880f2750c
10 изменённых файлов: 1518 добавлений и 556 удалений

Просмотреть файл

@ -0,0 +1,201 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
* 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 Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Igor Bukanov
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (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 NPL, indicate your decision by
* deleting the provisions above and replace 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 NPL or the GPL.
*/
// API class
package org.mozilla.javascript;
public class IdFunction extends ScriptableObject implements Function
{
public static final int FUNCTION_ONLY = 0;
public static final int CONSTRUCTOR_ONLY = 1;
public static final int FUNCTION_AND_CONSTRUCTOR = 2;
public static interface Master {
/** 'thisObj' will be null if invoked as constructor, in which case
** instance of Scriptable should be returned */
public Object execMethod(int methodId, IdFunction function, Context cx, Scriptable scope, Scriptable thisObj, Object[] args)
throws JavaScriptException;
public int methodArity(int methodId, IdFunction function);
public Scriptable getParentScope();
}
public IdFunction(Master master, String name, int id) {
this.master = master;
this.methodName = name;
this.methodId = id;
}
/**
** Primary goal of idTag is to allow to use the same method id in class
** and its descendants.
*/
public final Object idTag() {
return idTag;
}
public void setIdTag(Object tag) {
idTag = tag;
}
public final int functionType() {
return functionType;
}
public void setFunctionType(int type) {
functionType = type;
}
public String getClassName() { return "NativeMethod"; }
public boolean has(String name, Scriptable start) {
return nameToId(name) != 0 || super.has(name, start);
}
public Object get(String name, Scriptable start) {
int id = nameToId(name);
return (0 != id) ? getField(id) : super.get(name, start);
}
public void put(String name, Scriptable start, Object value) {
if (nameToId(name) == 0) {
super.put(name, start, value);
}
}
public void delete(String name) {
if (nameToId(name) == 0) {
super.delete(name);
}
} /**
* Implements the instanceof operator for JavaScript Function objects.
* <p>
* <code>
* foo = new Foo();<br>
* foo instanceof Foo; // true<br>
* </code>
*
* @param instance The value that appeared on the LHS of the instanceof
* operator
* @return true if the "prototype" property of "this" appears in
* value's prototype chain
*
*/
public boolean hasInstance(Scriptable instance) {
Object protoProp = ScriptableObject.getProperty(this, "prototype");
if (protoProp instanceof Scriptable && protoProp != Undefined.instance) {
return ScriptRuntime.jsDelegatesTo(instance, (Scriptable)protoProp);
}
throw NativeGlobal.typeError1
("msg.instanceof.bad.prototype", this.methodName, instance);
}
public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args)
throws JavaScriptException
{
if (functionType != CONSTRUCTOR_ONLY) {
return master.execMethod(methodId, this, cx, scope, thisObj, args);
}
else {
return Undefined.instance;
}
}
public Scriptable construct(Context cx, Scriptable scope, Object[] args)
throws JavaScriptException
{
if (functionType != FUNCTION_ONLY) {
Object result
= master.execMethod(methodId, this, cx, scope, null, args);
// It is program error not to return Scriptable from constructor
return (Scriptable)result;
}
else {
return Undefined.instance;
}
}
public Scriptable getPrototype() {
// For native functions this does not called often so it is better
// to run this expensive operation here and not in constructor
return getFunctionPrototype(getParentScope());
}
public Scriptable getParentScope() {
Scriptable result = super.getParentScope();
if (result == null) {
result = master.getParentScope();
}
return result;
}
private Object getField(int fieldId) {
switch (fieldId) {
case ID_ARITY: case ID_LENGTH:
return new Integer(master.methodArity(methodId, this));
case ID_NAME:
return methodName;
}
return null;
}
private int nameToId(String s) {
int id = 0;
String guess = null;
switch (s.length()) {
case 4: guess = "name"; id = ID_NAME; break;
case 5: guess = "arity"; id = ID_ARITY; break;
case 6: guess = "length"; id = ID_LENGTH; break;
}
return (guess != null && guess.equals(s)) ? id : 0;
}
private static final int
ID_ARITY = 1,
ID_LENGTH = 2,
ID_NAME = 3;
protected final Master master;
protected final String methodName;
protected final int methodId;
protected Object idTag;
protected int functionType = FUNCTION_ONLY;
}

Просмотреть файл

@ -20,6 +20,7 @@
* *
* Contributor(s): * Contributor(s):
* Norris Boyd * Norris Boyd
* Igor Bukanov
* Mike McCabe * Mike McCabe
* *
* Alternatively, the contents of this file may be used under the * Alternatively, the contents of this file may be used under the
@ -49,34 +50,84 @@ import java.lang.reflect.Method;
* @author Mike Shaver * @author Mike Shaver
*/ */
public class NativeGlobal { public class NativeGlobal implements IdFunction.Master {
public static void init(Scriptable scope) public static void init(Scriptable scope)
throws PropertyException, throws PropertyException,
NotAFunctionException, NotAFunctionException,
JavaScriptException JavaScriptException
{ {
NativeGlobal instance = new NativeGlobal();
String names[] = { "eval", Context cx = Context.getContext();
"parseInt",
"parseFloat",
"escape",
"unescape",
"isNaN",
"isFinite",
"decodeURI",
"decodeURIComponent",
"encodeURI",
"encodeURIComponent"
};
// We can downcast here because Context.initStandardObjects // We can downcast here because Context.initStandardObjects
// takes a ScriptableObject scope. // takes a ScriptableObject scope.
ScriptableObject global = (ScriptableObject) scope; instance.initForGlobal(cx, (ScriptableObject) scope, false);
global.defineFunctionProperties(names, NativeGlobal.class, }
ScriptableObject.DONTENUM);
global.defineProperty("NaN", ScriptRuntime.NaNobj, public Object execMethod(int methodId, IdFunction function, Context cx,
Scriptable scope, Scriptable thisObj,
Object[] args)
throws JavaScriptException
{
switch (methodId) {
case Id_decodeURI: return js_decodeURI(cx, args);
case Id_decodeURIComponent: return js_decodeURIComponent(cx, args);
case Id_encodeURI: return js_encodeURI(cx, args);
case Id_encodeURIComponent: return js_encodeURIComponent(cx, args);
case Id_escape: return js_escape(cx, args);
case Id_eval: return js_eval(cx, scope, args);
case Id_isFinite: return js_isFinite(cx, args);
case Id_isNaN: return js_isNaN(cx, args);
case Id_parseFloat: return js_parseFloat(cx, args);
case Id_parseInt: return js_parseInt(cx, args);
case Id_unescape: return js_unescape(cx, args);
case Id_new_CommonError:
return new_CommonError(function, cx, scope, args);
}
return null;
}
public int methodArity(int methodId, IdFunction function) {
if (methodId == Id_parseInt) { return 2; }
return 1;
}
public Scriptable getParentScope() { return null; }
private String getMethodName(int methodId) {
switch (methodId) {
case Id_decodeURI: return "decodeURI";
case Id_decodeURIComponent: return "decodeURIComponent";
case Id_encodeURI: return "encodeURI";
case Id_encodeURIComponent: return "encodeURIComponent";
case Id_escape: return "escape";
case Id_eval: return "eval";
case Id_isFinite: return "isFinite";
case Id_isNaN: return "isNaN";
case Id_parseFloat: return "parseFloat";
case Id_parseInt: return "parseInt";
case Id_unescape: return "unescape";
}
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); ScriptableObject.DONTENUM);
global.defineProperty("Infinity", new Double(Double.POSITIVE_INFINITY), global.defineProperty("Infinity", new Double(Double.POSITIVE_INFINITY),
ScriptableObject.DONTENUM); ScriptableObject.DONTENUM);
@ -91,28 +142,36 @@ public class NativeGlobal {
"TypeError", "TypeError",
"URIError" "URIError"
}; };
Method[] m = FunctionObject.findMethods(NativeGlobal.class,
"CommonError");
Context cx = Context.getContext();
/* /*
Each error constructor gets its own Error object as a prototype, Each error constructor gets its own Error object as a prototype,
with the 'name' property set to the name of the error. with the 'name' property set to the name of the error.
*/ */
for (int i = 0; i < errorMethods.length; i++) { for (int i = 0; i < errorMethods.length; i++) {
String name = errorMethods[i]; String name = errorMethods[i];
FunctionObject ctor = new FunctionObject(name, m[0], global); IdFunction ctor = new IdFunction(this, name, Id_new_CommonError);
ctor.setFunctionType(IdFunction.FUNCTION_AND_CONSTRUCTOR);
global.defineProperty(name, ctor, ScriptableObject.DONTENUM); global.defineProperty(name, ctor, ScriptableObject.DONTENUM);
Scriptable errorProto = cx.newObject(scope, "Error");
Scriptable errorProto = cx.newObject(global, "Error");
errorProto.put("name", errorProto, name); errorProto.put("name", errorProto, name);
ctor.put("prototype", ctor, errorProto); 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. * The global method parseInt, as per ECMA-262 15.1.2.2.
*/ */
public static Object parseInt(String s, int radix) { private Object js_parseInt(Context cx, Object[] args) {
String s = ScriptRuntime.toString(args, 0);
int radix = ScriptRuntime.toInt32(args, 1);
int len = s.length(); int len = s.length();
if (len == 0) if (len == 0)
return ScriptRuntime.NaNobj; return ScriptRuntime.NaNobj;
@ -135,9 +194,7 @@ public class NativeGlobal {
radix = NO_RADIX; radix = NO_RADIX;
} else if (radix < 2 || radix > 36) { } else if (radix < 2 || radix > 36) {
return ScriptRuntime.NaNobj; return ScriptRuntime.NaNobj;
} else if (radix == 16 && len - start > 1 && } else if (radix == 16 && len - start > 1 && s.charAt(start) == '0') {
s.charAt(start) == '0')
{
c = s.charAt(start+1); c = s.charAt(start+1);
if (c == 'x' || c == 'X') if (c == 'x' || c == 'X')
start += 2; start += 2;
@ -169,9 +226,7 @@ public class NativeGlobal {
* @param args the arguments to parseFloat, ignoring args[>=1] * @param args the arguments to parseFloat, ignoring args[>=1]
* @param funObj unused * @param funObj unused
*/ */
public static Object parseFloat(Context cx, Scriptable thisObj, private Object js_parseFloat(Context cx, Object[] args) {
Object[] args, Function funObj)
{
if (args.length < 1) if (args.length < 1)
return ScriptRuntime.NaNobj; return ScriptRuntime.NaNobj;
String s = ScriptRuntime.toString(args[0]); String s = ScriptRuntime.toString(args[0]);
@ -252,21 +307,14 @@ public class NativeGlobal {
* method, which used to be part of the browser imbedding. Blame * method, which used to be part of the browser imbedding. Blame
* for the strange constant names should be directed there. * for the strange constant names should be directed there.
*/ */
private static int
URL_XALPHAS = 1,
URL_XPALPHAS = 2,
URL_PATH = 4;
public static Object escape(Context cx, Scriptable thisObj, private Object js_escape(Context cx, Object[] args) {
Object[] args, Function funObj) final int
{ URL_XALPHAS = 1,
char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', URL_XPALPHAS = 2,
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; URL_PATH = 4;
if (args.length < 1) String s = ScriptRuntime.toString(args, 0);
args = ScriptRuntime.padArguments(args, 1);
String s = ScriptRuntime.toString(args[0]);
int mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH; int mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH;
if (args.length > 1) { // the 'mask' argument. Non-ECMA. if (args.length > 1) { // the 'mask' argument. Non-ECMA.
@ -274,105 +322,99 @@ public class NativeGlobal {
if (d != d || ((mask = (int) d) != d) || if (d != d || ((mask = (int) d) != d) ||
0 != (mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH))) 0 != (mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH)))
{ {
String message = Context.getMessage String message = Context.getMessage0("msg.bad.esc.mask");
("msg.bad.esc.mask", null);
cx.reportError(message); cx.reportError(message);
// do the ecma thing, in case reportError returns. // do the ecma thing, in case reportError returns.
mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH; mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH;
} }
} }
StringBuffer R = new StringBuffer(); StringBuffer R = new StringBuffer();
for (int k = 0; k < s.length(); k++) { for (int k = 0; k < s.length(); k++) {
char c = s.charAt(k); int c = s.charAt(k), d;
if (mask != 0 && if (mask != 0 && ((c >= '0' && c <= '9') ||
((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') ||
(c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
(c >= 'a' && c <= 'z') || c == '@' || c == '*' || c == '_' ||
c == '@' || c == '*' || c == '_' ||
c == '-' || c == '.' || c == '-' || c == '.' ||
((c == '/' || c == '+') && mask > 3))) ((c == '/' || c == '+') && mask > 3)))
R.append(c); R.append((char)c);
else if (c < 256) { else if (c < 256) {
if (c == ' ' && mask == URL_XPALPHAS) { if (c == ' ' && mask == URL_XPALPHAS) {
R.append('+'); R.append('+');
} else { } else {
R.append('%'); R.append('%');
R.append(digits[c >> 4]); R.append(hex_digit_to_char(c >>> 4));
R.append(digits[c & 0xF]); R.append(hex_digit_to_char(c & 0xF));
} }
} else { } else {
R.append('%'); R.append('%');
R.append('u'); R.append('u');
R.append(digits[c >> 12]); R.append(hex_digit_to_char(c >>> 12));
R.append(digits[(c & 0xF00) >> 8]); R.append(hex_digit_to_char((c & 0xF00) >>> 8));
R.append(digits[(c & 0xF0) >> 4]); R.append(hex_digit_to_char((c & 0xF0) >>> 4));
R.append(digits[c & 0xF]); R.append(hex_digit_to_char(c & 0xF));
} }
} }
return R.toString(); return R.toString();
}
private static char hex_digit_to_char(int x) {
return (char)(x <= 9 ? x + '0' : x + ('A' - 10));
} }
/** /**
* The global unescape method, as per ECMA-262 15.1.2.5. * The global unescape method, as per ECMA-262 15.1.2.5.
*/ */
public static Object unescape(Context cx, Scriptable thisObj, private Object js_unescape(Context cx, Object[] args)
Object[] args, Function funObj)
{ {
if (args.length < 1) String s = ScriptRuntime.toString(args, 0);
args = ScriptRuntime.padArguments(args, 1); StringBuffer R = new StringBuffer();
stringIter: for (int k = 0; k < s.length(); k++) {
char c = s.charAt(k);
if (c != '%' || k == s.length() -1) {
R.append(c);
continue;
}
String hex;
int end, start;
if (s.charAt(k+1) == 'u') {
start = k+2;
end = k+6;
} else {
start = k+1;
end = k+3;
}
if (end > s.length()) {
R.append('%');
continue;
}
hex = s.substring(start, end);
for (int i = 0; i < hex.length(); i++)
if (!TokenStream.isXDigit(hex.charAt(i))) {
R.append('%');
continue stringIter;
}
k = end - 1;
R.append((new Character((char) Integer.valueOf(hex, 16).intValue())));
}
String s = ScriptRuntime.toString(args[0]); return R.toString();
StringBuffer R = new StringBuffer();
stringIter: for (int k = 0; k < s.length(); k++) {
char c = s.charAt(k);
if (c != '%' || k == s.length() -1) {
R.append(c);
continue;
}
String hex;
int end, start;
if (s.charAt(k+1) == 'u') {
start = k+2;
end = k+6;
} else {
start = k+1;
end = k+3;
}
if (end > s.length()) {
R.append('%');
continue;
}
hex = s.substring(start, end);
for (int i = 0; i < hex.length(); i++)
if (!TokenStream.isXDigit(hex.charAt(i))) {
R.append('%');
continue stringIter;
}
k = end - 1;
R.append((new Character((char) Integer.valueOf(hex, 16).intValue())));
}
return R.toString();
} }
/** /**
* The global method isNaN, as per ECMA-262 15.1.2.6. * The global method isNaN, as per ECMA-262 15.1.2.6.
*/ */
public static Object isNaN(Context cx, Scriptable thisObj, private Object js_isNaN(Context cx, Object[] args) {
Object[] args, Function funObj)
{
if (args.length < 1) if (args.length < 1)
return Boolean.TRUE; return Boolean.TRUE;
double d = ScriptRuntime.toNumber(args[0]); double d = ScriptRuntime.toNumber(args[0]);
return (d != d) ? Boolean.TRUE : Boolean.FALSE; return (d != d) ? Boolean.TRUE : Boolean.FALSE;
} }
public static Object isFinite(Context cx, Scriptable thisObj, private Object js_isFinite(Context cx, Object[] args) {
Object[] args, Function funObj)
{
if (args.length < 1) if (args.length < 1)
return Boolean.FALSE; return Boolean.FALSE;
double d = ScriptRuntime.toNumber(args[0]); double d = ScriptRuntime.toNumber(args[0]);
@ -382,12 +424,11 @@ public class NativeGlobal {
: Boolean.TRUE; : Boolean.TRUE;
} }
public static Object eval(Context cx, Scriptable thisObj, private Object js_eval(Context cx, Scriptable scope, Object[] args)
Object[] args, Function funObj)
throws JavaScriptException throws JavaScriptException
{ {
String m = ScriptRuntime.getMessage1("msg.cant.call.indirect", "eval"); String m = ScriptRuntime.getMessage1("msg.cant.call.indirect", "eval");
throw NativeGlobal.constructError(cx, "EvalError", m, funObj); throw NativeGlobal.constructError(cx, "EvalError", m, scope);
} }
/** /**
@ -520,12 +561,12 @@ public class NativeGlobal {
* The implementation of all the ECMA error constructors (SyntaxError, * The implementation of all the ECMA error constructors (SyntaxError,
* TypeError, etc.) * TypeError, etc.)
*/ */
public static Object CommonError(Context cx, Object[] args, private Object new_CommonError(IdFunction ctorObj, Context cx,
Function ctorObj, boolean inNewExpr) Scriptable scope, Object[] args)
{ {
Scriptable newInstance = new NativeError(); Scriptable newInstance = new NativeError();
newInstance.setPrototype((Scriptable)(ctorObj.get("prototype", ctorObj))); newInstance.setPrototype((Scriptable)(ctorObj.get("prototype", ctorObj)));
newInstance.setParentScope(cx.ctorScope); newInstance.setParentScope(scope);
if (args.length > 0) if (args.length > 0)
newInstance.put("message", newInstance, args[0]); newInstance.put("message", newInstance, args[0]);
return newInstance; return newInstance;
@ -578,7 +619,7 @@ public class NativeGlobal {
} }
k++; k++;
} }
return R.toString(); return R.toString();
} }
private static boolean isHex(char c) { private static boolean isHex(char c) {
@ -672,33 +713,25 @@ public class NativeGlobal {
private static String uriUnescaped = private static String uriUnescaped =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.!~*'()"; "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.!~*'()";
public static String decodeURI(Context cx, Scriptable thisObj, private String js_decodeURI(Context cx, Object[] args) {
Object[] args, Function funObj) String str = ScriptRuntime.toString(args, 0);
{
String str = ScriptRuntime.toString(args[0]);
return decode(cx, str, uriReservedPlusPound); return decode(cx, str, uriReservedPlusPound);
} }
public static String decodeURIComponent(Context cx, Scriptable thisObj, private String js_decodeURIComponent(Context cx, Object[] args) {
Object[] args, Function funObj) String str = ScriptRuntime.toString(args, 0);
{
String str = ScriptRuntime.toString(args[0]);
return decode(cx, str, ""); return decode(cx, str, "");
} }
public static Object encodeURI(Context cx, Scriptable thisObj, private Object js_encodeURI(Context cx, Object[] args) {
Object[] args, Function funObj) String str = ScriptRuntime.toString(args, 0);
{
String str = ScriptRuntime.toString(args[0]);
return encode(cx, str, uriReservedPlusPound + uriUnescaped); return encode(cx, str, uriReservedPlusPound + uriUnescaped);
} }
public static String encodeURIComponent(Context cx, Scriptable thisObj, private String js_encodeURIComponent(Context cx, Object[] args) {
Object[] args, Function funObj) String str = ScriptRuntime.toString(args, 0);
{
String str = ScriptRuntime.toString(args[0]);
return encode(cx, str, uriUnescaped); return encode(cx, str, uriUnescaped);
} }
/* Convert one UCS-4 char and write it into a UTF-8 buffer, which must be /* Convert one UCS-4 char and write it into a UTF-8 buffer, which must be
* at least 6 bytes long. Return the number of UTF-8 bytes of data written. * at least 6 bytes long. Return the number of UTF-8 bytes of data written.
@ -707,7 +740,7 @@ public class NativeGlobal {
int utf8Length = 1; int utf8Length = 1;
//JS_ASSERT(ucs4Char <= 0x7FFFFFFF); //JS_ASSERT(ucs4Char <= 0x7FFFFFFF);
if ((ucs4Char < 0x80) && (ucs4Char >= 0)) if ((ucs4Char & ~0x7F) == 0)
utf8Buffer[0] = (char)ucs4Char; utf8Buffer[0] = (char)ucs4Char;
else { else {
int i; int i;
@ -737,7 +770,7 @@ public class NativeGlobal {
//JS_ASSERT(utf8Length >= 1 && utf8Length <= 6); //JS_ASSERT(utf8Length >= 1 && utf8Length <= 6);
if (utf8Length == 1) { if (utf8Length == 1) {
ucs4Char = utf8Buffer[0]; ucs4Char = utf8Buffer[0];
// JS_ASSERT(!(ucs4Char & 0x80)); // JS_ASSERT(!(ucs4Char & 0x80));
} else { } else {
//JS_ASSERT((*utf8Buffer & (0x100 - (1 << (7-utf8Length)))) == (0x100 - (1 << (8-utf8Length)))); //JS_ASSERT((*utf8Buffer & (0x100 - (1 << (7-utf8Length)))) == (0x100 - (1 << (8-utf8Length))));
ucs4Char = utf8Buffer[k++] & ((1<<(7-utf8Length))-1); ucs4Char = utf8Buffer[k++] & ((1<<(7-utf8Length))-1);
@ -749,4 +782,20 @@ public class NativeGlobal {
return ucs4Char; return ucs4Char;
} }
private static final int
Id_decodeURI = 1,
Id_decodeURIComponent = 2,
Id_encodeURI = 3,
Id_encodeURIComponent = 4,
Id_escape = 5,
Id_eval = 6,
Id_isFinite = 7,
Id_isNaN = 8,
Id_parseFloat = 9,
Id_parseInt = 10,
Id_unescape = 11,
LAST_METHOD_ID = 11,
Id_new_CommonError = 12;
} }

Просмотреть файл

@ -1,4 +1,4 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- /* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-
* *
* The contents of this file are subject to the Netscape Public * The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file * License Version 1.1 (the "License"); you may not use this file
@ -19,6 +19,8 @@
* Rights Reserved. * Rights Reserved.
* *
* Contributor(s): * Contributor(s):
* Norris Boyd
* Igor Bukanov
* *
* Alternatively, the contents of this file may be used under the * Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the * terms of the GNU Public License (the "GPL"), in which case the
@ -39,119 +41,241 @@ package org.mozilla.javascript;
* See ECMA 15.8. * See ECMA 15.8.
* @author Norris Boyd * @author Norris Boyd
*/ */
public class NativeMath extends ScriptableObject {
public static Scriptable init(Scriptable scope) public class NativeMath extends ScriptableObject
throws PropertyException implements IdFunction.Master
{ {
public static Scriptable init(Scriptable scope) {
NativeMath m = new NativeMath(); NativeMath m = new NativeMath();
m.setPrototype(getObjectPrototype(scope)); Context cx = Context.getContext();
m.setParentScope(scope);
String[] names = { "atan", "atan2", "ceil",
"cos", "floor", "random",
"sin", "sqrt", "tan" };
m.defineFunctionProperties(names, java.lang.Math.class,
ScriptableObject.DONTENUM);
// These functions exist in java.lang.Math, but
// are overloaded. Define our own wrappers.
String[] localNames = { "acos", "asin", "abs", "exp", "max", "min",
"round", "pow", "log" };
m.defineFunctionProperties(localNames, NativeMath.class,
ScriptableObject.DONTENUM);
/*
have to fix up the length property for max & min
which are varargs form, but need to have a length of 2
*/
((FunctionObject)m.get("max", scope)).setLength((short)2);
((FunctionObject)m.get("min", scope)).setLength((short)2);
final int attr = ScriptableObject.DONTENUM |
ScriptableObject.PERMANENT |
ScriptableObject.READONLY;
m.defineProperty("E", new Double(Math.E), attr);
m.defineProperty("PI", new Double(Math.PI), attr);
m.defineProperty("LN10", new Double(2.302585092994046), attr);
m.defineProperty("LN2", new Double(0.6931471805599453), attr);
m.defineProperty("LOG2E", new Double(1.4426950408889634), attr);
m.defineProperty("LOG10E", new Double(0.4342944819032518), attr);
m.defineProperty("SQRT1_2", new Double(0.7071067811865476), attr);
m.defineProperty("SQRT2", new Double(1.4142135623730951), attr);
// We know that scope is a Scriptable object since we // We know that scope is a Scriptable object since we
// constrained the type on initStandardObjects. // constrained the type on initStandardObjects.
ScriptableObject global = (ScriptableObject) scope; m.initForGlobal(cx, (ScriptableObject)scope, false);
global.defineProperty("Math", m, ScriptableObject.DONTENUM);
return m; return m;
} }
public NativeMath() { }
public NativeMath() { public void initForGlobal(Context cx, ScriptableObject global,
} boolean sealed)
public String getClassName() {
return "Math";
}
public static double abs(double d) {
if (d == 0.0)
return 0.0; // abs(-0.0) should be 0.0, but -0.0 < 0.0 == false
else if (d < 0.0)
return -d;
else
return d;
}
public static double acos(double d) {
if ((d != d)
|| (d > 1.0)
|| (d < -1.0))
return Double.NaN;
return Math.acos(d);
}
public static double asin(double d) {
if ((d != d)
|| (d > 1.0)
|| (d < -1.0))
return Double.NaN;
return Math.asin(d);
}
public static double max(Context cx, Scriptable thisObj,
Object[] args, Function funObj)
{
double result = Double.NEGATIVE_INFINITY;
if (args.length == 0)
return result;
for (int i = 0; i < args.length; i++) {
double d = ScriptRuntime.toNumber(args[i]);
if (d != d) return d;
result = Math.max(result, d);
}
return result;
}
public static double min(Context cx, Scriptable thisObj,
Object[] args, Function funObj)
{ {
double result = Double.POSITIVE_INFINITY; setPrototype(getObjectPrototype(global));
if (args.length == 0) setParentScope(global);
return result; if (sealed) {
for (int i = 0; i < args.length; i++) { sealObject();
double d = ScriptRuntime.toNumber(args[i]); }
if (d != d) return d; global.defineProperty("Math", this, ScriptableObject.DONTENUM);
result = Math.min(result, d);
}
return result;
} }
public static double round(double d) { public String getClassName() { return "Math"; }
public boolean has(String name, Scriptable start) {
int id = nameToId(name);
if (0 != id && !wasOverwritten(id)) { return true; }
return super.has(name, start);
}
public Object get(String name, Scriptable start) {
// ALERT: cache the last used value like ScriptableObject does.
// But what about thread safety then?
int id = nameToId(name);
if (0 != id && !wasOverwritten(id)) {
return (id > LAST_METHOD_ID)
? getField(id) : wrapMethod(name, id);
}
return super.get(name, start);
}
public void put(String name, Scriptable start, Object value) {
if (markAsModified(name)) {
super.put(name, start, value);
}
}
public void delete(String name) {
if (markAsModified(name)) {
super.delete(name);
}
}
private boolean wasOverwritten(int id) {
return 0 != (overwritten_flags & (1 << id));
}
// Return if field or method was marked as overwritten
private boolean markAsModified(String name) {
int id = nameToId(name);
if (0 != id && !wasOverwritten(id)) {
if (isSealed() || id > LAST_METHOD_ID) {
// Read only Math constant, ignore modifications
return false;
}
overwritten_flags |= (1 << id);
function_cache[id - 1] = null;
}
return true;
}
private Object wrapMethod(String name, int id) {
IdFunction f = function_cache[id - 1];
if (f == null) {
synchronized (this) {
if (function_cache[id - 1] == null) {
function_cache[id - 1] = new IdFunction(this, name, id);
f = function_cache[id - 1];
}
}
}
return f;
}
private Object getField(int fieldId) {
switch (fieldId) {
case Id_E: return E;
case Id_PI: return PI;
case Id_LN10: return LN10;
case Id_LN2: return LN2;
case Id_LOG2E: return LOG2E;
case Id_LOG10E: return LOG10E;
case Id_SQRT1_2: return SQRT1_2;
case Id_SQRT2: return SQRT2;
}
return null;
}
public int methodArity(int methodId, IdFunction function) {
switch (methodId) {
case Id_atan2:
case Id_max:
case Id_min:
case Id_pow:
return 2;
case Id_random:
return 0;
}
return 1;
}
public Object execMethod
(int methodId, IdFunction function,
Context cx, Scriptable scope, Scriptable thisObj, Object[] args)
throws JavaScriptException
{
switch (methodId) {
case Id_abs: return wrap_dbl(js_abs(to_dbl(args, 0)));
case Id_acos: return wrap_dbl(js_acos(to_dbl(args, 0)));
case Id_asin: return wrap_dbl(js_asin(to_dbl(args, 0)));
case Id_atan: return wrap_dbl(js_atan(to_dbl(args, 0)));
case Id_atan2: return wrap_dbl
(js_atan2(to_dbl(args, 0), to_dbl(args, 1)));
case Id_ceil: return wrap_dbl(js_ceil(to_dbl(args, 0)));
case Id_cos: return wrap_dbl(js_cos(to_dbl(args, 0)));
case Id_exp: return wrap_dbl(js_exp(to_dbl(args, 0)));
case Id_floor: return wrap_dbl(js_floor(to_dbl(args, 0)));
case Id_log: return wrap_dbl(js_log(to_dbl(args, 0)));
case Id_max: return wrap_dbl(js_max(args));
case Id_min: return wrap_dbl(js_min(args));
case Id_pow: return wrap_dbl
(js_pow(to_dbl(args, 0), to_dbl(args, 1)));
case Id_random: return wrap_dbl(js_random());
case Id_round: return wrap_dbl(js_round(to_dbl(args, 0)));
case Id_sin: return wrap_dbl(js_sin(to_dbl(args, 0)));
case Id_sqrt: return wrap_dbl(js_sqrt(to_dbl(args, 0)));
case Id_tan: return wrap_dbl(js_tan(to_dbl(args, 0)));
}
return null;
}
private double to_dbl(Object[] args, int index) {
return ScriptRuntime.toNumber(args, index);
}
private double to_dbl(Object arg) {
return ScriptRuntime.toNumber(arg);
}
private Double wrap_dbl(double x) {
return (x == x) ? new Double(x) : ScriptRuntime.NaNobj;
}
private double js_abs(double x) {
// abs(-0.0) should be 0.0, but -0.0 < 0.0 == false
return (x == 0.0) ? 0.0 : (x < 0.0) ? -x : x;
}
private double js_acos(double x) {
return (x == x && -1.0 <= x && x <= 1.0) ? Math.acos(x) : Double.NaN;
}
private double js_asin(double x) {
return (x == x && -1.0 <= x && x <= 1.0) ? Math.asin(x) : Double.NaN;
}
private double js_atan(double x) { return Math.atan(x); }
private double js_atan2(double x, double y) { return Math.atan2(x, y); }
private double js_ceil(double x) { return Math.ceil(x); }
private double js_cos(double x) { return Math.cos(x); }
private double js_exp(double x) {
return (x == Double.POSITIVE_INFINITY) ? x
: (x == Double.NEGATIVE_INFINITY) ? 0.0
: Math.exp(x);
}
private double js_floor(double x) { return Math.floor(x); }
private double js_log(double x) {
// Java's log(<0) = -Infinity; we need NaN
return (x < 0) ? Double.NaN : Math.log(x);
}
private double js_max(Object[] args) {
double result = Double.NEGATIVE_INFINITY;
if (args.length == 0)
return result;
for (int i = 0; i < args.length; i++) {
double d = ScriptRuntime.toNumber(args[i]);
if (d != d) return d;
result = Math.max(result, d);
}
return result;
}
private double js_min(Object[] args) {
double result = Double.POSITIVE_INFINITY;
if (args.length == 0)
return result;
for (int i = 0; i < args.length; i++) {
double d = ScriptRuntime.toNumber(args[i]);
if (d != d) return d;
result = Math.min(result, d);
}
return result;
}
private double js_pow(double x, double y) {
if (y == 0) return 1.0; // Java's pow(NaN, 0) = NaN; we need 1
if ((x == 0) && (y < 0)) {
if (1 / x > 0) {
// x is +0, Java is -oo, we need +oo
return Double.POSITIVE_INFINITY;
}
/* if x is -0 and y is an odd integer, -oo */
int y_int = (int)y;
if (y_int == y && (y_int & 0x1) != 0)
return Double.NEGATIVE_INFINITY;
return Double.POSITIVE_INFINITY;
}
return Math.pow(x, y);
}
private double js_random() { return Math.random(); }
private double js_round(double d) {
if (d != d) if (d != d)
return d; // NaN return d; // NaN
if (d == Double.POSITIVE_INFINITY || d == Double.NEGATIVE_INFINITY) if (d == Double.POSITIVE_INFINITY || d == Double.NEGATIVE_INFINITY)
@ -164,35 +288,120 @@ public class NativeMath extends ScriptableObject {
return d == 0.0 ? d : 0.0; return d == 0.0 ? d : 0.0;
} }
return (double) l; return (double) l;
} }
public static double pow(double x, double y) { private double js_sin(double x) { return Math.sin(x); }
if (y == 0)
return 1.0; // Java's pow(NaN, 0) = NaN; we need 1 private double js_sqrt(double x) { return Math.sqrt(x); }
if ((x == 0) && (y < 0)) {
Double d = new Double(x); private double js_tan(double x) { return Math.tan(x); }
if (d.equals(new Double(0))) // x is +0
return Double.POSITIVE_INFINITY; // Java is -Infinity private static int nameToId(String s) {
/* if x is -0 and y is an odd integer, -Infinity */ int c;
if (((int)y == y) && (((int)y & 0x1) == 1)) int id = 0;
return Double.NEGATIVE_INFINITY; String guess = null;
return Double.POSITIVE_INFINITY; L:switch (s.length()) {
case 1: if (s.charAt(0)=='E') return Id_E;
break L;
case 2: if (s.charAt(0)=='P'&&s.charAt(1)=='I') return Id_PI;
break L;
case 3: switch (s.charAt(0)) {
case 'L': if (s.charAt(1)=='N'&&s.charAt(2)=='2') return Id_LN2;
break L;
case 'a': if (s.charAt(1)=='b'&&s.charAt(2)=='s') return Id_abs;
break L;
case 'c': if (s.charAt(1)=='o'&&s.charAt(2)=='s') return Id_cos;
break L;
case 'e': if (s.charAt(1)=='x'&&s.charAt(2)=='p') return Id_exp;
break L;
case 'l': if (s.charAt(1)=='o'&&s.charAt(2)=='g') return Id_log;
break L;
case 'm': c=s.charAt(1);
if (c=='a') { if (s.charAt(2)=='x') return Id_max; }
else if (c=='i') { if (s.charAt(2)=='n') return Id_min; }
break L;
case 'p': if (s.charAt(1)=='o'&&s.charAt(2)=='w') return Id_pow;
break L;
case 's': if (s.charAt(1)=='i'&&s.charAt(2)=='n') return Id_sin;
break L;
case 't': if (s.charAt(1)=='a'&&s.charAt(2)=='n') return Id_tan;
break L;
}
break L;
case 4: switch (s.charAt(1)) {
case 'N': guess="LN10";id=Id_LN10; break L;
case 'c': guess="acos";id=Id_acos; break L;
case 's': guess="asin";id=Id_asin; break L;
case 't': guess="atan";id=Id_atan; break L;
case 'e': guess="ceil";id=Id_ceil; break L;
case 'q': guess="sqrt";id=Id_sqrt; break L;
}
break L;
case 5: switch (s.charAt(0)) {
case 'S': guess="SQRT2";id=Id_SQRT2; break L;
case 'L': guess="LOG2E";id=Id_LOG2E; break L;
case 'a': guess="atan2";id=Id_atan2; break L;
case 'f': guess="floor";id=Id_floor; break L;
case 'r': guess="round";id=Id_round; break L;
}
break L;
case 6: c=s.charAt(0);
if (c=='L') { guess="LOG10E";id=Id_LOG10E; }
else if (c=='r') { guess="random";id=Id_random; }
break L;
case 7: guess="SQRT1_2";id=Id_SQRT1_2;
break L;
} }
return Math.pow(x, y);
}
public static double exp(double d) {
if (d == Double.POSITIVE_INFINITY)
return d;
if (d == Double.NEGATIVE_INFINITY)
return 0.0;
return Math.exp(d);
}
public static double log(double x) { return (guess != null && s.equals(guess)) ? id : 0;
if (x < 0)
return Double.NaN; // Java's log(<0) = -Infinity; we need NaN
return Math.log(x);
} }
private static final int
Id_abs = 1,
Id_acos = 2,
Id_asin = 3,
Id_atan = 4,
Id_atan2 = 5,
Id_ceil = 6,
Id_cos = 7,
Id_exp = 8,
Id_floor = 9,
Id_log = 10,
Id_max = 11,
Id_min = 12,
Id_pow = 13,
Id_random = 14,
Id_round = 15,
Id_sin = 16,
Id_sqrt = 17,
Id_tan = 18,
LAST_METHOD_ID = 18,
Id_E = 19,
Id_PI = 20,
Id_LN10 = 21,
Id_LN2 = 22,
Id_LOG2E = 23,
Id_LOG10E = 24,
Id_SQRT1_2 = 25,
Id_SQRT2 = 26;
private static final Double
E = new Double(Math.E),
PI = new Double(Math.PI),
LN10 = new Double(2.302585092994046),
LN2 = new Double(0.6931471805599453),
LOG2E = new Double(1.4426950408889634),
LOG10E = new Double(0.4342944819032518),
SQRT1_2 = new Double(0.7071067811865476),
SQRT2 = new Double(1.4142135623730951);
// Indicates that field or method was overwritten
private int overwritten_flags;
// Cache of constructed wrappers for methods
private IdFunction[] function_cache = new IdFunction[LAST_METHOD_ID];
} }

Просмотреть файл

@ -120,14 +120,14 @@ public class ScriptRuntime {
* See ECMA 9.3. * See ECMA 9.3.
*/ */
public static double toNumber(Object val) { public static double toNumber(Object val) {
if (val != null && val instanceof Scriptable) { if (val == null)
return +0.0;
if (val instanceof Scriptable) {
val = ((Scriptable) val).getDefaultValue(NumberClass); val = ((Scriptable) val).getDefaultValue(NumberClass);
if (val != null && val instanceof Scriptable) if (val != null && val instanceof Scriptable)
throw errorWithClassName("msg.primitive.expected", val); throw errorWithClassName("msg.primitive.expected", val);
// fall through // fall through
} }
if (val == null)
return +0.0;
if (val instanceof String) if (val instanceof String)
return toNumber((String) val); return toNumber((String) val);
if (val instanceof Number) if (val instanceof Number)
@ -137,6 +137,11 @@ public class ScriptRuntime {
throw errorWithClassName("msg.invalid.type", val); throw errorWithClassName("msg.invalid.type", val);
} }
public static double toNumber(Object[] args, int index) {
return (0 <= index && index < args.length)
? toNumber(args[index]) : NaN;
}
// This definition of NaN is identical to that in java.lang.Double // This definition of NaN is identical to that in java.lang.Double
// except that it is not final. This is a workaround for a bug in // except that it is not final. This is a workaround for a bug in
// the Microsoft VM, versions 2.01 and 3.0P1, that causes some uses // the Microsoft VM, versions 2.01 and 3.0P1, that causes some uses
@ -421,6 +426,11 @@ public class ScriptRuntime {
} }
} }
public static String toString(Object[] args, int index) {
return (0 <= index && index < args.length)
? toString(args[index]) : "undefined";
}
public static String numberToString(double d, int base) { public static String numberToString(double d, int base) {
if (d != d) if (d != d)
return "NaN"; return "NaN";
@ -577,6 +587,11 @@ public class ScriptRuntime {
return (int)d; return (int)d;
} }
public static int toInt32(Object[] args, int index) {
return (0 <= index && index < args.length)
? toInt32(args[index]) : 0;
}
public static int toInt32(double d) { public static int toInt32(double d) {
// 0x100000000 gives me a numeric overflow... // 0x100000000 gives me a numeric overflow...
double two32 = 4294967296.0; double two32 = 4294967296.0;
@ -1205,14 +1220,20 @@ public class ScriptRuntime {
String filename, int lineNumber) String filename, int lineNumber)
throws JavaScriptException throws JavaScriptException
{ {
if (fun instanceof FunctionObject) { if (fun instanceof IdFunction) {
IdFunction f = (IdFunction)fun;
String name = f.methodName;
Class cl = f.master.getClass();
if (name.equals("eval") && cl == NativeGlobal.class) {
return NativeGlobal.evalSpecial(cx, scope, thisArg, args,
filename, lineNumber);
}
}
else if (fun instanceof FunctionObject) {
FunctionObject fo = (FunctionObject) fun; FunctionObject fo = (FunctionObject) fun;
Member m = fo.method; Member m = fo.method;
Class cl = m.getDeclaringClass(); Class cl = m.getDeclaringClass();
String name = m.getName(); String name = m.getName();
if (name.equals("eval") && cl == NativeGlobal.class)
return NativeGlobal.evalSpecial(cx, scope, thisArg, args,
filename, lineNumber);
if (name.equals("With") && cl == NativeWith.class) if (name.equals("With") && cl == NativeWith.class)
return NativeWith.newWithSpecial(cx, args, fo, !isCall); return NativeWith.newWithSpecial(cx, args, fo, !isCall);
if (name.equals("jsFunction_exec") && cl == NativeScript.class) if (name.equals("jsFunction_exec") && cl == NativeScript.class)

Просмотреть файл

@ -794,6 +794,7 @@ public abstract class ScriptableObject implements Scriptable {
final String setterPrefix = "jsSet_"; final String setterPrefix = "jsSet_";
final String ctorName = "jsConstructor"; final String ctorName = "jsConstructor";
boolean hasPrefix = false;
Method[] ctorMeths = FunctionObject.findMethods(clazz, ctorName); Method[] ctorMeths = FunctionObject.findMethods(clazz, ctorName);
Member ctorMember = null; Member ctorMember = null;
if (ctorMeths != null) { if (ctorMeths != null) {
@ -803,11 +804,11 @@ public abstract class ScriptableObject implements Scriptable {
ctorMeths[0], ctorMeths[1])); ctorMeths[0], ctorMeths[1]));
} }
ctorMember = ctorMeths[0]; ctorMember = ctorMeths[0];
hasPrefix = true;
} }
// Deprecated: look for functions with the same name as the class // Deprecated: look for functions with the same name as the class
// and consider them constructors. // and consider them constructors.
boolean hasPrefix = false;
for (int i=0; i < methods.length; i++) { for (int i=0; i < methods.length; i++) {
String name = methods[i].getName(); String name = methods[i].getName();
String prefix = null; String prefix = null;

Просмотреть файл

@ -0,0 +1,201 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
* 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 Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Igor Bukanov
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (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 NPL, indicate your decision by
* deleting the provisions above and replace 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 NPL or the GPL.
*/
// API class
package org.mozilla.javascript;
public class IdFunction extends ScriptableObject implements Function
{
public static final int FUNCTION_ONLY = 0;
public static final int CONSTRUCTOR_ONLY = 1;
public static final int FUNCTION_AND_CONSTRUCTOR = 2;
public static interface Master {
/** 'thisObj' will be null if invoked as constructor, in which case
** instance of Scriptable should be returned */
public Object execMethod(int methodId, IdFunction function, Context cx, Scriptable scope, Scriptable thisObj, Object[] args)
throws JavaScriptException;
public int methodArity(int methodId, IdFunction function);
public Scriptable getParentScope();
}
public IdFunction(Master master, String name, int id) {
this.master = master;
this.methodName = name;
this.methodId = id;
}
/**
** Primary goal of idTag is to allow to use the same method id in class
** and its descendants.
*/
public final Object idTag() {
return idTag;
}
public void setIdTag(Object tag) {
idTag = tag;
}
public final int functionType() {
return functionType;
}
public void setFunctionType(int type) {
functionType = type;
}
public String getClassName() { return "NativeMethod"; }
public boolean has(String name, Scriptable start) {
return nameToId(name) != 0 || super.has(name, start);
}
public Object get(String name, Scriptable start) {
int id = nameToId(name);
return (0 != id) ? getField(id) : super.get(name, start);
}
public void put(String name, Scriptable start, Object value) {
if (nameToId(name) == 0) {
super.put(name, start, value);
}
}
public void delete(String name) {
if (nameToId(name) == 0) {
super.delete(name);
}
} /**
* Implements the instanceof operator for JavaScript Function objects.
* <p>
* <code>
* foo = new Foo();<br>
* foo instanceof Foo; // true<br>
* </code>
*
* @param instance The value that appeared on the LHS of the instanceof
* operator
* @return true if the "prototype" property of "this" appears in
* value's prototype chain
*
*/
public boolean hasInstance(Scriptable instance) {
Object protoProp = ScriptableObject.getProperty(this, "prototype");
if (protoProp instanceof Scriptable && protoProp != Undefined.instance) {
return ScriptRuntime.jsDelegatesTo(instance, (Scriptable)protoProp);
}
throw NativeGlobal.typeError1
("msg.instanceof.bad.prototype", this.methodName, instance);
}
public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args)
throws JavaScriptException
{
if (functionType != CONSTRUCTOR_ONLY) {
return master.execMethod(methodId, this, cx, scope, thisObj, args);
}
else {
return Undefined.instance;
}
}
public Scriptable construct(Context cx, Scriptable scope, Object[] args)
throws JavaScriptException
{
if (functionType != FUNCTION_ONLY) {
Object result
= master.execMethod(methodId, this, cx, scope, null, args);
// It is program error not to return Scriptable from constructor
return (Scriptable)result;
}
else {
return Undefined.instance;
}
}
public Scriptable getPrototype() {
// For native functions this does not called often so it is better
// to run this expensive operation here and not in constructor
return getFunctionPrototype(getParentScope());
}
public Scriptable getParentScope() {
Scriptable result = super.getParentScope();
if (result == null) {
result = master.getParentScope();
}
return result;
}
private Object getField(int fieldId) {
switch (fieldId) {
case ID_ARITY: case ID_LENGTH:
return new Integer(master.methodArity(methodId, this));
case ID_NAME:
return methodName;
}
return null;
}
private int nameToId(String s) {
int id = 0;
String guess = null;
switch (s.length()) {
case 4: guess = "name"; id = ID_NAME; break;
case 5: guess = "arity"; id = ID_ARITY; break;
case 6: guess = "length"; id = ID_LENGTH; break;
}
return (guess != null && guess.equals(s)) ? id : 0;
}
private static final int
ID_ARITY = 1,
ID_LENGTH = 2,
ID_NAME = 3;
protected final Master master;
protected final String methodName;
protected final int methodId;
protected Object idTag;
protected int functionType = FUNCTION_ONLY;
}

Просмотреть файл

@ -20,6 +20,7 @@
* *
* Contributor(s): * Contributor(s):
* Norris Boyd * Norris Boyd
* Igor Bukanov
* Mike McCabe * Mike McCabe
* *
* Alternatively, the contents of this file may be used under the * Alternatively, the contents of this file may be used under the
@ -49,34 +50,84 @@ import java.lang.reflect.Method;
* @author Mike Shaver * @author Mike Shaver
*/ */
public class NativeGlobal { public class NativeGlobal implements IdFunction.Master {
public static void init(Scriptable scope) public static void init(Scriptable scope)
throws PropertyException, throws PropertyException,
NotAFunctionException, NotAFunctionException,
JavaScriptException JavaScriptException
{ {
NativeGlobal instance = new NativeGlobal();
String names[] = { "eval", Context cx = Context.getContext();
"parseInt",
"parseFloat",
"escape",
"unescape",
"isNaN",
"isFinite",
"decodeURI",
"decodeURIComponent",
"encodeURI",
"encodeURIComponent"
};
// We can downcast here because Context.initStandardObjects // We can downcast here because Context.initStandardObjects
// takes a ScriptableObject scope. // takes a ScriptableObject scope.
ScriptableObject global = (ScriptableObject) scope; instance.initForGlobal(cx, (ScriptableObject) scope, false);
global.defineFunctionProperties(names, NativeGlobal.class, }
ScriptableObject.DONTENUM);
global.defineProperty("NaN", ScriptRuntime.NaNobj, public Object execMethod(int methodId, IdFunction function, Context cx,
Scriptable scope, Scriptable thisObj,
Object[] args)
throws JavaScriptException
{
switch (methodId) {
case Id_decodeURI: return js_decodeURI(cx, args);
case Id_decodeURIComponent: return js_decodeURIComponent(cx, args);
case Id_encodeURI: return js_encodeURI(cx, args);
case Id_encodeURIComponent: return js_encodeURIComponent(cx, args);
case Id_escape: return js_escape(cx, args);
case Id_eval: return js_eval(cx, scope, args);
case Id_isFinite: return js_isFinite(cx, args);
case Id_isNaN: return js_isNaN(cx, args);
case Id_parseFloat: return js_parseFloat(cx, args);
case Id_parseInt: return js_parseInt(cx, args);
case Id_unescape: return js_unescape(cx, args);
case Id_new_CommonError:
return new_CommonError(function, cx, scope, args);
}
return null;
}
public int methodArity(int methodId, IdFunction function) {
if (methodId == Id_parseInt) { return 2; }
return 1;
}
public Scriptable getParentScope() { return null; }
private String getMethodName(int methodId) {
switch (methodId) {
case Id_decodeURI: return "decodeURI";
case Id_decodeURIComponent: return "decodeURIComponent";
case Id_encodeURI: return "encodeURI";
case Id_encodeURIComponent: return "encodeURIComponent";
case Id_escape: return "escape";
case Id_eval: return "eval";
case Id_isFinite: return "isFinite";
case Id_isNaN: return "isNaN";
case Id_parseFloat: return "parseFloat";
case Id_parseInt: return "parseInt";
case Id_unescape: return "unescape";
}
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); ScriptableObject.DONTENUM);
global.defineProperty("Infinity", new Double(Double.POSITIVE_INFINITY), global.defineProperty("Infinity", new Double(Double.POSITIVE_INFINITY),
ScriptableObject.DONTENUM); ScriptableObject.DONTENUM);
@ -91,28 +142,36 @@ public class NativeGlobal {
"TypeError", "TypeError",
"URIError" "URIError"
}; };
Method[] m = FunctionObject.findMethods(NativeGlobal.class,
"CommonError");
Context cx = Context.getContext();
/* /*
Each error constructor gets its own Error object as a prototype, Each error constructor gets its own Error object as a prototype,
with the 'name' property set to the name of the error. with the 'name' property set to the name of the error.
*/ */
for (int i = 0; i < errorMethods.length; i++) { for (int i = 0; i < errorMethods.length; i++) {
String name = errorMethods[i]; String name = errorMethods[i];
FunctionObject ctor = new FunctionObject(name, m[0], global); IdFunction ctor = new IdFunction(this, name, Id_new_CommonError);
ctor.setFunctionType(IdFunction.FUNCTION_AND_CONSTRUCTOR);
global.defineProperty(name, ctor, ScriptableObject.DONTENUM); global.defineProperty(name, ctor, ScriptableObject.DONTENUM);
Scriptable errorProto = cx.newObject(scope, "Error");
Scriptable errorProto = cx.newObject(global, "Error");
errorProto.put("name", errorProto, name); errorProto.put("name", errorProto, name);
ctor.put("prototype", ctor, errorProto); 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. * The global method parseInt, as per ECMA-262 15.1.2.2.
*/ */
public static Object parseInt(String s, int radix) { private Object js_parseInt(Context cx, Object[] args) {
String s = ScriptRuntime.toString(args, 0);
int radix = ScriptRuntime.toInt32(args, 1);
int len = s.length(); int len = s.length();
if (len == 0) if (len == 0)
return ScriptRuntime.NaNobj; return ScriptRuntime.NaNobj;
@ -135,9 +194,7 @@ public class NativeGlobal {
radix = NO_RADIX; radix = NO_RADIX;
} else if (radix < 2 || radix > 36) { } else if (radix < 2 || radix > 36) {
return ScriptRuntime.NaNobj; return ScriptRuntime.NaNobj;
} else if (radix == 16 && len - start > 1 && } else if (radix == 16 && len - start > 1 && s.charAt(start) == '0') {
s.charAt(start) == '0')
{
c = s.charAt(start+1); c = s.charAt(start+1);
if (c == 'x' || c == 'X') if (c == 'x' || c == 'X')
start += 2; start += 2;
@ -169,9 +226,7 @@ public class NativeGlobal {
* @param args the arguments to parseFloat, ignoring args[>=1] * @param args the arguments to parseFloat, ignoring args[>=1]
* @param funObj unused * @param funObj unused
*/ */
public static Object parseFloat(Context cx, Scriptable thisObj, private Object js_parseFloat(Context cx, Object[] args) {
Object[] args, Function funObj)
{
if (args.length < 1) if (args.length < 1)
return ScriptRuntime.NaNobj; return ScriptRuntime.NaNobj;
String s = ScriptRuntime.toString(args[0]); String s = ScriptRuntime.toString(args[0]);
@ -252,21 +307,14 @@ public class NativeGlobal {
* method, which used to be part of the browser imbedding. Blame * method, which used to be part of the browser imbedding. Blame
* for the strange constant names should be directed there. * for the strange constant names should be directed there.
*/ */
private static int
URL_XALPHAS = 1,
URL_XPALPHAS = 2,
URL_PATH = 4;
public static Object escape(Context cx, Scriptable thisObj, private Object js_escape(Context cx, Object[] args) {
Object[] args, Function funObj) final int
{ URL_XALPHAS = 1,
char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', URL_XPALPHAS = 2,
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; URL_PATH = 4;
if (args.length < 1) String s = ScriptRuntime.toString(args, 0);
args = ScriptRuntime.padArguments(args, 1);
String s = ScriptRuntime.toString(args[0]);
int mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH; int mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH;
if (args.length > 1) { // the 'mask' argument. Non-ECMA. if (args.length > 1) { // the 'mask' argument. Non-ECMA.
@ -274,105 +322,99 @@ public class NativeGlobal {
if (d != d || ((mask = (int) d) != d) || if (d != d || ((mask = (int) d) != d) ||
0 != (mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH))) 0 != (mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH)))
{ {
String message = Context.getMessage String message = Context.getMessage0("msg.bad.esc.mask");
("msg.bad.esc.mask", null);
cx.reportError(message); cx.reportError(message);
// do the ecma thing, in case reportError returns. // do the ecma thing, in case reportError returns.
mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH; mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH;
} }
} }
StringBuffer R = new StringBuffer(); StringBuffer R = new StringBuffer();
for (int k = 0; k < s.length(); k++) { for (int k = 0; k < s.length(); k++) {
char c = s.charAt(k); int c = s.charAt(k), d;
if (mask != 0 && if (mask != 0 && ((c >= '0' && c <= '9') ||
((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') ||
(c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
(c >= 'a' && c <= 'z') || c == '@' || c == '*' || c == '_' ||
c == '@' || c == '*' || c == '_' ||
c == '-' || c == '.' || c == '-' || c == '.' ||
((c == '/' || c == '+') && mask > 3))) ((c == '/' || c == '+') && mask > 3)))
R.append(c); R.append((char)c);
else if (c < 256) { else if (c < 256) {
if (c == ' ' && mask == URL_XPALPHAS) { if (c == ' ' && mask == URL_XPALPHAS) {
R.append('+'); R.append('+');
} else { } else {
R.append('%'); R.append('%');
R.append(digits[c >> 4]); R.append(hex_digit_to_char(c >>> 4));
R.append(digits[c & 0xF]); R.append(hex_digit_to_char(c & 0xF));
} }
} else { } else {
R.append('%'); R.append('%');
R.append('u'); R.append('u');
R.append(digits[c >> 12]); R.append(hex_digit_to_char(c >>> 12));
R.append(digits[(c & 0xF00) >> 8]); R.append(hex_digit_to_char((c & 0xF00) >>> 8));
R.append(digits[(c & 0xF0) >> 4]); R.append(hex_digit_to_char((c & 0xF0) >>> 4));
R.append(digits[c & 0xF]); R.append(hex_digit_to_char(c & 0xF));
} }
} }
return R.toString(); return R.toString();
}
private static char hex_digit_to_char(int x) {
return (char)(x <= 9 ? x + '0' : x + ('A' - 10));
} }
/** /**
* The global unescape method, as per ECMA-262 15.1.2.5. * The global unescape method, as per ECMA-262 15.1.2.5.
*/ */
public static Object unescape(Context cx, Scriptable thisObj, private Object js_unescape(Context cx, Object[] args)
Object[] args, Function funObj)
{ {
if (args.length < 1) String s = ScriptRuntime.toString(args, 0);
args = ScriptRuntime.padArguments(args, 1); StringBuffer R = new StringBuffer();
stringIter: for (int k = 0; k < s.length(); k++) {
char c = s.charAt(k);
if (c != '%' || k == s.length() -1) {
R.append(c);
continue;
}
String hex;
int end, start;
if (s.charAt(k+1) == 'u') {
start = k+2;
end = k+6;
} else {
start = k+1;
end = k+3;
}
if (end > s.length()) {
R.append('%');
continue;
}
hex = s.substring(start, end);
for (int i = 0; i < hex.length(); i++)
if (!TokenStream.isXDigit(hex.charAt(i))) {
R.append('%');
continue stringIter;
}
k = end - 1;
R.append((new Character((char) Integer.valueOf(hex, 16).intValue())));
}
String s = ScriptRuntime.toString(args[0]); return R.toString();
StringBuffer R = new StringBuffer();
stringIter: for (int k = 0; k < s.length(); k++) {
char c = s.charAt(k);
if (c != '%' || k == s.length() -1) {
R.append(c);
continue;
}
String hex;
int end, start;
if (s.charAt(k+1) == 'u') {
start = k+2;
end = k+6;
} else {
start = k+1;
end = k+3;
}
if (end > s.length()) {
R.append('%');
continue;
}
hex = s.substring(start, end);
for (int i = 0; i < hex.length(); i++)
if (!TokenStream.isXDigit(hex.charAt(i))) {
R.append('%');
continue stringIter;
}
k = end - 1;
R.append((new Character((char) Integer.valueOf(hex, 16).intValue())));
}
return R.toString();
} }
/** /**
* The global method isNaN, as per ECMA-262 15.1.2.6. * The global method isNaN, as per ECMA-262 15.1.2.6.
*/ */
public static Object isNaN(Context cx, Scriptable thisObj, private Object js_isNaN(Context cx, Object[] args) {
Object[] args, Function funObj)
{
if (args.length < 1) if (args.length < 1)
return Boolean.TRUE; return Boolean.TRUE;
double d = ScriptRuntime.toNumber(args[0]); double d = ScriptRuntime.toNumber(args[0]);
return (d != d) ? Boolean.TRUE : Boolean.FALSE; return (d != d) ? Boolean.TRUE : Boolean.FALSE;
} }
public static Object isFinite(Context cx, Scriptable thisObj, private Object js_isFinite(Context cx, Object[] args) {
Object[] args, Function funObj)
{
if (args.length < 1) if (args.length < 1)
return Boolean.FALSE; return Boolean.FALSE;
double d = ScriptRuntime.toNumber(args[0]); double d = ScriptRuntime.toNumber(args[0]);
@ -382,12 +424,11 @@ public class NativeGlobal {
: Boolean.TRUE; : Boolean.TRUE;
} }
public static Object eval(Context cx, Scriptable thisObj, private Object js_eval(Context cx, Scriptable scope, Object[] args)
Object[] args, Function funObj)
throws JavaScriptException throws JavaScriptException
{ {
String m = ScriptRuntime.getMessage1("msg.cant.call.indirect", "eval"); String m = ScriptRuntime.getMessage1("msg.cant.call.indirect", "eval");
throw NativeGlobal.constructError(cx, "EvalError", m, funObj); throw NativeGlobal.constructError(cx, "EvalError", m, scope);
} }
/** /**
@ -520,12 +561,12 @@ public class NativeGlobal {
* The implementation of all the ECMA error constructors (SyntaxError, * The implementation of all the ECMA error constructors (SyntaxError,
* TypeError, etc.) * TypeError, etc.)
*/ */
public static Object CommonError(Context cx, Object[] args, private Object new_CommonError(IdFunction ctorObj, Context cx,
Function ctorObj, boolean inNewExpr) Scriptable scope, Object[] args)
{ {
Scriptable newInstance = new NativeError(); Scriptable newInstance = new NativeError();
newInstance.setPrototype((Scriptable)(ctorObj.get("prototype", ctorObj))); newInstance.setPrototype((Scriptable)(ctorObj.get("prototype", ctorObj)));
newInstance.setParentScope(cx.ctorScope); newInstance.setParentScope(scope);
if (args.length > 0) if (args.length > 0)
newInstance.put("message", newInstance, args[0]); newInstance.put("message", newInstance, args[0]);
return newInstance; return newInstance;
@ -578,7 +619,7 @@ public class NativeGlobal {
} }
k++; k++;
} }
return R.toString(); return R.toString();
} }
private static boolean isHex(char c) { private static boolean isHex(char c) {
@ -672,33 +713,25 @@ public class NativeGlobal {
private static String uriUnescaped = private static String uriUnescaped =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.!~*'()"; "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.!~*'()";
public static String decodeURI(Context cx, Scriptable thisObj, private String js_decodeURI(Context cx, Object[] args) {
Object[] args, Function funObj) String str = ScriptRuntime.toString(args, 0);
{
String str = ScriptRuntime.toString(args[0]);
return decode(cx, str, uriReservedPlusPound); return decode(cx, str, uriReservedPlusPound);
} }
public static String decodeURIComponent(Context cx, Scriptable thisObj, private String js_decodeURIComponent(Context cx, Object[] args) {
Object[] args, Function funObj) String str = ScriptRuntime.toString(args, 0);
{
String str = ScriptRuntime.toString(args[0]);
return decode(cx, str, ""); return decode(cx, str, "");
} }
public static Object encodeURI(Context cx, Scriptable thisObj, private Object js_encodeURI(Context cx, Object[] args) {
Object[] args, Function funObj) String str = ScriptRuntime.toString(args, 0);
{
String str = ScriptRuntime.toString(args[0]);
return encode(cx, str, uriReservedPlusPound + uriUnescaped); return encode(cx, str, uriReservedPlusPound + uriUnescaped);
} }
public static String encodeURIComponent(Context cx, Scriptable thisObj, private String js_encodeURIComponent(Context cx, Object[] args) {
Object[] args, Function funObj) String str = ScriptRuntime.toString(args, 0);
{
String str = ScriptRuntime.toString(args[0]);
return encode(cx, str, uriUnescaped); return encode(cx, str, uriUnescaped);
} }
/* Convert one UCS-4 char and write it into a UTF-8 buffer, which must be /* Convert one UCS-4 char and write it into a UTF-8 buffer, which must be
* at least 6 bytes long. Return the number of UTF-8 bytes of data written. * at least 6 bytes long. Return the number of UTF-8 bytes of data written.
@ -707,7 +740,7 @@ public class NativeGlobal {
int utf8Length = 1; int utf8Length = 1;
//JS_ASSERT(ucs4Char <= 0x7FFFFFFF); //JS_ASSERT(ucs4Char <= 0x7FFFFFFF);
if ((ucs4Char < 0x80) && (ucs4Char >= 0)) if ((ucs4Char & ~0x7F) == 0)
utf8Buffer[0] = (char)ucs4Char; utf8Buffer[0] = (char)ucs4Char;
else { else {
int i; int i;
@ -737,7 +770,7 @@ public class NativeGlobal {
//JS_ASSERT(utf8Length >= 1 && utf8Length <= 6); //JS_ASSERT(utf8Length >= 1 && utf8Length <= 6);
if (utf8Length == 1) { if (utf8Length == 1) {
ucs4Char = utf8Buffer[0]; ucs4Char = utf8Buffer[0];
// JS_ASSERT(!(ucs4Char & 0x80)); // JS_ASSERT(!(ucs4Char & 0x80));
} else { } else {
//JS_ASSERT((*utf8Buffer & (0x100 - (1 << (7-utf8Length)))) == (0x100 - (1 << (8-utf8Length)))); //JS_ASSERT((*utf8Buffer & (0x100 - (1 << (7-utf8Length)))) == (0x100 - (1 << (8-utf8Length))));
ucs4Char = utf8Buffer[k++] & ((1<<(7-utf8Length))-1); ucs4Char = utf8Buffer[k++] & ((1<<(7-utf8Length))-1);
@ -749,4 +782,20 @@ public class NativeGlobal {
return ucs4Char; return ucs4Char;
} }
private static final int
Id_decodeURI = 1,
Id_decodeURIComponent = 2,
Id_encodeURI = 3,
Id_encodeURIComponent = 4,
Id_escape = 5,
Id_eval = 6,
Id_isFinite = 7,
Id_isNaN = 8,
Id_parseFloat = 9,
Id_parseInt = 10,
Id_unescape = 11,
LAST_METHOD_ID = 11,
Id_new_CommonError = 12;
} }

Просмотреть файл

@ -1,4 +1,4 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- /* -*- Mode: java; tab-width: 4; indent-tabs-mode: 1; c-basic-offset: 4 -*-
* *
* The contents of this file are subject to the Netscape Public * The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file * License Version 1.1 (the "License"); you may not use this file
@ -19,6 +19,8 @@
* Rights Reserved. * Rights Reserved.
* *
* Contributor(s): * Contributor(s):
* Norris Boyd
* Igor Bukanov
* *
* Alternatively, the contents of this file may be used under the * Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the * terms of the GNU Public License (the "GPL"), in which case the
@ -39,119 +41,241 @@ package org.mozilla.javascript;
* See ECMA 15.8. * See ECMA 15.8.
* @author Norris Boyd * @author Norris Boyd
*/ */
public class NativeMath extends ScriptableObject {
public static Scriptable init(Scriptable scope) public class NativeMath extends ScriptableObject
throws PropertyException implements IdFunction.Master
{ {
public static Scriptable init(Scriptable scope) {
NativeMath m = new NativeMath(); NativeMath m = new NativeMath();
m.setPrototype(getObjectPrototype(scope)); Context cx = Context.getContext();
m.setParentScope(scope);
String[] names = { "atan", "atan2", "ceil",
"cos", "floor", "random",
"sin", "sqrt", "tan" };
m.defineFunctionProperties(names, java.lang.Math.class,
ScriptableObject.DONTENUM);
// These functions exist in java.lang.Math, but
// are overloaded. Define our own wrappers.
String[] localNames = { "acos", "asin", "abs", "exp", "max", "min",
"round", "pow", "log" };
m.defineFunctionProperties(localNames, NativeMath.class,
ScriptableObject.DONTENUM);
/*
have to fix up the length property for max & min
which are varargs form, but need to have a length of 2
*/
((FunctionObject)m.get("max", scope)).setLength((short)2);
((FunctionObject)m.get("min", scope)).setLength((short)2);
final int attr = ScriptableObject.DONTENUM |
ScriptableObject.PERMANENT |
ScriptableObject.READONLY;
m.defineProperty("E", new Double(Math.E), attr);
m.defineProperty("PI", new Double(Math.PI), attr);
m.defineProperty("LN10", new Double(2.302585092994046), attr);
m.defineProperty("LN2", new Double(0.6931471805599453), attr);
m.defineProperty("LOG2E", new Double(1.4426950408889634), attr);
m.defineProperty("LOG10E", new Double(0.4342944819032518), attr);
m.defineProperty("SQRT1_2", new Double(0.7071067811865476), attr);
m.defineProperty("SQRT2", new Double(1.4142135623730951), attr);
// We know that scope is a Scriptable object since we // We know that scope is a Scriptable object since we
// constrained the type on initStandardObjects. // constrained the type on initStandardObjects.
ScriptableObject global = (ScriptableObject) scope; m.initForGlobal(cx, (ScriptableObject)scope, false);
global.defineProperty("Math", m, ScriptableObject.DONTENUM);
return m; return m;
} }
public NativeMath() { }
public NativeMath() { public void initForGlobal(Context cx, ScriptableObject global,
} boolean sealed)
public String getClassName() {
return "Math";
}
public static double abs(double d) {
if (d == 0.0)
return 0.0; // abs(-0.0) should be 0.0, but -0.0 < 0.0 == false
else if (d < 0.0)
return -d;
else
return d;
}
public static double acos(double d) {
if ((d != d)
|| (d > 1.0)
|| (d < -1.0))
return Double.NaN;
return Math.acos(d);
}
public static double asin(double d) {
if ((d != d)
|| (d > 1.0)
|| (d < -1.0))
return Double.NaN;
return Math.asin(d);
}
public static double max(Context cx, Scriptable thisObj,
Object[] args, Function funObj)
{
double result = Double.NEGATIVE_INFINITY;
if (args.length == 0)
return result;
for (int i = 0; i < args.length; i++) {
double d = ScriptRuntime.toNumber(args[i]);
if (d != d) return d;
result = Math.max(result, d);
}
return result;
}
public static double min(Context cx, Scriptable thisObj,
Object[] args, Function funObj)
{ {
double result = Double.POSITIVE_INFINITY; setPrototype(getObjectPrototype(global));
if (args.length == 0) setParentScope(global);
return result; if (sealed) {
for (int i = 0; i < args.length; i++) { sealObject();
double d = ScriptRuntime.toNumber(args[i]); }
if (d != d) return d; global.defineProperty("Math", this, ScriptableObject.DONTENUM);
result = Math.min(result, d);
}
return result;
} }
public static double round(double d) { public String getClassName() { return "Math"; }
public boolean has(String name, Scriptable start) {
int id = nameToId(name);
if (0 != id && !wasOverwritten(id)) { return true; }
return super.has(name, start);
}
public Object get(String name, Scriptable start) {
// ALERT: cache the last used value like ScriptableObject does.
// But what about thread safety then?
int id = nameToId(name);
if (0 != id && !wasOverwritten(id)) {
return (id > LAST_METHOD_ID)
? getField(id) : wrapMethod(name, id);
}
return super.get(name, start);
}
public void put(String name, Scriptable start, Object value) {
if (markAsModified(name)) {
super.put(name, start, value);
}
}
public void delete(String name) {
if (markAsModified(name)) {
super.delete(name);
}
}
private boolean wasOverwritten(int id) {
return 0 != (overwritten_flags & (1 << id));
}
// Return if field or method was marked as overwritten
private boolean markAsModified(String name) {
int id = nameToId(name);
if (0 != id && !wasOverwritten(id)) {
if (isSealed() || id > LAST_METHOD_ID) {
// Read only Math constant, ignore modifications
return false;
}
overwritten_flags |= (1 << id);
function_cache[id - 1] = null;
}
return true;
}
private Object wrapMethod(String name, int id) {
IdFunction f = function_cache[id - 1];
if (f == null) {
synchronized (this) {
if (function_cache[id - 1] == null) {
function_cache[id - 1] = new IdFunction(this, name, id);
f = function_cache[id - 1];
}
}
}
return f;
}
private Object getField(int fieldId) {
switch (fieldId) {
case Id_E: return E;
case Id_PI: return PI;
case Id_LN10: return LN10;
case Id_LN2: return LN2;
case Id_LOG2E: return LOG2E;
case Id_LOG10E: return LOG10E;
case Id_SQRT1_2: return SQRT1_2;
case Id_SQRT2: return SQRT2;
}
return null;
}
public int methodArity(int methodId, IdFunction function) {
switch (methodId) {
case Id_atan2:
case Id_max:
case Id_min:
case Id_pow:
return 2;
case Id_random:
return 0;
}
return 1;
}
public Object execMethod
(int methodId, IdFunction function,
Context cx, Scriptable scope, Scriptable thisObj, Object[] args)
throws JavaScriptException
{
switch (methodId) {
case Id_abs: return wrap_dbl(js_abs(to_dbl(args, 0)));
case Id_acos: return wrap_dbl(js_acos(to_dbl(args, 0)));
case Id_asin: return wrap_dbl(js_asin(to_dbl(args, 0)));
case Id_atan: return wrap_dbl(js_atan(to_dbl(args, 0)));
case Id_atan2: return wrap_dbl
(js_atan2(to_dbl(args, 0), to_dbl(args, 1)));
case Id_ceil: return wrap_dbl(js_ceil(to_dbl(args, 0)));
case Id_cos: return wrap_dbl(js_cos(to_dbl(args, 0)));
case Id_exp: return wrap_dbl(js_exp(to_dbl(args, 0)));
case Id_floor: return wrap_dbl(js_floor(to_dbl(args, 0)));
case Id_log: return wrap_dbl(js_log(to_dbl(args, 0)));
case Id_max: return wrap_dbl(js_max(args));
case Id_min: return wrap_dbl(js_min(args));
case Id_pow: return wrap_dbl
(js_pow(to_dbl(args, 0), to_dbl(args, 1)));
case Id_random: return wrap_dbl(js_random());
case Id_round: return wrap_dbl(js_round(to_dbl(args, 0)));
case Id_sin: return wrap_dbl(js_sin(to_dbl(args, 0)));
case Id_sqrt: return wrap_dbl(js_sqrt(to_dbl(args, 0)));
case Id_tan: return wrap_dbl(js_tan(to_dbl(args, 0)));
}
return null;
}
private double to_dbl(Object[] args, int index) {
return ScriptRuntime.toNumber(args, index);
}
private double to_dbl(Object arg) {
return ScriptRuntime.toNumber(arg);
}
private Double wrap_dbl(double x) {
return (x == x) ? new Double(x) : ScriptRuntime.NaNobj;
}
private double js_abs(double x) {
// abs(-0.0) should be 0.0, but -0.0 < 0.0 == false
return (x == 0.0) ? 0.0 : (x < 0.0) ? -x : x;
}
private double js_acos(double x) {
return (x == x && -1.0 <= x && x <= 1.0) ? Math.acos(x) : Double.NaN;
}
private double js_asin(double x) {
return (x == x && -1.0 <= x && x <= 1.0) ? Math.asin(x) : Double.NaN;
}
private double js_atan(double x) { return Math.atan(x); }
private double js_atan2(double x, double y) { return Math.atan2(x, y); }
private double js_ceil(double x) { return Math.ceil(x); }
private double js_cos(double x) { return Math.cos(x); }
private double js_exp(double x) {
return (x == Double.POSITIVE_INFINITY) ? x
: (x == Double.NEGATIVE_INFINITY) ? 0.0
: Math.exp(x);
}
private double js_floor(double x) { return Math.floor(x); }
private double js_log(double x) {
// Java's log(<0) = -Infinity; we need NaN
return (x < 0) ? Double.NaN : Math.log(x);
}
private double js_max(Object[] args) {
double result = Double.NEGATIVE_INFINITY;
if (args.length == 0)
return result;
for (int i = 0; i < args.length; i++) {
double d = ScriptRuntime.toNumber(args[i]);
if (d != d) return d;
result = Math.max(result, d);
}
return result;
}
private double js_min(Object[] args) {
double result = Double.POSITIVE_INFINITY;
if (args.length == 0)
return result;
for (int i = 0; i < args.length; i++) {
double d = ScriptRuntime.toNumber(args[i]);
if (d != d) return d;
result = Math.min(result, d);
}
return result;
}
private double js_pow(double x, double y) {
if (y == 0) return 1.0; // Java's pow(NaN, 0) = NaN; we need 1
if ((x == 0) && (y < 0)) {
if (1 / x > 0) {
// x is +0, Java is -oo, we need +oo
return Double.POSITIVE_INFINITY;
}
/* if x is -0 and y is an odd integer, -oo */
int y_int = (int)y;
if (y_int == y && (y_int & 0x1) != 0)
return Double.NEGATIVE_INFINITY;
return Double.POSITIVE_INFINITY;
}
return Math.pow(x, y);
}
private double js_random() { return Math.random(); }
private double js_round(double d) {
if (d != d) if (d != d)
return d; // NaN return d; // NaN
if (d == Double.POSITIVE_INFINITY || d == Double.NEGATIVE_INFINITY) if (d == Double.POSITIVE_INFINITY || d == Double.NEGATIVE_INFINITY)
@ -164,35 +288,120 @@ public class NativeMath extends ScriptableObject {
return d == 0.0 ? d : 0.0; return d == 0.0 ? d : 0.0;
} }
return (double) l; return (double) l;
} }
public static double pow(double x, double y) { private double js_sin(double x) { return Math.sin(x); }
if (y == 0)
return 1.0; // Java's pow(NaN, 0) = NaN; we need 1 private double js_sqrt(double x) { return Math.sqrt(x); }
if ((x == 0) && (y < 0)) {
Double d = new Double(x); private double js_tan(double x) { return Math.tan(x); }
if (d.equals(new Double(0))) // x is +0
return Double.POSITIVE_INFINITY; // Java is -Infinity private static int nameToId(String s) {
/* if x is -0 and y is an odd integer, -Infinity */ int c;
if (((int)y == y) && (((int)y & 0x1) == 1)) int id = 0;
return Double.NEGATIVE_INFINITY; String guess = null;
return Double.POSITIVE_INFINITY; L:switch (s.length()) {
case 1: if (s.charAt(0)=='E') return Id_E;
break L;
case 2: if (s.charAt(0)=='P'&&s.charAt(1)=='I') return Id_PI;
break L;
case 3: switch (s.charAt(0)) {
case 'L': if (s.charAt(1)=='N'&&s.charAt(2)=='2') return Id_LN2;
break L;
case 'a': if (s.charAt(1)=='b'&&s.charAt(2)=='s') return Id_abs;
break L;
case 'c': if (s.charAt(1)=='o'&&s.charAt(2)=='s') return Id_cos;
break L;
case 'e': if (s.charAt(1)=='x'&&s.charAt(2)=='p') return Id_exp;
break L;
case 'l': if (s.charAt(1)=='o'&&s.charAt(2)=='g') return Id_log;
break L;
case 'm': c=s.charAt(1);
if (c=='a') { if (s.charAt(2)=='x') return Id_max; }
else if (c=='i') { if (s.charAt(2)=='n') return Id_min; }
break L;
case 'p': if (s.charAt(1)=='o'&&s.charAt(2)=='w') return Id_pow;
break L;
case 's': if (s.charAt(1)=='i'&&s.charAt(2)=='n') return Id_sin;
break L;
case 't': if (s.charAt(1)=='a'&&s.charAt(2)=='n') return Id_tan;
break L;
}
break L;
case 4: switch (s.charAt(1)) {
case 'N': guess="LN10";id=Id_LN10; break L;
case 'c': guess="acos";id=Id_acos; break L;
case 's': guess="asin";id=Id_asin; break L;
case 't': guess="atan";id=Id_atan; break L;
case 'e': guess="ceil";id=Id_ceil; break L;
case 'q': guess="sqrt";id=Id_sqrt; break L;
}
break L;
case 5: switch (s.charAt(0)) {
case 'S': guess="SQRT2";id=Id_SQRT2; break L;
case 'L': guess="LOG2E";id=Id_LOG2E; break L;
case 'a': guess="atan2";id=Id_atan2; break L;
case 'f': guess="floor";id=Id_floor; break L;
case 'r': guess="round";id=Id_round; break L;
}
break L;
case 6: c=s.charAt(0);
if (c=='L') { guess="LOG10E";id=Id_LOG10E; }
else if (c=='r') { guess="random";id=Id_random; }
break L;
case 7: guess="SQRT1_2";id=Id_SQRT1_2;
break L;
} }
return Math.pow(x, y);
}
public static double exp(double d) {
if (d == Double.POSITIVE_INFINITY)
return d;
if (d == Double.NEGATIVE_INFINITY)
return 0.0;
return Math.exp(d);
}
public static double log(double x) { return (guess != null && s.equals(guess)) ? id : 0;
if (x < 0)
return Double.NaN; // Java's log(<0) = -Infinity; we need NaN
return Math.log(x);
} }
private static final int
Id_abs = 1,
Id_acos = 2,
Id_asin = 3,
Id_atan = 4,
Id_atan2 = 5,
Id_ceil = 6,
Id_cos = 7,
Id_exp = 8,
Id_floor = 9,
Id_log = 10,
Id_max = 11,
Id_min = 12,
Id_pow = 13,
Id_random = 14,
Id_round = 15,
Id_sin = 16,
Id_sqrt = 17,
Id_tan = 18,
LAST_METHOD_ID = 18,
Id_E = 19,
Id_PI = 20,
Id_LN10 = 21,
Id_LN2 = 22,
Id_LOG2E = 23,
Id_LOG10E = 24,
Id_SQRT1_2 = 25,
Id_SQRT2 = 26;
private static final Double
E = new Double(Math.E),
PI = new Double(Math.PI),
LN10 = new Double(2.302585092994046),
LN2 = new Double(0.6931471805599453),
LOG2E = new Double(1.4426950408889634),
LOG10E = new Double(0.4342944819032518),
SQRT1_2 = new Double(0.7071067811865476),
SQRT2 = new Double(1.4142135623730951);
// Indicates that field or method was overwritten
private int overwritten_flags;
// Cache of constructed wrappers for methods
private IdFunction[] function_cache = new IdFunction[LAST_METHOD_ID];
} }

Просмотреть файл

@ -120,14 +120,14 @@ public class ScriptRuntime {
* See ECMA 9.3. * See ECMA 9.3.
*/ */
public static double toNumber(Object val) { public static double toNumber(Object val) {
if (val != null && val instanceof Scriptable) { if (val == null)
return +0.0;
if (val instanceof Scriptable) {
val = ((Scriptable) val).getDefaultValue(NumberClass); val = ((Scriptable) val).getDefaultValue(NumberClass);
if (val != null && val instanceof Scriptable) if (val != null && val instanceof Scriptable)
throw errorWithClassName("msg.primitive.expected", val); throw errorWithClassName("msg.primitive.expected", val);
// fall through // fall through
} }
if (val == null)
return +0.0;
if (val instanceof String) if (val instanceof String)
return toNumber((String) val); return toNumber((String) val);
if (val instanceof Number) if (val instanceof Number)
@ -137,6 +137,11 @@ public class ScriptRuntime {
throw errorWithClassName("msg.invalid.type", val); throw errorWithClassName("msg.invalid.type", val);
} }
public static double toNumber(Object[] args, int index) {
return (0 <= index && index < args.length)
? toNumber(args[index]) : NaN;
}
// This definition of NaN is identical to that in java.lang.Double // This definition of NaN is identical to that in java.lang.Double
// except that it is not final. This is a workaround for a bug in // except that it is not final. This is a workaround for a bug in
// the Microsoft VM, versions 2.01 and 3.0P1, that causes some uses // the Microsoft VM, versions 2.01 and 3.0P1, that causes some uses
@ -421,6 +426,11 @@ public class ScriptRuntime {
} }
} }
public static String toString(Object[] args, int index) {
return (0 <= index && index < args.length)
? toString(args[index]) : "undefined";
}
public static String numberToString(double d, int base) { public static String numberToString(double d, int base) {
if (d != d) if (d != d)
return "NaN"; return "NaN";
@ -577,6 +587,11 @@ public class ScriptRuntime {
return (int)d; return (int)d;
} }
public static int toInt32(Object[] args, int index) {
return (0 <= index && index < args.length)
? toInt32(args[index]) : 0;
}
public static int toInt32(double d) { public static int toInt32(double d) {
// 0x100000000 gives me a numeric overflow... // 0x100000000 gives me a numeric overflow...
double two32 = 4294967296.0; double two32 = 4294967296.0;
@ -1205,14 +1220,20 @@ public class ScriptRuntime {
String filename, int lineNumber) String filename, int lineNumber)
throws JavaScriptException throws JavaScriptException
{ {
if (fun instanceof FunctionObject) { if (fun instanceof IdFunction) {
IdFunction f = (IdFunction)fun;
String name = f.methodName;
Class cl = f.master.getClass();
if (name.equals("eval") && cl == NativeGlobal.class) {
return NativeGlobal.evalSpecial(cx, scope, thisArg, args,
filename, lineNumber);
}
}
else if (fun instanceof FunctionObject) {
FunctionObject fo = (FunctionObject) fun; FunctionObject fo = (FunctionObject) fun;
Member m = fo.method; Member m = fo.method;
Class cl = m.getDeclaringClass(); Class cl = m.getDeclaringClass();
String name = m.getName(); String name = m.getName();
if (name.equals("eval") && cl == NativeGlobal.class)
return NativeGlobal.evalSpecial(cx, scope, thisArg, args,
filename, lineNumber);
if (name.equals("With") && cl == NativeWith.class) if (name.equals("With") && cl == NativeWith.class)
return NativeWith.newWithSpecial(cx, args, fo, !isCall); return NativeWith.newWithSpecial(cx, args, fo, !isCall);
if (name.equals("jsFunction_exec") && cl == NativeScript.class) if (name.equals("jsFunction_exec") && cl == NativeScript.class)

Просмотреть файл

@ -794,6 +794,7 @@ public abstract class ScriptableObject implements Scriptable {
final String setterPrefix = "jsSet_"; final String setterPrefix = "jsSet_";
final String ctorName = "jsConstructor"; final String ctorName = "jsConstructor";
boolean hasPrefix = false;
Method[] ctorMeths = FunctionObject.findMethods(clazz, ctorName); Method[] ctorMeths = FunctionObject.findMethods(clazz, ctorName);
Member ctorMember = null; Member ctorMember = null;
if (ctorMeths != null) { if (ctorMeths != null) {
@ -803,11 +804,11 @@ public abstract class ScriptableObject implements Scriptable {
ctorMeths[0], ctorMeths[1])); ctorMeths[0], ctorMeths[1]));
} }
ctorMember = ctorMeths[0]; ctorMember = ctorMeths[0];
hasPrefix = true;
} }
// Deprecated: look for functions with the same name as the class // Deprecated: look for functions with the same name as the class
// and consider them constructors. // and consider them constructors.
boolean hasPrefix = false;
for (int i=0; i < methods.length; i++) { for (int i=0; i < methods.length; i++) {
String name = methods[i].getName(); String name = methods[i].getName();
String prefix = null; String prefix = null;