зеркало из https://github.com/mozilla/pjs.git
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:
Родитель
8bf5b5c07a
Коммит
8880f2750c
|
@ -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):
|
||||
* Norris Boyd
|
||||
* Igor Bukanov
|
||||
* Mike McCabe
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the
|
||||
|
@ -49,34 +50,84 @@ import java.lang.reflect.Method;
|
|||
* @author Mike Shaver
|
||||
*/
|
||||
|
||||
public class NativeGlobal {
|
||||
public class NativeGlobal implements IdFunction.Master {
|
||||
|
||||
public static void init(Scriptable scope)
|
||||
throws PropertyException,
|
||||
NotAFunctionException,
|
||||
JavaScriptException
|
||||
{
|
||||
|
||||
String names[] = { "eval",
|
||||
"parseInt",
|
||||
"parseFloat",
|
||||
"escape",
|
||||
"unescape",
|
||||
"isNaN",
|
||||
"isFinite",
|
||||
"decodeURI",
|
||||
"decodeURIComponent",
|
||||
"encodeURI",
|
||||
"encodeURIComponent"
|
||||
};
|
||||
|
||||
NativeGlobal instance = new NativeGlobal();
|
||||
Context cx = Context.getContext();
|
||||
// We can downcast here because Context.initStandardObjects
|
||||
// takes a ScriptableObject scope.
|
||||
ScriptableObject global = (ScriptableObject) scope;
|
||||
global.defineFunctionProperties(names, NativeGlobal.class,
|
||||
ScriptableObject.DONTENUM);
|
||||
instance.initForGlobal(cx, (ScriptableObject) scope, false);
|
||||
}
|
||||
|
||||
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);
|
||||
global.defineProperty("Infinity", new Double(Double.POSITIVE_INFINITY),
|
||||
ScriptableObject.DONTENUM);
|
||||
|
@ -91,28 +142,36 @@ public class NativeGlobal {
|
|||
"TypeError",
|
||||
"URIError"
|
||||
};
|
||||
Method[] m = FunctionObject.findMethods(NativeGlobal.class,
|
||||
"CommonError");
|
||||
Context cx = Context.getContext();
|
||||
|
||||
/*
|
||||
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];
|
||||
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);
|
||||
Scriptable errorProto = cx.newObject(scope, "Error");
|
||||
|
||||
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.
|
||||
*/
|
||||
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();
|
||||
if (len == 0)
|
||||
return ScriptRuntime.NaNobj;
|
||||
|
@ -135,9 +194,7 @@ public class NativeGlobal {
|
|||
radix = NO_RADIX;
|
||||
} else if (radix < 2 || radix > 36) {
|
||||
return ScriptRuntime.NaNobj;
|
||||
} else if (radix == 16 && len - start > 1 &&
|
||||
s.charAt(start) == '0')
|
||||
{
|
||||
} else if (radix == 16 && len - start > 1 && s.charAt(start) == '0') {
|
||||
c = s.charAt(start+1);
|
||||
if (c == 'x' || c == 'X')
|
||||
start += 2;
|
||||
|
@ -169,9 +226,7 @@ public class NativeGlobal {
|
|||
* @param args the arguments to parseFloat, ignoring args[>=1]
|
||||
* @param funObj unused
|
||||
*/
|
||||
public static Object parseFloat(Context cx, Scriptable thisObj,
|
||||
Object[] args, Function funObj)
|
||||
{
|
||||
private Object js_parseFloat(Context cx, Object[] args) {
|
||||
if (args.length < 1)
|
||||
return ScriptRuntime.NaNobj;
|
||||
String s = ScriptRuntime.toString(args[0]);
|
||||
|
@ -252,21 +307,14 @@ public class NativeGlobal {
|
|||
* method, which used to be part of the browser imbedding. Blame
|
||||
* 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,
|
||||
Object[] args, Function funObj)
|
||||
{
|
||||
char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
||||
|
||||
if (args.length < 1)
|
||||
args = ScriptRuntime.padArguments(args, 1);
|
||||
|
||||
String s = ScriptRuntime.toString(args[0]);
|
||||
private Object js_escape(Context cx, Object[] args) {
|
||||
final int
|
||||
URL_XALPHAS = 1,
|
||||
URL_XPALPHAS = 2,
|
||||
URL_PATH = 4;
|
||||
|
||||
String s = ScriptRuntime.toString(args, 0);
|
||||
|
||||
int mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH;
|
||||
if (args.length > 1) { // the 'mask' argument. Non-ECMA.
|
||||
|
@ -274,105 +322,99 @@ public class NativeGlobal {
|
|||
if (d != d || ((mask = (int) d) != d) ||
|
||||
0 != (mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH)))
|
||||
{
|
||||
String message = Context.getMessage
|
||||
("msg.bad.esc.mask", null);
|
||||
String message = Context.getMessage0("msg.bad.esc.mask");
|
||||
cx.reportError(message);
|
||||
// do the ecma thing, in case reportError returns.
|
||||
mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH;
|
||||
}
|
||||
}
|
||||
|
||||
StringBuffer R = new StringBuffer();
|
||||
for (int k = 0; k < s.length(); k++) {
|
||||
char c = s.charAt(k);
|
||||
if (mask != 0 &&
|
||||
((c >= '0' && c <= '9') ||
|
||||
(c >= 'A' && c <= 'Z') ||
|
||||
(c >= 'a' && c <= 'z') ||
|
||||
c == '@' || c == '*' || c == '_' ||
|
||||
StringBuffer R = new StringBuffer();
|
||||
for (int k = 0; k < s.length(); k++) {
|
||||
int c = s.charAt(k), d;
|
||||
if (mask != 0 && ((c >= '0' && c <= '9') ||
|
||||
(c >= 'A' && c <= 'Z') ||
|
||||
(c >= 'a' && c <= 'z') ||
|
||||
c == '@' || c == '*' || c == '_' ||
|
||||
c == '-' || c == '.' ||
|
||||
((c == '/' || c == '+') && mask > 3)))
|
||||
R.append(c);
|
||||
else if (c < 256) {
|
||||
((c == '/' || c == '+') && mask > 3)))
|
||||
R.append((char)c);
|
||||
else if (c < 256) {
|
||||
if (c == ' ' && mask == URL_XPALPHAS) {
|
||||
R.append('+');
|
||||
} else {
|
||||
R.append('%');
|
||||
R.append(digits[c >> 4]);
|
||||
R.append(digits[c & 0xF]);
|
||||
R.append(hex_digit_to_char(c >>> 4));
|
||||
R.append(hex_digit_to_char(c & 0xF));
|
||||
}
|
||||
} else {
|
||||
} else {
|
||||
R.append('%');
|
||||
R.append('u');
|
||||
R.append(digits[c >> 12]);
|
||||
R.append(digits[(c & 0xF00) >> 8]);
|
||||
R.append(digits[(c & 0xF0) >> 4]);
|
||||
R.append(digits[c & 0xF]);
|
||||
}
|
||||
}
|
||||
return R.toString();
|
||||
R.append(hex_digit_to_char(c >>> 12));
|
||||
R.append(hex_digit_to_char((c & 0xF00) >>> 8));
|
||||
R.append(hex_digit_to_char((c & 0xF0) >>> 4));
|
||||
R.append(hex_digit_to_char(c & 0xF));
|
||||
}
|
||||
}
|
||||
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.
|
||||
*/
|
||||
|
||||
public static Object unescape(Context cx, Scriptable thisObj,
|
||||
Object[] args, Function funObj)
|
||||
private Object js_unescape(Context cx, Object[] args)
|
||||
{
|
||||
if (args.length < 1)
|
||||
args = ScriptRuntime.padArguments(args, 1);
|
||||
String s = ScriptRuntime.toString(args, 0);
|
||||
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]);
|
||||
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();
|
||||
return R.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* The global method isNaN, as per ECMA-262 15.1.2.6.
|
||||
*/
|
||||
|
||||
public static Object isNaN(Context cx, Scriptable thisObj,
|
||||
Object[] args, Function funObj)
|
||||
{
|
||||
private Object js_isNaN(Context cx, Object[] args) {
|
||||
if (args.length < 1)
|
||||
return Boolean.TRUE;
|
||||
double d = ScriptRuntime.toNumber(args[0]);
|
||||
return (d != d) ? Boolean.TRUE : Boolean.FALSE;
|
||||
}
|
||||
|
||||
public static Object isFinite(Context cx, Scriptable thisObj,
|
||||
Object[] args, Function funObj)
|
||||
{
|
||||
private Object js_isFinite(Context cx, Object[] args) {
|
||||
if (args.length < 1)
|
||||
return Boolean.FALSE;
|
||||
double d = ScriptRuntime.toNumber(args[0]);
|
||||
|
@ -382,12 +424,11 @@ public class NativeGlobal {
|
|||
: Boolean.TRUE;
|
||||
}
|
||||
|
||||
public static Object eval(Context cx, Scriptable thisObj,
|
||||
Object[] args, Function funObj)
|
||||
private Object js_eval(Context cx, Scriptable scope, Object[] args)
|
||||
throws JavaScriptException
|
||||
{
|
||||
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,
|
||||
* TypeError, etc.)
|
||||
*/
|
||||
public static Object CommonError(Context cx, Object[] args,
|
||||
Function ctorObj, boolean inNewExpr)
|
||||
private Object new_CommonError(IdFunction ctorObj, Context cx,
|
||||
Scriptable scope, Object[] args)
|
||||
{
|
||||
Scriptable newInstance = new NativeError();
|
||||
newInstance.setPrototype((Scriptable)(ctorObj.get("prototype", ctorObj)));
|
||||
newInstance.setParentScope(cx.ctorScope);
|
||||
newInstance.setParentScope(scope);
|
||||
if (args.length > 0)
|
||||
newInstance.put("message", newInstance, args[0]);
|
||||
return newInstance;
|
||||
|
@ -578,7 +619,7 @@ public class NativeGlobal {
|
|||
}
|
||||
k++;
|
||||
}
|
||||
return R.toString();
|
||||
return R.toString();
|
||||
}
|
||||
|
||||
private static boolean isHex(char c) {
|
||||
|
@ -672,33 +713,25 @@ public class NativeGlobal {
|
|||
private static String uriUnescaped =
|
||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.!~*'()";
|
||||
|
||||
public static String decodeURI(Context cx, Scriptable thisObj,
|
||||
Object[] args, Function funObj)
|
||||
{
|
||||
String str = ScriptRuntime.toString(args[0]);
|
||||
private String js_decodeURI(Context cx, Object[] args) {
|
||||
String str = ScriptRuntime.toString(args, 0);
|
||||
return decode(cx, str, uriReservedPlusPound);
|
||||
}
|
||||
}
|
||||
|
||||
public static String decodeURIComponent(Context cx, Scriptable thisObj,
|
||||
Object[] args, Function funObj)
|
||||
{
|
||||
String str = ScriptRuntime.toString(args[0]);
|
||||
private String js_decodeURIComponent(Context cx, Object[] args) {
|
||||
String str = ScriptRuntime.toString(args, 0);
|
||||
return decode(cx, str, "");
|
||||
}
|
||||
}
|
||||
|
||||
public static Object encodeURI(Context cx, Scriptable thisObj,
|
||||
Object[] args, Function funObj)
|
||||
{
|
||||
String str = ScriptRuntime.toString(args[0]);
|
||||
private Object js_encodeURI(Context cx, Object[] args) {
|
||||
String str = ScriptRuntime.toString(args, 0);
|
||||
return encode(cx, str, uriReservedPlusPound + uriUnescaped);
|
||||
}
|
||||
}
|
||||
|
||||
public static String encodeURIComponent(Context cx, Scriptable thisObj,
|
||||
Object[] args, Function funObj)
|
||||
{
|
||||
String str = ScriptRuntime.toString(args[0]);
|
||||
private String js_encodeURIComponent(Context cx, Object[] args) {
|
||||
String str = ScriptRuntime.toString(args, 0);
|
||||
return encode(cx, str, uriUnescaped);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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.
|
||||
|
@ -707,7 +740,7 @@ public class NativeGlobal {
|
|||
int utf8Length = 1;
|
||||
|
||||
//JS_ASSERT(ucs4Char <= 0x7FFFFFFF);
|
||||
if ((ucs4Char < 0x80) && (ucs4Char >= 0))
|
||||
if ((ucs4Char & ~0x7F) == 0)
|
||||
utf8Buffer[0] = (char)ucs4Char;
|
||||
else {
|
||||
int i;
|
||||
|
@ -737,7 +770,7 @@ public class NativeGlobal {
|
|||
//JS_ASSERT(utf8Length >= 1 && utf8Length <= 6);
|
||||
if (utf8Length == 1) {
|
||||
ucs4Char = utf8Buffer[0];
|
||||
// JS_ASSERT(!(ucs4Char & 0x80));
|
||||
// JS_ASSERT(!(ucs4Char & 0x80));
|
||||
} else {
|
||||
//JS_ASSERT((*utf8Buffer & (0x100 - (1 << (7-utf8Length)))) == (0x100 - (1 << (8-utf8Length))));
|
||||
ucs4Char = utf8Buffer[k++] & ((1<<(7-utf8Length))-1);
|
||||
|
@ -749,4 +782,20 @@ public class NativeGlobal {
|
|||
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
|
||||
* License Version 1.1 (the "License"); you may not use this file
|
||||
|
@ -19,6 +19,8 @@
|
|||
* Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Norris Boyd
|
||||
* 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
|
||||
|
@ -39,119 +41,241 @@ package org.mozilla.javascript;
|
|||
* See ECMA 15.8.
|
||||
* @author Norris Boyd
|
||||
*/
|
||||
public class NativeMath extends ScriptableObject {
|
||||
|
||||
public static Scriptable init(Scriptable scope)
|
||||
throws PropertyException
|
||||
{
|
||||
public class NativeMath extends ScriptableObject
|
||||
implements IdFunction.Master
|
||||
{
|
||||
|
||||
public static Scriptable init(Scriptable scope) {
|
||||
NativeMath m = new NativeMath();
|
||||
m.setPrototype(getObjectPrototype(scope));
|
||||
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);
|
||||
|
||||
Context cx = Context.getContext();
|
||||
// We know that scope is a Scriptable object since we
|
||||
// constrained the type on initStandardObjects.
|
||||
ScriptableObject global = (ScriptableObject) scope;
|
||||
global.defineProperty("Math", m, ScriptableObject.DONTENUM);
|
||||
|
||||
m.initForGlobal(cx, (ScriptableObject)scope, false);
|
||||
return m;
|
||||
}
|
||||
|
||||
public NativeMath() { }
|
||||
|
||||
public NativeMath() {
|
||||
}
|
||||
|
||||
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)
|
||||
public void initForGlobal(Context cx, ScriptableObject global,
|
||||
boolean sealed)
|
||||
{
|
||||
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;
|
||||
setPrototype(getObjectPrototype(global));
|
||||
setParentScope(global);
|
||||
if (sealed) {
|
||||
sealObject();
|
||||
}
|
||||
global.defineProperty("Math", this, ScriptableObject.DONTENUM);
|
||||
}
|
||||
|
||||
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)
|
||||
return d; // NaN
|
||||
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 (double) l;
|
||||
}
|
||||
}
|
||||
|
||||
public static double 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)) {
|
||||
Double d = new Double(x);
|
||||
if (d.equals(new Double(0))) // x is +0
|
||||
return Double.POSITIVE_INFINITY; // Java is -Infinity
|
||||
/* if x is -0 and y is an odd integer, -Infinity */
|
||||
if (((int)y == y) && (((int)y & 0x1) == 1))
|
||||
return Double.NEGATIVE_INFINITY;
|
||||
return Double.POSITIVE_INFINITY;
|
||||
private double js_sin(double x) { return Math.sin(x); }
|
||||
|
||||
private double js_sqrt(double x) { return Math.sqrt(x); }
|
||||
|
||||
private double js_tan(double x) { return Math.tan(x); }
|
||||
|
||||
private static int nameToId(String s) {
|
||||
int c;
|
||||
int id = 0;
|
||||
String guess = null;
|
||||
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) {
|
||||
if (x < 0)
|
||||
return Double.NaN; // Java's log(<0) = -Infinity; we need NaN
|
||||
return Math.log(x);
|
||||
return (guess != null && s.equals(guess)) ? id : 0;
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
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);
|
||||
if (val != null && val instanceof Scriptable)
|
||||
throw errorWithClassName("msg.primitive.expected", val);
|
||||
// fall through
|
||||
}
|
||||
if (val == null)
|
||||
return +0.0;
|
||||
if (val instanceof String)
|
||||
return toNumber((String) val);
|
||||
if (val instanceof Number)
|
||||
|
@ -137,6 +137,11 @@ public class ScriptRuntime {
|
|||
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
|
||||
// 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
|
||||
|
@ -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) {
|
||||
if (d != d)
|
||||
return "NaN";
|
||||
|
@ -577,6 +587,11 @@ public class ScriptRuntime {
|
|||
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) {
|
||||
// 0x100000000 gives me a numeric overflow...
|
||||
double two32 = 4294967296.0;
|
||||
|
@ -1205,14 +1220,20 @@ public class ScriptRuntime {
|
|||
String filename, int lineNumber)
|
||||
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;
|
||||
Member m = fo.method;
|
||||
Class cl = m.getDeclaringClass();
|
||||
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)
|
||||
return NativeWith.newWithSpecial(cx, args, fo, !isCall);
|
||||
if (name.equals("jsFunction_exec") && cl == NativeScript.class)
|
||||
|
|
|
@ -794,6 +794,7 @@ public abstract class ScriptableObject implements Scriptable {
|
|||
final String setterPrefix = "jsSet_";
|
||||
final String ctorName = "jsConstructor";
|
||||
|
||||
boolean hasPrefix = false;
|
||||
Method[] ctorMeths = FunctionObject.findMethods(clazz, ctorName);
|
||||
Member ctorMember = null;
|
||||
if (ctorMeths != null) {
|
||||
|
@ -803,11 +804,11 @@ public abstract class ScriptableObject implements Scriptable {
|
|||
ctorMeths[0], ctorMeths[1]));
|
||||
}
|
||||
ctorMember = ctorMeths[0];
|
||||
hasPrefix = true;
|
||||
}
|
||||
|
||||
// Deprecated: look for functions with the same name as the class
|
||||
// and consider them constructors.
|
||||
boolean hasPrefix = false;
|
||||
for (int i=0; i < methods.length; i++) {
|
||||
String name = methods[i].getName();
|
||||
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):
|
||||
* Norris Boyd
|
||||
* Igor Bukanov
|
||||
* Mike McCabe
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the
|
||||
|
@ -49,34 +50,84 @@ import java.lang.reflect.Method;
|
|||
* @author Mike Shaver
|
||||
*/
|
||||
|
||||
public class NativeGlobal {
|
||||
public class NativeGlobal implements IdFunction.Master {
|
||||
|
||||
public static void init(Scriptable scope)
|
||||
throws PropertyException,
|
||||
NotAFunctionException,
|
||||
JavaScriptException
|
||||
{
|
||||
|
||||
String names[] = { "eval",
|
||||
"parseInt",
|
||||
"parseFloat",
|
||||
"escape",
|
||||
"unescape",
|
||||
"isNaN",
|
||||
"isFinite",
|
||||
"decodeURI",
|
||||
"decodeURIComponent",
|
||||
"encodeURI",
|
||||
"encodeURIComponent"
|
||||
};
|
||||
|
||||
NativeGlobal instance = new NativeGlobal();
|
||||
Context cx = Context.getContext();
|
||||
// We can downcast here because Context.initStandardObjects
|
||||
// takes a ScriptableObject scope.
|
||||
ScriptableObject global = (ScriptableObject) scope;
|
||||
global.defineFunctionProperties(names, NativeGlobal.class,
|
||||
ScriptableObject.DONTENUM);
|
||||
instance.initForGlobal(cx, (ScriptableObject) scope, false);
|
||||
}
|
||||
|
||||
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);
|
||||
global.defineProperty("Infinity", new Double(Double.POSITIVE_INFINITY),
|
||||
ScriptableObject.DONTENUM);
|
||||
|
@ -91,28 +142,36 @@ public class NativeGlobal {
|
|||
"TypeError",
|
||||
"URIError"
|
||||
};
|
||||
Method[] m = FunctionObject.findMethods(NativeGlobal.class,
|
||||
"CommonError");
|
||||
Context cx = Context.getContext();
|
||||
|
||||
/*
|
||||
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];
|
||||
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);
|
||||
Scriptable errorProto = cx.newObject(scope, "Error");
|
||||
|
||||
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.
|
||||
*/
|
||||
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();
|
||||
if (len == 0)
|
||||
return ScriptRuntime.NaNobj;
|
||||
|
@ -135,9 +194,7 @@ public class NativeGlobal {
|
|||
radix = NO_RADIX;
|
||||
} else if (radix < 2 || radix > 36) {
|
||||
return ScriptRuntime.NaNobj;
|
||||
} else if (radix == 16 && len - start > 1 &&
|
||||
s.charAt(start) == '0')
|
||||
{
|
||||
} else if (radix == 16 && len - start > 1 && s.charAt(start) == '0') {
|
||||
c = s.charAt(start+1);
|
||||
if (c == 'x' || c == 'X')
|
||||
start += 2;
|
||||
|
@ -169,9 +226,7 @@ public class NativeGlobal {
|
|||
* @param args the arguments to parseFloat, ignoring args[>=1]
|
||||
* @param funObj unused
|
||||
*/
|
||||
public static Object parseFloat(Context cx, Scriptable thisObj,
|
||||
Object[] args, Function funObj)
|
||||
{
|
||||
private Object js_parseFloat(Context cx, Object[] args) {
|
||||
if (args.length < 1)
|
||||
return ScriptRuntime.NaNobj;
|
||||
String s = ScriptRuntime.toString(args[0]);
|
||||
|
@ -252,21 +307,14 @@ public class NativeGlobal {
|
|||
* method, which used to be part of the browser imbedding. Blame
|
||||
* 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,
|
||||
Object[] args, Function funObj)
|
||||
{
|
||||
char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
||||
|
||||
if (args.length < 1)
|
||||
args = ScriptRuntime.padArguments(args, 1);
|
||||
|
||||
String s = ScriptRuntime.toString(args[0]);
|
||||
private Object js_escape(Context cx, Object[] args) {
|
||||
final int
|
||||
URL_XALPHAS = 1,
|
||||
URL_XPALPHAS = 2,
|
||||
URL_PATH = 4;
|
||||
|
||||
String s = ScriptRuntime.toString(args, 0);
|
||||
|
||||
int mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH;
|
||||
if (args.length > 1) { // the 'mask' argument. Non-ECMA.
|
||||
|
@ -274,105 +322,99 @@ public class NativeGlobal {
|
|||
if (d != d || ((mask = (int) d) != d) ||
|
||||
0 != (mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH)))
|
||||
{
|
||||
String message = Context.getMessage
|
||||
("msg.bad.esc.mask", null);
|
||||
String message = Context.getMessage0("msg.bad.esc.mask");
|
||||
cx.reportError(message);
|
||||
// do the ecma thing, in case reportError returns.
|
||||
mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH;
|
||||
}
|
||||
}
|
||||
|
||||
StringBuffer R = new StringBuffer();
|
||||
for (int k = 0; k < s.length(); k++) {
|
||||
char c = s.charAt(k);
|
||||
if (mask != 0 &&
|
||||
((c >= '0' && c <= '9') ||
|
||||
(c >= 'A' && c <= 'Z') ||
|
||||
(c >= 'a' && c <= 'z') ||
|
||||
c == '@' || c == '*' || c == '_' ||
|
||||
StringBuffer R = new StringBuffer();
|
||||
for (int k = 0; k < s.length(); k++) {
|
||||
int c = s.charAt(k), d;
|
||||
if (mask != 0 && ((c >= '0' && c <= '9') ||
|
||||
(c >= 'A' && c <= 'Z') ||
|
||||
(c >= 'a' && c <= 'z') ||
|
||||
c == '@' || c == '*' || c == '_' ||
|
||||
c == '-' || c == '.' ||
|
||||
((c == '/' || c == '+') && mask > 3)))
|
||||
R.append(c);
|
||||
else if (c < 256) {
|
||||
((c == '/' || c == '+') && mask > 3)))
|
||||
R.append((char)c);
|
||||
else if (c < 256) {
|
||||
if (c == ' ' && mask == URL_XPALPHAS) {
|
||||
R.append('+');
|
||||
} else {
|
||||
R.append('%');
|
||||
R.append(digits[c >> 4]);
|
||||
R.append(digits[c & 0xF]);
|
||||
R.append(hex_digit_to_char(c >>> 4));
|
||||
R.append(hex_digit_to_char(c & 0xF));
|
||||
}
|
||||
} else {
|
||||
} else {
|
||||
R.append('%');
|
||||
R.append('u');
|
||||
R.append(digits[c >> 12]);
|
||||
R.append(digits[(c & 0xF00) >> 8]);
|
||||
R.append(digits[(c & 0xF0) >> 4]);
|
||||
R.append(digits[c & 0xF]);
|
||||
}
|
||||
}
|
||||
return R.toString();
|
||||
R.append(hex_digit_to_char(c >>> 12));
|
||||
R.append(hex_digit_to_char((c & 0xF00) >>> 8));
|
||||
R.append(hex_digit_to_char((c & 0xF0) >>> 4));
|
||||
R.append(hex_digit_to_char(c & 0xF));
|
||||
}
|
||||
}
|
||||
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.
|
||||
*/
|
||||
|
||||
public static Object unescape(Context cx, Scriptable thisObj,
|
||||
Object[] args, Function funObj)
|
||||
private Object js_unescape(Context cx, Object[] args)
|
||||
{
|
||||
if (args.length < 1)
|
||||
args = ScriptRuntime.padArguments(args, 1);
|
||||
String s = ScriptRuntime.toString(args, 0);
|
||||
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]);
|
||||
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();
|
||||
return R.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* The global method isNaN, as per ECMA-262 15.1.2.6.
|
||||
*/
|
||||
|
||||
public static Object isNaN(Context cx, Scriptable thisObj,
|
||||
Object[] args, Function funObj)
|
||||
{
|
||||
private Object js_isNaN(Context cx, Object[] args) {
|
||||
if (args.length < 1)
|
||||
return Boolean.TRUE;
|
||||
double d = ScriptRuntime.toNumber(args[0]);
|
||||
return (d != d) ? Boolean.TRUE : Boolean.FALSE;
|
||||
}
|
||||
|
||||
public static Object isFinite(Context cx, Scriptable thisObj,
|
||||
Object[] args, Function funObj)
|
||||
{
|
||||
private Object js_isFinite(Context cx, Object[] args) {
|
||||
if (args.length < 1)
|
||||
return Boolean.FALSE;
|
||||
double d = ScriptRuntime.toNumber(args[0]);
|
||||
|
@ -382,12 +424,11 @@ public class NativeGlobal {
|
|||
: Boolean.TRUE;
|
||||
}
|
||||
|
||||
public static Object eval(Context cx, Scriptable thisObj,
|
||||
Object[] args, Function funObj)
|
||||
private Object js_eval(Context cx, Scriptable scope, Object[] args)
|
||||
throws JavaScriptException
|
||||
{
|
||||
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,
|
||||
* TypeError, etc.)
|
||||
*/
|
||||
public static Object CommonError(Context cx, Object[] args,
|
||||
Function ctorObj, boolean inNewExpr)
|
||||
private Object new_CommonError(IdFunction ctorObj, Context cx,
|
||||
Scriptable scope, Object[] args)
|
||||
{
|
||||
Scriptable newInstance = new NativeError();
|
||||
newInstance.setPrototype((Scriptable)(ctorObj.get("prototype", ctorObj)));
|
||||
newInstance.setParentScope(cx.ctorScope);
|
||||
newInstance.setParentScope(scope);
|
||||
if (args.length > 0)
|
||||
newInstance.put("message", newInstance, args[0]);
|
||||
return newInstance;
|
||||
|
@ -578,7 +619,7 @@ public class NativeGlobal {
|
|||
}
|
||||
k++;
|
||||
}
|
||||
return R.toString();
|
||||
return R.toString();
|
||||
}
|
||||
|
||||
private static boolean isHex(char c) {
|
||||
|
@ -672,33 +713,25 @@ public class NativeGlobal {
|
|||
private static String uriUnescaped =
|
||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.!~*'()";
|
||||
|
||||
public static String decodeURI(Context cx, Scriptable thisObj,
|
||||
Object[] args, Function funObj)
|
||||
{
|
||||
String str = ScriptRuntime.toString(args[0]);
|
||||
private String js_decodeURI(Context cx, Object[] args) {
|
||||
String str = ScriptRuntime.toString(args, 0);
|
||||
return decode(cx, str, uriReservedPlusPound);
|
||||
}
|
||||
}
|
||||
|
||||
public static String decodeURIComponent(Context cx, Scriptable thisObj,
|
||||
Object[] args, Function funObj)
|
||||
{
|
||||
String str = ScriptRuntime.toString(args[0]);
|
||||
private String js_decodeURIComponent(Context cx, Object[] args) {
|
||||
String str = ScriptRuntime.toString(args, 0);
|
||||
return decode(cx, str, "");
|
||||
}
|
||||
}
|
||||
|
||||
public static Object encodeURI(Context cx, Scriptable thisObj,
|
||||
Object[] args, Function funObj)
|
||||
{
|
||||
String str = ScriptRuntime.toString(args[0]);
|
||||
private Object js_encodeURI(Context cx, Object[] args) {
|
||||
String str = ScriptRuntime.toString(args, 0);
|
||||
return encode(cx, str, uriReservedPlusPound + uriUnescaped);
|
||||
}
|
||||
}
|
||||
|
||||
public static String encodeURIComponent(Context cx, Scriptable thisObj,
|
||||
Object[] args, Function funObj)
|
||||
{
|
||||
String str = ScriptRuntime.toString(args[0]);
|
||||
private String js_encodeURIComponent(Context cx, Object[] args) {
|
||||
String str = ScriptRuntime.toString(args, 0);
|
||||
return encode(cx, str, uriUnescaped);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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.
|
||||
|
@ -707,7 +740,7 @@ public class NativeGlobal {
|
|||
int utf8Length = 1;
|
||||
|
||||
//JS_ASSERT(ucs4Char <= 0x7FFFFFFF);
|
||||
if ((ucs4Char < 0x80) && (ucs4Char >= 0))
|
||||
if ((ucs4Char & ~0x7F) == 0)
|
||||
utf8Buffer[0] = (char)ucs4Char;
|
||||
else {
|
||||
int i;
|
||||
|
@ -737,7 +770,7 @@ public class NativeGlobal {
|
|||
//JS_ASSERT(utf8Length >= 1 && utf8Length <= 6);
|
||||
if (utf8Length == 1) {
|
||||
ucs4Char = utf8Buffer[0];
|
||||
// JS_ASSERT(!(ucs4Char & 0x80));
|
||||
// JS_ASSERT(!(ucs4Char & 0x80));
|
||||
} else {
|
||||
//JS_ASSERT((*utf8Buffer & (0x100 - (1 << (7-utf8Length)))) == (0x100 - (1 << (8-utf8Length))));
|
||||
ucs4Char = utf8Buffer[k++] & ((1<<(7-utf8Length))-1);
|
||||
|
@ -749,4 +782,20 @@ public class NativeGlobal {
|
|||
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
|
||||
* License Version 1.1 (the "License"); you may not use this file
|
||||
|
@ -19,6 +19,8 @@
|
|||
* Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Norris Boyd
|
||||
* 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
|
||||
|
@ -39,119 +41,241 @@ package org.mozilla.javascript;
|
|||
* See ECMA 15.8.
|
||||
* @author Norris Boyd
|
||||
*/
|
||||
public class NativeMath extends ScriptableObject {
|
||||
|
||||
public static Scriptable init(Scriptable scope)
|
||||
throws PropertyException
|
||||
{
|
||||
public class NativeMath extends ScriptableObject
|
||||
implements IdFunction.Master
|
||||
{
|
||||
|
||||
public static Scriptable init(Scriptable scope) {
|
||||
NativeMath m = new NativeMath();
|
||||
m.setPrototype(getObjectPrototype(scope));
|
||||
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);
|
||||
|
||||
Context cx = Context.getContext();
|
||||
// We know that scope is a Scriptable object since we
|
||||
// constrained the type on initStandardObjects.
|
||||
ScriptableObject global = (ScriptableObject) scope;
|
||||
global.defineProperty("Math", m, ScriptableObject.DONTENUM);
|
||||
|
||||
m.initForGlobal(cx, (ScriptableObject)scope, false);
|
||||
return m;
|
||||
}
|
||||
|
||||
public NativeMath() { }
|
||||
|
||||
public NativeMath() {
|
||||
}
|
||||
|
||||
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)
|
||||
public void initForGlobal(Context cx, ScriptableObject global,
|
||||
boolean sealed)
|
||||
{
|
||||
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;
|
||||
setPrototype(getObjectPrototype(global));
|
||||
setParentScope(global);
|
||||
if (sealed) {
|
||||
sealObject();
|
||||
}
|
||||
global.defineProperty("Math", this, ScriptableObject.DONTENUM);
|
||||
}
|
||||
|
||||
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)
|
||||
return d; // NaN
|
||||
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 (double) l;
|
||||
}
|
||||
}
|
||||
|
||||
public static double 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)) {
|
||||
Double d = new Double(x);
|
||||
if (d.equals(new Double(0))) // x is +0
|
||||
return Double.POSITIVE_INFINITY; // Java is -Infinity
|
||||
/* if x is -0 and y is an odd integer, -Infinity */
|
||||
if (((int)y == y) && (((int)y & 0x1) == 1))
|
||||
return Double.NEGATIVE_INFINITY;
|
||||
return Double.POSITIVE_INFINITY;
|
||||
private double js_sin(double x) { return Math.sin(x); }
|
||||
|
||||
private double js_sqrt(double x) { return Math.sqrt(x); }
|
||||
|
||||
private double js_tan(double x) { return Math.tan(x); }
|
||||
|
||||
private static int nameToId(String s) {
|
||||
int c;
|
||||
int id = 0;
|
||||
String guess = null;
|
||||
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) {
|
||||
if (x < 0)
|
||||
return Double.NaN; // Java's log(<0) = -Infinity; we need NaN
|
||||
return Math.log(x);
|
||||
return (guess != null && s.equals(guess)) ? id : 0;
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
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);
|
||||
if (val != null && val instanceof Scriptable)
|
||||
throw errorWithClassName("msg.primitive.expected", val);
|
||||
// fall through
|
||||
}
|
||||
if (val == null)
|
||||
return +0.0;
|
||||
if (val instanceof String)
|
||||
return toNumber((String) val);
|
||||
if (val instanceof Number)
|
||||
|
@ -137,6 +137,11 @@ public class ScriptRuntime {
|
|||
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
|
||||
// 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
|
||||
|
@ -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) {
|
||||
if (d != d)
|
||||
return "NaN";
|
||||
|
@ -577,6 +587,11 @@ public class ScriptRuntime {
|
|||
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) {
|
||||
// 0x100000000 gives me a numeric overflow...
|
||||
double two32 = 4294967296.0;
|
||||
|
@ -1205,14 +1220,20 @@ public class ScriptRuntime {
|
|||
String filename, int lineNumber)
|
||||
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;
|
||||
Member m = fo.method;
|
||||
Class cl = m.getDeclaringClass();
|
||||
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)
|
||||
return NativeWith.newWithSpecial(cx, args, fo, !isCall);
|
||||
if (name.equals("jsFunction_exec") && cl == NativeScript.class)
|
||||
|
|
|
@ -794,6 +794,7 @@ public abstract class ScriptableObject implements Scriptable {
|
|||
final String setterPrefix = "jsSet_";
|
||||
final String ctorName = "jsConstructor";
|
||||
|
||||
boolean hasPrefix = false;
|
||||
Method[] ctorMeths = FunctionObject.findMethods(clazz, ctorName);
|
||||
Member ctorMember = null;
|
||||
if (ctorMeths != null) {
|
||||
|
@ -803,11 +804,11 @@ public abstract class ScriptableObject implements Scriptable {
|
|||
ctorMeths[0], ctorMeths[1]));
|
||||
}
|
||||
ctorMember = ctorMeths[0];
|
||||
hasPrefix = true;
|
||||
}
|
||||
|
||||
// Deprecated: look for functions with the same name as the class
|
||||
// and consider them constructors.
|
||||
boolean hasPrefix = false;
|
||||
for (int i=0; i < methods.length; i++) {
|
||||
String name = methods[i].getName();
|
||||
String prefix = null;
|
||||
|
|
Загрузка…
Ссылка в новой задаче