1
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3
* The contents of this file are subject to the Netscape Public
4
* License Version 1.1 (the "License"); you may not use this file
5
* except in compliance with the License. You may obtain a copy of
6
* the License at http://www.mozilla.org/NPL/
8
* Software distributed under the License is distributed on an "AS
9
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
10
* implied. See the License for the specific language governing
11
* rights and limitations under the License.
13
* The Original Code is Rhino code, released
16
* The Initial Developer of the Original Code is Netscape
17
* Communications Corporation. Portions created by Netscape are
18
* Copyright (C) 1997-2000 Netscape Communications Corporation. All
29
* Alternatively, the contents of this file may be used under the
30
* terms of the GNU Public License (the "GPL"), in which case the
31
* provisions of the GPL are applicable instead of those above.
32
* If you wish to allow use of your version of this file only
33
* under the terms of the GPL and not to allow others to use your
34
* version of this file under the NPL, indicate your decision by
35
* deleting the provisions above and replace them with the notice
36
* and other provisions required by the GPL. If you do not delete
37
* the provisions above, a recipient may use your version of this
38
* file under either the NPL or the GPL.
41
package org.mozilla.javascript;
44
import java.lang.reflect.*;
45
import org.mozilla.classfile.DefiningClassLoader;
48
* This is the class that implements the runtime.
53
public class ScriptRuntime {
56
* No instances should be created.
58
protected ScriptRuntime() {
62
* There's such a huge space (and some time) waste for the Foo.class
63
* syntax: the compiler sticks in a test of a static field in the
64
* enclosing class for null and the code for creating the class value.
65
* It has to do this since the reference has to get pushed off til
66
* executiontime (i.e. can't force an early load), but for the
67
* 'standard' classes - especially those in java.lang, we can trust
68
* that they won't cause problems by being loaded early.
71
public final static Class UndefinedClass = Undefined.class;
72
public final static Class ScriptableClass = Scriptable.class;
73
public final static Class StringClass = String.class;
74
public final static Class NumberClass = Number.class;
75
public final static Class BooleanClass = Boolean.class;
76
public final static Class ByteClass = Byte.class;
77
public final static Class ShortClass = Short.class;
78
public final static Class IntegerClass = Integer.class;
79
public final static Class LongClass = Long.class;
80
public final static Class FloatClass = Float.class;
81
public final static Class DoubleClass = Double.class;
82
public final static Class CharacterClass = Character.class;
83
public final static Class ObjectClass = Object.class;
84
public final static Class FunctionClass = Function.class;
85
public final static Class ClassClass = Class.class;
88
* Convert the value to a boolean.
92
public static boolean toBoolean(Object val) {
95
if (val instanceof Scriptable) {
96
if (Context.getContext().isVersionECMA1()) {
98
return val != Undefined.instance;
101
val = ((Scriptable) val).getDefaultValue(BooleanClass);
102
if (val instanceof Scriptable)
103
throw errorWithClassName("msg.primitive.expected", val);
106
if (val instanceof String)
107
return ((String) val).length() != 0;
108
if (val instanceof Number) {
109
double d = ((Number) val).doubleValue();
110
return (d == d && d != 0.0);
112
if (val instanceof Boolean)
113
return ((Boolean) val).booleanValue();
114
throw errorWithClassName("msg.invalid.type", val);
117
public static boolean toBoolean(Object[] args, int index) {
118
return (index < args.length) ? toBoolean(args[index]) : false;
121
* Convert the value to a number.
125
public static double toNumber(Object val) {
128
if (val instanceof Scriptable) {
129
val = ((Scriptable) val).getDefaultValue(NumberClass);
130
if (val != null && val instanceof Scriptable)
131
throw errorWithClassName("msg.primitive.expected", val);
134
if (val instanceof String)
135
return toNumber((String) val);
136
if (val instanceof Number)
137
return ((Number) val).doubleValue();
138
if (val instanceof Boolean)
139
return ((Boolean) val).booleanValue() ? 1 : +0.0;
140
throw errorWithClassName("msg.invalid.type", val);
143
public static double toNumber(Object[] args, int index) {
144
return (index < args.length) ? toNumber(args[index]) : NaN;
147
// This definition of NaN is identical to that in java.lang.Double
148
// except that it is not final. This is a workaround for a bug in
149
// the Microsoft VM, versions 2.01 and 3.0P1, that causes some uses
150
// (returns at least) of Double.NaN to be converted to 1.0.
151
// So we use ScriptRuntime.NaN instead of Double.NaN.
152
public static double NaN = 0.0d / 0.0;
153
public static Double NaNobj = new Double(0.0d / 0.0);
155
// A similar problem exists for negative zero.
156
public static double negativeZero = -0.0;
159
* Helper function for toNumber, parseInt, and TokenStream.getToken.
161
static double stringToNumber(String s, int start, int radix) {
163
char lowerCaseBound = 'a';
164
char upperCaseBound = 'A';
165
int len = s.length();
167
digitMax = (char) ('0' + radix - 1);
170
lowerCaseBound = (char) ('a' + radix - 10);
171
upperCaseBound = (char) ('A' + radix - 10);
175
for (end=start; end < len; end++) {
176
char c = s.charAt(end);
178
if ('0' <= c && c <= digitMax)
180
else if ('a' <= c && c < lowerCaseBound)
181
newDigit = c - 'a' + 10;
182
else if ('A' <= c && c < upperCaseBound)
183
newDigit = c - 'A' + 10;
186
sum = sum*radix + newDigit;
191
if (sum >= 9007199254740992.0) {
193
/* If we're accumulating a decimal number and the number
194
* is >= 2^53, then the result from the repeated multiply-add
195
* above may be inaccurate. Call Java to get the correct
199
return Double.valueOf(s.substring(start, end)).doubleValue();
200
} catch (NumberFormatException nfe) {
203
} else if (radix == 2 || radix == 4 || radix == 8 ||
204
radix == 16 || radix == 32)
206
/* The number may also be inaccurate for one of these bases.
207
* This happens if the addition in value*radix + digit causes
208
* a round-down to an even least significant mantissa bit
209
* when the first dropped bit is a one. If any of the
210
* following digits in the number (which haven't been added
211
* in yet) are nonzero then the correct action would have
212
* been to round up instead of down. An example of this
213
* occurs when reading the number 0x1000000000000081, which
214
* rounds to 0x1000000000000000 instead of 0x1000000000000100.
216
BinaryDigitReader bdr = new BinaryDigitReader(radix, s, start, end);
220
/* Skip leading zeros. */
222
bit = bdr.getNextBinaryDigit();
226
/* Gather the 53 significant bits (including the leading 1) */
228
for (int j = 52; j != 0; j--) {
229
bit = bdr.getNextBinaryDigit();
234
/* bit54 is the 54th bit (the first dropped from the mantissa) */
235
int bit54 = bdr.getNextBinaryDigit();
238
int sticky = 0; /* sticky is 1 if any bit beyond the 54th is 1 */
241
while ((bit3 = bdr.getNextBinaryDigit()) >= 0) {
245
sum += bit54 & (bit | sticky);
250
/* We don't worry about inaccurate numbers for any other base. */
257
* ToNumber applied to the String type
261
public static double toNumber(String s) {
262
int len = s.length();
267
// Empty or contains only whitespace
270
startChar = s.charAt(start);
271
if (!Character.isWhitespace(startChar))
276
if (startChar == '0' && start+2 < len &&
277
Character.toLowerCase(s.charAt(start+1)) == 'x')
278
// A hexadecimal number
279
return stringToNumber(s, start + 2, 16);
281
if ((startChar == '+' || startChar == '-') && start+3 < len &&
282
s.charAt(start+1) == '0' &&
283
Character.toLowerCase(s.charAt(start+2)) == 'x') {
284
// A hexadecimal number
285
double val = stringToNumber(s, start + 3, 16);
286
return startChar == '-' ? -val : val;
291
while (Character.isWhitespace(endChar = s.charAt(end)))
293
if (endChar == 'y') {
294
// check for "Infinity"
295
if (startChar == '+' || startChar == '-')
297
if (start + 7 == end && s.regionMatches(start, "Infinity", 0, 8))
298
return startChar == '-'
299
? Double.NEGATIVE_INFINITY
300
: Double.POSITIVE_INFINITY;
303
// A non-hexadecimal, non-infinity number:
304
// just try a normal floating point conversion
305
String sub = s.substring(start, end+1);
306
if (MSJVM_BUG_WORKAROUNDS) {
307
// The MS JVM will accept non-conformant strings
308
// rather than throwing a NumberFormatException
310
for (int i=sub.length()-1; i >= 0; i--) {
311
char c = sub.charAt(i);
312
if (('0' <= c && c <= '9') || c == '.' ||
313
c == 'e' || c == 'E' ||
314
c == '+' || c == '-')
320
return Double.valueOf(sub).doubleValue();
321
} catch (NumberFormatException ex) {
327
* Helper function for builtin objects that use the varargs form.
328
* ECMA function formal arguments are undefined if not supplied;
329
* this function pads the argument array out to the expected
330
* length, if necessary.
332
public static Object[] padArguments(Object[] args, int count) {
333
if (count < args.length)
337
Object[] result = new Object[count];
338
for (i = 0; i < args.length; i++) {
342
for (; i < count; i++) {
343
result[i] = Undefined.instance;
349
/* Work around Microsoft Java VM bugs. */
350
private final static boolean MSJVM_BUG_WORKAROUNDS = true;
353
* For escaping strings printed by object and array literals; not quite
354
* the same as 'escape.'
356
public static String escapeString(String s) {
357
// ack! Java lacks \v.
358
String escapeMap = "\bb\ff\nn\rr\tt\u000bv\"\"''";
359
StringBuffer result = new StringBuffer(s.length());
361
for(int i=0; i < s.length(); i++) {
362
char c = s.charAt(i);
364
// an ordinary print character
365
if (c >= ' ' && c <= '~' // string.h isprint()
372
// an \escaped sort of character
374
if ((index = escapeMap.indexOf(c)) >= 0) {
376
result.append(escapeMap.charAt(index + 1));
382
String hex = Integer.toHexString((int) c);
383
if (hex.length() == 1) {
384
result.append("\\x0");
387
result.append("\\x");
394
String hex = Integer.toHexString((int) c);
395
// cool idiom courtesy Shaver.
396
result.append("\\u");
397
for (int l = hex.length(); l < 4; l++)
402
return result.toString();
407
* Convert the value to a string.
411
public static String toString(Object val) {
415
if (val instanceof Scriptable) {
416
val = ((Scriptable) val).getDefaultValue(StringClass);
417
if (val != Undefined.instance && val instanceof Scriptable) {
418
throw errorWithClassName("msg.primitive.expected", val);
422
if (val instanceof Number) {
423
// XXX should we just teach NativeNumber.stringValue()
425
return numberToString(((Number) val).doubleValue(), 10);
427
return val.toString();
431
public static String toString(Object[] args, int index) {
432
return (index < args.length) ? toString(args[index]) : "undefined";
436
* Optimized version of toString(Object) for numbers.
438
public static String toString(double val) {
439
return numberToString(val, 10);
442
public static String numberToString(double d, int base) {
445
if (d == Double.POSITIVE_INFINITY)
447
if (d == Double.NEGATIVE_INFINITY)
452
if ((base < 2) || (base > 36)) {
453
throw Context.reportRuntimeError1(
454
"msg.bad.radix", Integer.toString(base));
458
return DToA.JS_dtobasestr(base, d);
460
StringBuffer result = new StringBuffer();
461
DToA.JS_dtostr(result, DToA.DTOSTR_STANDARD, 0, d);
462
return result.toString();
468
* Convert the value to an object.
472
public static Scriptable toObject(Scriptable scope, Object val) {
473
return toObject(scope, val, null);
476
public static Scriptable toObject(Scriptable scope, Object val,
480
throw NativeGlobal.typeError0("msg.null.to.object", scope);
482
if (val instanceof Scriptable) {
483
if (val == Undefined.instance) {
484
throw NativeGlobal.typeError0("msg.undef.to.object", scope);
486
return (Scriptable) val;
488
String className = val instanceof String ? "String" :
489
val instanceof Number ? "Number" :
490
val instanceof Boolean ? "Boolean" :
493
if (className == null) {
494
// Extension: Wrap as a LiveConnect object.
495
Object wrapped = NativeJavaObject.wrap(scope, val, staticClass);
496
if (wrapped instanceof Scriptable)
497
return (Scriptable) wrapped;
498
throw errorWithClassName("msg.invalid.type", val);
501
Object[] args = { val };
502
scope = ScriptableObject.getTopLevelScope(scope);
503
Scriptable result = newObject(Context.getContext(), scope,
508
public static Scriptable newObject(Context cx, Scriptable scope,
509
String constructorName, Object[] args)
513
return cx.newObject(scope, constructorName, args);
515
catch (NotAFunctionException e) {
518
catch (PropertyException e) {
521
catch (JavaScriptException e) {
524
throw cx.reportRuntimeError(re.getMessage());
531
public static double toInteger(Object val) {
532
return toInteger(toNumber(val));
535
// convenience method
536
public static double toInteger(double d) {
542
d == Double.POSITIVE_INFINITY ||
543
d == Double.NEGATIVE_INFINITY)
547
return Math.floor(d);
552
public static double toInteger(Object[] args, int index) {
553
return (index < args.length) ? toInteger(args[index]) : +0.0;
560
public static int toInt32(Object val) {
561
// 0x100000000 gives me a numeric overflow...
562
double two32 = 4294967296.0;
563
double two31 = 2147483648.0;
565
// short circuit for common small values; TokenStream
566
// returns them as Bytes.
567
if (val instanceof Byte)
568
return ((Number)val).intValue();
570
double d = toNumber(val);
571
if (d != d || d == 0.0 ||
572
d == Double.POSITIVE_INFINITY ||
573
d == Double.NEGATIVE_INFINITY)
576
d = Math.IEEEremainder(d, two32);
583
return (int)(d - two32);
588
public static int toInt32(Object[] args, int index) {
589
return (index < args.length) ? toInt32(args[index]) : 0;
592
public static int toInt32(double d) {
593
// 0x100000000 gives me a numeric overflow...
594
double two32 = 4294967296.0;
595
double two31 = 2147483648.0;
597
if (d != d || d == 0.0 ||
598
d == Double.POSITIVE_INFINITY ||
599
d == Double.NEGATIVE_INFINITY)
602
d = Math.IEEEremainder(d, two32);
609
return (int)(d - two32);
619
// must return long to hold an _unsigned_ int
620
public static long toUint32(double d) {
621
// 0x100000000 gives me a numeric overflow...
622
double two32 = 4294967296.0;
624
if (d != d || d == 0.0 ||
625
d == Double.POSITIVE_INFINITY ||
626
d == Double.NEGATIVE_INFINITY)
634
d = Math.IEEEremainder(d, two32);
640
return (long) Math.floor(d);
643
public static long toUint32(Object val) {
644
return toUint32(toNumber(val));
651
public static char toUint16(Object val) {
652
long int16 = 0x10000;
654
double d = toNumber(val);
655
if (d != d || d == 0.0 ||
656
d == Double.POSITIVE_INFINITY ||
657
d == Double.NEGATIVE_INFINITY)
662
d = Math.IEEEremainder(d, int16);
668
return (char) Math.floor(d);
672
* Unwrap a JavaScriptException. Sleight of hand so that we don't
673
* javadoc JavaScriptException.getRuntimeValue().
675
public static Object unwrapJavaScriptException(JavaScriptException jse) {
680
* Check a WrappedException. Unwrap a JavaScriptException and return
681
* the value, otherwise rethrow.
683
public static Object unwrapWrappedException(WrappedException we) {
684
Throwable t = we.getWrappedException();
685
if (t instanceof JavaScriptException)
686
return ((JavaScriptException) t).value;
690
public static Object getProp(Object obj, String id, Scriptable scope) {
692
if (obj instanceof Scriptable) {
693
start = (Scriptable) obj;
695
start = toObject(scope, obj);
697
if (start == null || start == Undefined.instance) {
698
String msg = start == null ? "msg.null.to.object"
700
throw NativeGlobal.constructError(
701
Context.getContext(), "ConversionError",
702
ScriptRuntime.getMessage0(msg),
705
Scriptable m = start;
707
Object result = m.get(id, start);
708
if (result != Scriptable.NOT_FOUND)
710
m = m.getPrototype();
712
return Undefined.instance;
715
public static Object getTopLevelProp(Scriptable scope, String id) {
716
Scriptable s = ScriptableObject.getTopLevelScope(scope);
720
if (v != Scriptable.NOT_FOUND)
722
s = s.getPrototype();
729
/***********************************************************************/
731
public static Scriptable getProto(Object obj, Scriptable scope) {
733
if (obj instanceof Scriptable) {
734
s = (Scriptable) obj;
736
s = toObject(scope, obj);
739
throw NativeGlobal.typeError0("msg.null.to.object", scope);
741
return s.getPrototype();
744
public static Scriptable getParent(Object obj) {
747
s = (Scriptable) obj;
749
catch (ClassCastException e) {
755
return getThis(s.getParentScope());
758
public static Scriptable getParent(Object obj, Scriptable scope) {
760
if (obj instanceof Scriptable) {
761
s = (Scriptable) obj;
763
s = toObject(scope, obj);
766
throw NativeGlobal.typeError0("msg.null.to.object", scope);
768
return s.getParentScope();
771
public static Object setProto(Object obj, Object value, Scriptable scope) {
773
if (obj instanceof Scriptable) {
774
start = (Scriptable) obj;
776
start = toObject(scope, obj);
778
Scriptable result = value == null ? null : toObject(scope, value);
779
Scriptable s = result;
782
throw Context.reportRuntimeError1(
783
"msg.cyclic.value", "__proto__");
785
s = s.getPrototype();
788
throw NativeGlobal.typeError0("msg.null.to.object", scope);
790
start.setPrototype(result);
794
public static Object setParent(Object obj, Object value, Scriptable scope) {
796
if (obj instanceof Scriptable) {
797
start = (Scriptable) obj;
799
start = toObject(scope, obj);
801
Scriptable result = value == null ? null : toObject(scope, value);
802
Scriptable s = result;
805
throw Context.reportRuntimeError1(
806
"msg.cyclic.value", "__parent__");
808
s = s.getParentScope();
811
throw NativeGlobal.typeError0("msg.null.to.object", scope);
813
start.setParentScope(result);
817
public static Object setProp(Object obj, String id, Object value,
821
if (obj instanceof Scriptable) {
822
start = (Scriptable) obj;
824
start = toObject(scope, obj);
827
throw NativeGlobal.typeError0("msg.null.to.object", scope);
829
Scriptable m = start;
831
if (m.has(id, start)) {
832
m.put(id, start, value);
835
m = m.getPrototype();
838
start.put(id, start, value);
842
// Return -1L if str is not an index or the index value as lower 32
843
// bits of the result
844
private static long indexFromString(String str) {
845
// It must be a string.
847
// The length of the decimal string representation of
848
// Integer.MAX_VALUE, 2147483647
849
final int MAX_VALUE_LENGTH = 10;
851
int len = str.length();
854
boolean negate = false;
855
int c = str.charAt(0);
865
&& len <= (negate ? MAX_VALUE_LENGTH + 1 : MAX_VALUE_LENGTH))
867
// Use negative numbers to accumulate index to handle
868
// Integer.MIN_VALUE that is greater by 1 in absolute value
869
// then Integer.MAX_VALUE
874
// Note that 00, 01, 000 etc. are not indexes
875
while (i != len && 0 <= (c = str.charAt(i) - '0') && c <= 9)
878
index = 10 * index - c;
882
// Make sure all characters were consumed and that it couldn't
885
(oldIndex > (Integer.MIN_VALUE / 10) ||
886
(oldIndex == (Integer.MIN_VALUE / 10) &&
887
c <= (negate ? -(Integer.MIN_VALUE % 10)
888
: (Integer.MAX_VALUE % 10)))))
890
return 0xFFFFFFFFL & (negate ? index : -index);
897
static String getStringId(Object id) {
898
if (id instanceof Number) {
899
double d = ((Number) id).doubleValue();
901
if (((double) index) == d)
905
String s = toString(id);
906
long indexTest = indexFromString(s);
912
static int getIntId(Object id) {
913
if (id instanceof Number) {
914
double d = ((Number) id).doubleValue();
916
if (((double) index) == d)
920
String s = toString(id);
921
long indexTest = indexFromString(s);
923
return (int)indexTest;
927
public static Object getElem(Object obj, Object id, Scriptable scope) {
930
if (id instanceof Number) {
931
double d = ((Number) id).doubleValue();
933
s = ((double) index) == d ? null : toString(id);
936
long indexTest = indexFromString(s);
937
if (indexTest >= 0) {
938
index = (int)indexTest;
944
Scriptable start = obj instanceof Scriptable
946
: toObject(scope, obj);
947
Scriptable m = start;
949
if (s.equals("__proto__"))
950
return start.getPrototype();
951
if (s.equals("__parent__"))
952
return start.getParentScope();
954
Object result = m.get(s, start);
955
if (result != Scriptable.NOT_FOUND)
957
m = m.getPrototype();
959
return Undefined.instance;
962
Object result = m.get(index, start);
963
if (result != Scriptable.NOT_FOUND)
965
m = m.getPrototype();
967
return Undefined.instance;
972
* A cheaper and less general version of the above for well-known argument
975
public static Object getElem(Scriptable obj, int index)
979
Object result = m.get(index, obj);
980
if (result != Scriptable.NOT_FOUND)
982
m = m.getPrototype();
984
return Undefined.instance;
987
public static Object setElem(Object obj, Object id, Object value,
992
if (id instanceof Number) {
993
double d = ((Number) id).doubleValue();
995
s = ((double) index) == d ? null : toString(id);
998
long indexTest = indexFromString(s);
999
if (indexTest >= 0) {
1000
index = (int)indexTest;
1007
Scriptable start = obj instanceof Scriptable
1009
: toObject(scope, obj);
1010
Scriptable m = start;
1012
if (s.equals("__proto__"))
1013
return setProto(obj, value, scope);
1014
if (s.equals("__parent__"))
1015
return setParent(obj, value, scope);
1018
if (m.has(s, start)) {
1019
m.put(s, start, value);
1022
m = m.getPrototype();
1023
} while (m != null);
1024
start.put(s, start, value);
1029
if (m.has(index, start)) {
1030
m.put(index, start, value);
1033
m = m.getPrototype();
1034
} while (m != null);
1035
start.put(index, start, value);
1040
* A cheaper and less general version of the above for well-known argument
1043
public static Object setElem(Scriptable obj, int index, Object value)
1047
if (m.has(index, obj)) {
1048
m.put(index, obj, value);
1051
m = m.getPrototype();
1052
} while (m != null);
1053
obj.put(index, obj, value);
1058
* The delete operator
1062
* In ECMA 0.19, the description of the delete operator (11.4.1)
1063
* assumes that the [[Delete]] method returns a value. However,
1064
* the definition of the [[Delete]] operator (8.6.2.5) does not
1065
* define a return value. Here we assume that the [[Delete]]
1066
* method doesn't return a value.
1068
public static Object delete(Object obj, Object id) {
1069
String s = getStringId(id);
1070
boolean result = s != null
1071
? ScriptableObject.deleteProperty((Scriptable) obj, s)
1072
: ScriptableObject.deleteProperty((Scriptable) obj, getIntId(id));
1073
return result ? Boolean.TRUE : Boolean.FALSE;
1077
* Looks up a name in the scope chain and returns its value.
1079
public static Object name(Scriptable scopeChain, String id) {
1080
Scriptable obj = scopeChain;
1082
while (obj != null) {
1085
Object result = m.get(id, obj);
1086
if (result != Scriptable.NOT_FOUND)
1088
m = m.getPrototype();
1089
} while (m != null);
1090
obj = obj.getParentScope();
1092
throw NativeGlobal.constructError
1093
(Context.getContext(), "ReferenceError",
1094
ScriptRuntime.getMessage1("msg.is.not.defined", id.toString()),
1099
* Returns the object in the scope chain that has a given property.
1101
* The order of evaluation of an assignment expression involves
1102
* evaluating the lhs to a reference, evaluating the rhs, and then
1103
* modifying the reference with the rhs value. This method is used
1104
* to 'bind' the given name to an object containing that property
1105
* so that the side effects of evaluating the rhs do not affect
1106
* which property is modified.
1107
* Typically used in conjunction with setName.
1111
public static Scriptable bind(Scriptable scope, String id) {
1112
Scriptable obj = scope;
1114
while (obj != null) {
1119
m = m.getPrototype();
1120
} while (m != null);
1121
obj = obj.getParentScope();
1126
public static Scriptable getBase(Scriptable scope, String id) {
1127
Scriptable obj = scope;
1129
while (obj != null) {
1132
if (m.get(id, obj) != Scriptable.NOT_FOUND)
1134
m = m.getPrototype();
1135
} while (m != null);
1136
obj = obj.getParentScope();
1138
throw NativeGlobal.constructError(
1139
Context.getContext(), "ReferenceError",
1140
ScriptRuntime.getMessage1("msg.is.not.defined", id),
1144
public static Scriptable getThis(Scriptable base) {
1145
while (base instanceof NativeWith)
1146
base = base.getPrototype();
1147
if (base instanceof NativeCall)
1148
base = ScriptableObject.getTopLevelScope(base);
1152
public static Object setName(Scriptable bound, Object value,
1153
Scriptable scope, String id)
1155
if (bound == null) {
1156
// "newname = 7;", where 'newname' has not yet
1157
// been defined, creates a new property in the
1158
// global object. Find the global object by
1159
// walking up the scope chain.
1160
Scriptable next = scope;
1163
next = bound.getParentScope();
1164
} while (next != null);
1166
bound.put(id, bound, value);
1168
This code is causing immense performance problems in
1169
scripts that assign to the variables as a way of creating them.
1170
XXX need strict mode
1171
String message = getMessage1("msg.assn.create", id);
1172
Context.reportWarning(message);
1176
return setProp(bound, id, value, scope);
1179
public static Enumeration initEnum(Object value, Scriptable scope) {
1180
Scriptable m = toObject(scope, value);
1181
return new IdEnumeration(m);
1184
public static Object nextEnum(Enumeration enum) {
1185
// OPT this could be more efficient; should junk the Enumeration
1187
if (!enum.hasMoreElements())
1189
return enum.nextElement();
1192
// Form used by class files generated by 1.4R3 and earlier.
1193
public static Object call(Context cx, Object fun, Object thisArg,
1195
throws JavaScriptException
1197
Scriptable scope = null;
1198
if (fun instanceof Scriptable)
1199
scope = ((Scriptable) fun).getParentScope();
1200
return call(cx, fun, thisArg, args, scope);
1203
public static Object call(Context cx, Object fun, Object thisArg,
1204
Object[] args, Scriptable scope)
1205
throws JavaScriptException
1209
function = (Function) fun;
1211
catch (ClassCastException e) {
1212
throw NativeGlobal.typeError1
1213
("msg.isnt.function", toString(fun), scope);
1217
if (thisArg instanceof Scriptable || thisArg == null) {
1218
thisObj = (Scriptable) thisArg;
1220
thisObj = ScriptRuntime.toObject(scope, thisArg);
1222
return function.call(cx, scope, thisObj, args);
1225
private static Object callOrNewSpecial(Context cx, Scriptable scope,
1226
Object fun, Object jsThis,
1228
Object[] args, boolean isCall,
1229
String filename, int lineNumber)
1230
throws JavaScriptException
1232
if (fun instanceof IdFunction) {
1233
IdFunction f = (IdFunction)fun;
1234
String name = f.getFunctionName();
1235
if (name.length() == 4) {
1236
if (name.equals("eval")) {
1237
if (f.master.getClass() == NativeGlobal.class) {
1238
return NativeGlobal.evalSpecial(cx, scope,
1240
filename, lineNumber);
1243
else if (name.equals("With")) {
1244
if (f.master.getClass() == NativeWith.class) {
1245
return NativeWith.newWithSpecial(cx, args, f, !isCall);
1248
else if (name.equals("exec")) {
1249
if (f.master.getClass() == NativeScript.class) {
1250
return ((NativeScript)jsThis).
1251
exec(cx, ScriptableObject.getTopLevelScope(scope));
1254
RegExpProxy proxy = cx.getRegExpProxy();
1255
if (proxy != null && proxy.isRegExp(jsThis)) {
1256
return call(cx, fun, jsThis, args, scope);
1262
else // could've been <java>.XXX.exec() that was re-directed here
1263
if (fun instanceof NativeJavaMethod)
1264
return call(cx, fun, jsThis, args, scope);
1267
return call(cx, fun, thisArg, args, scope);
1268
return newObject(cx, fun, args, scope);
1271
public static Object callSpecial(Context cx, Object fun,
1272
Object thisArg, Object[] args,
1273
Scriptable enclosingThisArg,
1274
Scriptable scope, String filename,
1276
throws JavaScriptException
1278
return callOrNewSpecial(cx, scope, fun, thisArg,
1279
enclosingThisArg, args, true,
1280
filename, lineNumber);
1288
public static Scriptable newObject(Context cx, Object fun,
1289
Object[] args, Scriptable scope)
1290
throws JavaScriptException
1296
return f.construct(cx, scope, args);
1298
// else fall through to error
1299
} catch (ClassCastException e) {
1300
// fall through to error
1302
throw NativeGlobal.typeError1
1303
("msg.isnt.function", toString(fun), scope);
1306
public static Scriptable newObjectSpecial(Context cx, Object fun,
1307
Object[] args, Scriptable scope)
1308
throws JavaScriptException
1310
return (Scriptable) callOrNewSpecial(cx, scope, fun, null, null, args,
1315
* The typeof operator
1317
public static String typeof(Object value) {
1318
if (value == Undefined.instance)
1322
if (value instanceof Scriptable)
1323
return (value instanceof Function) ? "function" : "object";
1324
if (value instanceof String)
1326
if (value instanceof Number)
1328
if (value instanceof Boolean)
1330
throw errorWithClassName("msg.invalid.type", value);
1334
* The typeof operator that correctly handles the undefined case
1336
public static String typeofName(Scriptable scope, String id) {
1337
Object val = bind(scope, id);
1340
return typeof(getProp(val, id, scope));
1344
// implement the '-' operator inline in the caller
1345
// as "-toNumber(val)"
1348
// implement the '!' operator inline in the caller
1349
// as "!toBoolean(val)"
1352
// implement the '~' operator inline in the caller
1353
// as "~toInt32(val)"
1355
public static Object add(Object val1, Object val2) {
1356
if (val1 instanceof Scriptable)
1357
val1 = ((Scriptable) val1).getDefaultValue(null);
1358
if (val2 instanceof Scriptable)
1359
val2 = ((Scriptable) val2).getDefaultValue(null);
1360
if (!(val1 instanceof String) && !(val2 instanceof String))
1361
if ((val1 instanceof Number) && (val2 instanceof Number))
1362
return new Double(((Number)val1).doubleValue() +
1363
((Number)val2).doubleValue());
1365
return new Double(toNumber(val1) + toNumber(val2));
1366
return toString(val1) + toString(val2);
1369
public static Object postIncrement(Object value) {
1370
if (value instanceof Number)
1371
value = new Double(((Number)value).doubleValue() + 1.0);
1373
value = new Double(toNumber(value) + 1.0);
1377
public static Object postIncrement(Scriptable scopeChain, String id) {
1378
Scriptable obj = scopeChain;
1380
while (obj != null) {
1383
Object result = m.get(id, obj);
1384
if (result != Scriptable.NOT_FOUND) {
1385
Object newValue = result;
1386
if (newValue instanceof Number) {
1387
newValue = new Double(
1388
((Number)newValue).doubleValue() + 1.0);
1389
m.put(id, obj, newValue);
1393
newValue = new Double(toNumber(newValue) + 1.0);
1394
m.put(id, obj, newValue);
1395
return new Double(toNumber(result));
1398
m = m.getPrototype();
1399
} while (m != null);
1400
obj = obj.getParentScope();
1402
throw NativeGlobal.constructError
1403
(Context.getContext(), "ReferenceError",
1404
ScriptRuntime.getMessage1("msg.is.not.defined", id),
1408
public static Object postIncrement(Object obj, String id, Scriptable scope) {
1410
if (obj instanceof Scriptable) {
1411
start = (Scriptable) obj;
1413
start = toObject(scope, obj);
1415
if (start == null) {
1416
throw NativeGlobal.typeError0("msg.null.to.object", scope);
1418
Scriptable m = start;
1420
Object result = m.get(id, start);
1421
if (result != Scriptable.NOT_FOUND) {
1422
Object newValue = result;
1423
if (newValue instanceof Number) {
1424
newValue = new Double(
1425
((Number)newValue).doubleValue() + 1.0);
1426
m.put(id, start, newValue);
1430
newValue = new Double(toNumber(newValue) + 1.0);
1431
m.put(id, start, newValue);
1432
return new Double(toNumber(result));
1435
m = m.getPrototype();
1436
} while (m != null);
1437
return Undefined.instance;
1440
public static Object postIncrementElem(Object obj,
1441
Object index, Scriptable scope) {
1442
Object oldValue = getElem(obj, index, scope);
1443
if (oldValue == Undefined.instance)
1444
return Undefined.instance;
1445
double resultValue = toNumber(oldValue);
1446
Double newValue = new Double(resultValue + 1.0);
1447
setElem(obj, index, newValue, scope);
1448
return new Double(resultValue);
1451
public static Object postDecrementElem(Object obj,
1452
Object index, Scriptable scope) {
1453
Object oldValue = getElem(obj, index, scope);
1454
if (oldValue == Undefined.instance)
1455
return Undefined.instance;
1456
double resultValue = toNumber(oldValue);
1457
Double newValue = new Double(resultValue - 1.0);
1458
setElem(obj, index, newValue, scope);
1459
return new Double(resultValue);
1462
public static Object postDecrement(Object value) {
1463
if (value instanceof Number)
1464
value = new Double(((Number)value).doubleValue() - 1.0);
1466
value = new Double(toNumber(value) - 1.0);
1470
public static Object postDecrement(Scriptable scopeChain, String id) {
1471
Scriptable obj = scopeChain;
1473
while (obj != null) {
1476
Object result = m.get(id, obj);
1477
if (result != Scriptable.NOT_FOUND) {
1478
Object newValue = result;
1479
if (newValue instanceof Number) {
1480
newValue = new Double(
1481
((Number)newValue).doubleValue() - 1.0);
1482
m.put(id, obj, newValue);
1486
newValue = new Double(toNumber(newValue) - 1.0);
1487
m.put(id, obj, newValue);
1488
return new Double(toNumber(result));
1491
m = m.getPrototype();
1492
} while (m != null);
1493
obj = obj.getParentScope();
1495
throw NativeGlobal.constructError
1496
(Context.getContext(), "ReferenceError",
1497
ScriptRuntime.getMessage1("msg.is.not.defined", id),
1501
public static Object postDecrement(Object obj, String id, Scriptable scope) {
1503
if (obj instanceof Scriptable) {
1504
start = (Scriptable) obj;
1506
start = toObject(scope, obj);
1508
if (start == null) {
1509
throw NativeGlobal.typeError0("msg.null.to.object", scope);
1511
Scriptable m = start;
1513
Object result = m.get(id, start);
1514
if (result != Scriptable.NOT_FOUND) {
1515
Object newValue = result;
1516
if (newValue instanceof Number) {
1517
newValue = new Double(
1518
((Number)newValue).doubleValue() - 1.0);
1519
m.put(id, start, newValue);
1523
newValue = new Double(toNumber(newValue) - 1.0);
1524
m.put(id, start, newValue);
1525
return new Double(toNumber(result));
1528
m = m.getPrototype();
1529
} while (m != null);
1530
return Undefined.instance;
1533
public static Object toPrimitive(Object val) {
1534
if (val == null || !(val instanceof Scriptable)) {
1537
Object result = ((Scriptable) val).getDefaultValue(null);
1538
if (result != null && result instanceof Scriptable)
1539
throw NativeGlobal.typeError0("msg.bad.default.value", val);
1543
private static Class getTypeOfValue(Object obj) {
1545
return ScriptableClass;
1546
if (obj == Undefined.instance)
1547
return UndefinedClass;
1548
if (obj instanceof Scriptable)
1549
return ScriptableClass;
1550
if (obj instanceof Number)
1552
return obj.getClass();
1560
public static boolean eq(Object x, Object y) {
1561
Object xCopy = x; // !!! JIT bug in Cafe 2.1
1562
Object yCopy = y; // need local copies, otherwise their values get blown below
1564
Class typeX = getTypeOfValue(x);
1565
Class typeY = getTypeOfValue(y);
1566
if (typeX == typeY) {
1567
if (typeX == UndefinedClass)
1569
if (typeX == NumberClass)
1570
return ((Number) x).doubleValue() ==
1571
((Number) y).doubleValue();
1572
if (typeX == StringClass || typeX == BooleanClass)
1573
return xCopy.equals(yCopy); // !!! JIT bug in Cafe 2.1
1574
if (typeX == ScriptableClass) {
1577
if (x instanceof Wrapper &&
1578
y instanceof Wrapper)
1580
return ((Wrapper) x).unwrap() ==
1581
((Wrapper) y).unwrap();
1585
throw new RuntimeException(); // shouldn't get here
1587
if (x == null && y == Undefined.instance)
1589
if (x == Undefined.instance && y == null)
1591
if (typeX == NumberClass &&
1592
typeY == StringClass)
1594
return ((Number) x).doubleValue() == toNumber(y);
1596
if (typeX == StringClass &&
1597
typeY == NumberClass)
1599
return toNumber(x) == ((Number) y).doubleValue();
1601
if (typeX == BooleanClass) {
1602
x = new Double(toNumber(x));
1603
xCopy = x; // !!! JIT bug in Cafe 2.1
1606
if (typeY == BooleanClass) {
1607
y = new Double(toNumber(y));
1608
yCopy = y; // !!! JIT bug in Cafe 2.1
1611
if ((typeX == StringClass ||
1612
typeX == NumberClass) &&
1613
typeY == ScriptableClass && y != null)
1616
yCopy = y; // !!! JIT bug in Cafe 2.1
1619
if (typeX == ScriptableClass && x != null &&
1620
(typeY == StringClass ||
1621
typeY == NumberClass))
1624
xCopy = x; // !!! JIT bug in Cafe 2.1
1631
public static Boolean eqB(Object x, Object y) {
1633
return Boolean.TRUE;
1635
return Boolean.FALSE;
1638
public static Boolean neB(Object x, Object y) {
1640
return Boolean.FALSE;
1642
return Boolean.TRUE;
1645
public static boolean shallowEq(Object x, Object y) {
1646
Class type = getTypeOfValue(x);
1647
if (type != getTypeOfValue(y))
1649
if (type == StringClass || type == BooleanClass)
1651
if (type == NumberClass)
1652
return ((Number) x).doubleValue() ==
1653
((Number) y).doubleValue();
1654
if (type == ScriptableClass) {
1657
if (x instanceof Wrapper && y instanceof Wrapper)
1658
return ((Wrapper) x).unwrap() ==
1659
((Wrapper) y).unwrap();
1662
if (type == UndefinedClass)
1667
public static Boolean seqB(Object x, Object y) {
1669
return Boolean.TRUE;
1671
return Boolean.FALSE;
1674
public static Boolean sneB(Object x, Object y) {
1676
return Boolean.FALSE;
1678
return Boolean.TRUE;
1682
* The instanceof operator.
1684
* @return a instanceof b
1686
public static boolean instanceOf(Scriptable scope, Object a, Object b) {
1687
// Check RHS is an object
1688
if (! (b instanceof Scriptable)) {
1689
throw NativeGlobal.typeError0("msg.instanceof.not.object", scope);
1692
// for primitive values on LHS, return false
1693
// XXX we may want to change this so that
1694
// 5 instanceof Number == true
1695
if (! (a instanceof Scriptable))
1698
return ((Scriptable)b).hasInstance((Scriptable)a);
1704
* @return true iff rhs appears in lhs' proto chain
1706
protected static boolean jsDelegatesTo(Scriptable lhs, Scriptable rhs) {
1707
Scriptable proto = lhs.getPrototype();
1709
while (proto != null) {
1710
if (proto.equals(rhs)) return true;
1711
proto = proto.getPrototype();
1720
* This is a new JS 1.3 language feature. The in operator mirrors
1721
* the operation of the for .. in construct, and tests whether the
1722
* rhs has the property given by the lhs. It is different from the
1723
* for .. in construct in that:
1724
* <BR> - it doesn't perform ToObject on the right hand side
1725
* <BR> - it returns true for DontEnum properties.
1726
* @param a the left hand operand
1727
* @param b the right hand operand
1729
* @return true if property name or element number a is a property of b
1731
public static boolean in(Object a, Object b, Scriptable scope) {
1732
if (!(b instanceof Scriptable)) {
1733
throw NativeGlobal.typeError0("msg.instanceof.not.object", scope);
1735
String s = getStringId(a);
1737
? ScriptableObject.hasProperty((Scriptable) b, s)
1738
: ScriptableObject.hasProperty((Scriptable) b, getIntId(a));
1741
public static Boolean cmp_LTB(Object val1, Object val2) {
1742
if (cmp_LT(val1, val2) == 1)
1743
return Boolean.TRUE;
1745
return Boolean.FALSE;
1748
public static int cmp_LT(Object val1, Object val2) {
1749
if (val1 instanceof Scriptable)
1750
val1 = ((Scriptable) val1).getDefaultValue(NumberClass);
1751
if (val2 instanceof Scriptable)
1752
val2 = ((Scriptable) val2).getDefaultValue(NumberClass);
1753
if (!(val1 instanceof String) || !(val2 instanceof String)) {
1754
double d1 = toNumber(val1);
1757
double d2 = toNumber(val2);
1760
return d1 < d2 ? 1 : 0;
1762
return toString(val1).compareTo(toString(val2)) < 0 ? 1 : 0;
1765
public static Boolean cmp_LEB(Object val1, Object val2) {
1766
if (cmp_LE(val1, val2) == 1)
1767
return Boolean.TRUE;
1769
return Boolean.FALSE;
1772
public static int cmp_LE(Object val1, Object val2) {
1773
if (val1 instanceof Scriptable)
1774
val1 = ((Scriptable) val1).getDefaultValue(NumberClass);
1775
if (val2 instanceof Scriptable)
1776
val2 = ((Scriptable) val2).getDefaultValue(NumberClass);
1777
if (!(val1 instanceof String) || !(val2 instanceof String)) {
1778
double d1 = toNumber(val1);
1781
double d2 = toNumber(val2);
1784
return d1 <= d2 ? 1 : 0;
1786
return toString(val1).compareTo(toString(val2)) <= 0 ? 1 : 0;
1790
// implement the '<' operator inline in the caller
1791
// as "compare(val1, val2) == 1"
1794
// implement the '<=' operator inline in the caller
1795
// as "compare(val2, val1) == 0"
1798
// implement the '>' operator inline in the caller
1799
// as "compare(val2, val1) == 1"
1802
// implement the '>=' operator inline in the caller
1803
// as "compare(val1, val2) == 0"
1805
// ------------------
1807
// ------------------
1809
private static final String GLOBAL_CLASS =
1810
"org.mozilla.javascript.tools.shell.Global";
1812
private static ScriptableObject getGlobal(Context cx) {
1814
Class globalClass = loadClassName(GLOBAL_CLASS);
1815
Class[] parm = { Context.class };
1816
Constructor globalClassCtor = globalClass.getConstructor(parm);
1817
Object[] arg = { cx };
1818
return (ScriptableObject) globalClassCtor.newInstance(arg);
1819
} catch (ClassNotFoundException e) {
1821
} catch (NoSuchMethodException e) {
1823
} catch (InvocationTargetException e) {
1825
} catch (IllegalAccessException e) {
1827
} catch (InstantiationException e) {
1830
return new ImporterTopLevel(cx);
1833
public static void main(String scriptClassName, String[] args)
1834
throws JavaScriptException
1836
Context cx = Context.enter();
1837
ScriptableObject global = getGlobal(cx);
1839
// get the command line arguments and define "arguments"
1840
// array in the top-level object
1841
Scriptable argsObj = cx.newArray(global, args);
1842
global.defineProperty("arguments", argsObj,
1843
ScriptableObject.DONTENUM);
1846
Class cl = loadClassName(scriptClassName);
1847
Script script = (Script) cl.newInstance();
1848
script.exec(cx, global);
1851
catch (ClassNotFoundException e) {
1853
catch (InstantiationException e) {
1855
catch (IllegalAccessException e) {
1860
throw new RuntimeException("Error creating script object");
1863
public static Scriptable initScript(Context cx, Scriptable scope,
1864
NativeFunction funObj,
1866
boolean fromEvalCode)
1868
String[] argNames = funObj.argNames;
1869
if (argNames != null) {
1870
ScriptableObject so;
1872
/* Global var definitions are supposed to be DONTDELETE
1873
* so we try to create them that way by hoping that the
1874
* scope is a ScriptableObject which provides access to
1875
* setting the attributes.
1877
so = (ScriptableObject) scope;
1878
} catch (ClassCastException x) {
1879
// oh well, we tried.
1883
Scriptable varScope = scope;
1885
// When executing an eval() inside a with statement,
1886
// define any variables resulting from var statements
1887
// in the first non-with scope. See bug 38590.
1889
while (varScope instanceof NativeWith)
1890
varScope = varScope.getParentScope();
1892
for (int i = argNames.length; i-- != 0;) {
1893
String name = argNames[i];
1894
// Don't overwrite existing def if already defined in object
1895
// or prototypes of object.
1896
if (!hasProp(scope, name)) {
1897
if (so != null && !fromEvalCode)
1898
so.defineProperty(name, Undefined.instance,
1899
ScriptableObject.PERMANENT);
1901
varScope.put(name, varScope, Undefined.instance);
1909
public static Scriptable runScript(Script script) {
1910
Context cx = Context.enter();
1911
ScriptableObject global = getGlobal(cx);
1913
script.exec(cx, global);
1914
} catch (JavaScriptException e) {
1915
throw new Error(e.toString());
1922
public static Scriptable initVarObj(Context cx, Scriptable scope,
1923
NativeFunction funObj,
1924
Scriptable thisObj, Object[] args)
1926
NativeCall result = new NativeCall(cx, scope, funObj, thisObj, args);
1927
String[] argNames = funObj.argNames;
1928
if (argNames != null) {
1929
for (int i = funObj.argCount; i != argNames.length; i++) {
1930
String name = argNames[i];
1931
result.put(name, result, Undefined.instance);
1937
public static void popActivation(Context cx) {
1938
NativeCall current = cx.currentActivation;
1939
if (current != null) {
1940
cx.currentActivation = current.caller;
1941
current.caller = null;
1945
public static Scriptable newScope() {
1946
return new NativeObject();
1949
public static Scriptable enterWith(Object value, Scriptable scope) {
1950
return new NativeWith(scope, toObject(scope, value));
1953
public static Scriptable leaveWith(Scriptable scope) {
1954
return scope.getParentScope();
1957
public static NativeFunction initFunction(NativeFunction fn,
1963
fn.setPrototype(ScriptableObject.getClassPrototype(scope, "Function"));
1964
fn.setParentScope(scope);
1966
setName(scope, fn, scope, fnName);
1970
public static NativeFunction createFunctionObject(Scriptable scope,
1971
Class functionClass,
1975
Constructor[] ctors = functionClass.getConstructors();
1977
NativeFunction result = null;
1978
Object[] initArgs = { scope, cx };
1980
result = (NativeFunction) ctors[0].newInstance(initArgs);
1982
catch (InstantiationException e) {
1983
throw WrappedException.wrapException(e);
1985
catch (IllegalAccessException e) {
1986
throw WrappedException.wrapException(e);
1988
catch (IllegalArgumentException e) {
1989
throw WrappedException.wrapException(e);
1991
catch (InvocationTargetException e) {
1992
throw WrappedException.wrapException(e);
1995
result.setPrototype(ScriptableObject.getClassPrototype(scope, "Function"));
1996
result.setParentScope(scope);
1998
String fnName = result.getFunctionName();
1999
if (setName && fnName != null && fnName.length() != 0 &&
2000
!fnName.equals("anonymous"))
2002
setProp(scope, fnName, result, scope);
2008
static void checkDeprecated(Context cx, String name) {
2009
int version = cx.getLanguageVersion();
2010
if (version >= Context.VERSION_1_4 || version == Context.VERSION_DEFAULT) {
2011
String msg = getMessage1("msg.deprec.ctor", name);
2012
if (version == Context.VERSION_DEFAULT)
2013
Context.reportWarning(msg);
2015
throw Context.reportRuntimeError(msg);
2019
public static String getMessage0(String messageId) {
2020
return Context.getMessage0(messageId);
2023
public static String getMessage1(String messageId, Object arg1) {
2024
return Context.getMessage1(messageId, arg1);
2027
public static String getMessage2
2028
(String messageId, Object arg1, Object arg2)
2030
return Context.getMessage2(messageId, arg1, arg2);
2033
public static String getMessage(String messageId, Object[] arguments) {
2034
return Context.getMessage(messageId, arguments);
2037
public static RegExpProxy getRegExpProxy(Context cx) {
2038
return cx.getRegExpProxy();
2041
public static NativeCall getCurrentActivation(Context cx) {
2042
return cx.currentActivation;
2045
public static void setCurrentActivation(Context cx,
2046
NativeCall activation)
2048
cx.currentActivation = activation;
2051
public static Class loadClassName(String className)
2052
throws ClassNotFoundException
2055
ClassLoader cl = DefiningClassLoader.getContextClassLoader();
2057
return cl.loadClass(className);
2058
} catch (SecurityException e) {
2060
} catch (ClassNotFoundException e) {
2061
// Rather than just letting the exception propagate
2062
// we'll try Class.forName as well. The results could be
2063
// different if this class was loaded on a different
2064
// thread than the current thread.
2065
// So fall through...
2067
return Class.forName(className);
2070
static boolean hasProp(Scriptable start, String name) {
2071
Scriptable m = start;
2073
if (m.has(name, start))
2075
m = m.getPrototype();
2076
} while (m != null);
2080
private static RuntimeException errorWithClassName(String msg, Object val)
2082
return Context.reportRuntimeError1(msg, val.getClass().getName());
2085
public static final Object[] emptyArgs = new Object[0];
2091
* This is the enumeration needed by the for..in statement.
2095
* IdEnumeration maintains a Hashtable to make sure a given
2096
* id is enumerated only once across multiple objects in a
2099
* XXX - ECMA delete doesn't hide properties in the prototype,
2100
* but js/ref does. This means that the js/ref for..in can
2101
* avoid maintaining a hash table and instead perform lookups
2102
* to see if a given property has already been enumerated.
2105
class IdEnumeration implements Enumeration {
2106
IdEnumeration(Scriptable m) {
2107
used = new Hashtable(27);
2112
public boolean hasMoreElements() {
2113
return next != null;
2116
public Object nextElement() {
2117
Object result = next;
2119
// only key used; 'next' as value for convenience
2120
used.put(next, next);
2126
private void changeObject(Scriptable m) {
2130
if (array.length == 0)
2131
changeObject(obj.getPrototype());
2136
private Object getNext() {
2141
if (index == array.length) {
2142
changeObject(obj.getPrototype());
2146
result = array[index++];
2147
if (result instanceof String) {
2148
if (!obj.has((String) result, obj))
2149
continue; // must have been deleted
2151
if (!obj.has(((Number) result).intValue(), obj))
2152
continue; // must have been deleted
2154
if (!used.containsKey(result)) {
2158
return ScriptRuntime.toString(result);
2161
private Object next;
2162
private Scriptable obj;
2164
private Object[] array;
2165
private Hashtable used;
1
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3
* The contents of this file are subject to the Netscape Public
4
* License Version 1.1 (the "License"); you may not use this file
5
* except in compliance with the License. You may obtain a copy of
6
* the License at http://www.mozilla.org/NPL/
8
* Software distributed under the License is distributed on an "AS
9
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
10
* implied. See the License for the specific language governing
11
* rights and limitations under the License.
13
* The Original Code is Rhino code, released
16
* The Initial Developer of the Original Code is Netscape
17
* Communications Corporation. Portions created by Netscape are
18
* Copyright (C) 1997-2000 Netscape Communications Corporation. All
32
* Alternatively, the contents of this file may be used under the
33
* terms of the GNU Public License (the "GPL"), in which case the
34
* provisions of the GPL are applicable instead of those above.
35
* If you wish to allow use of your version of this file only
36
* under the terms of the GPL and not to allow others to use your
37
* version of this file under the NPL, indicate your decision by
38
* deleting the provisions above and replace them with the notice
39
* and other provisions required by the GPL. If you do not delete
40
* the provisions above, a recipient may use your version of this
41
* file under either the NPL or the GPL.
44
package org.mozilla.javascript;
46
import java.lang.reflect.*;
47
import java.text.MessageFormat;
48
import java.util.Locale;
49
import java.util.ResourceBundle;
51
import org.mozilla.javascript.xml.XMLObject;
52
import org.mozilla.javascript.xml.XMLLib;
53
import org.mozilla.javascript.continuations.Continuation;
56
* This is the class that implements the runtime.
61
public class ScriptRuntime {
64
* No instances should be created.
66
protected ScriptRuntime() {
70
* There's such a huge space (and some time) waste for the Foo.class
71
* syntax: the compiler sticks in a test of a static field in the
72
* enclosing class for null and the code for creating the class value.
73
* It has to do this since the reference has to get pushed off til
74
* executiontime (i.e. can't force an early load), but for the
75
* 'standard' classes - especially those in java.lang, we can trust
76
* that they won't cause problems by being loaded early.
79
public final static Class
80
BooleanClass = Kit.classOrNull("java.lang.Boolean"),
81
ByteClass = Kit.classOrNull("java.lang.Byte"),
82
CharacterClass = Kit.classOrNull("java.lang.Character"),
83
ClassClass = Kit.classOrNull("java.lang.Class"),
84
DoubleClass = Kit.classOrNull("java.lang.Double"),
85
FloatClass = Kit.classOrNull("java.lang.Float"),
86
IntegerClass = Kit.classOrNull("java.lang.Integer"),
87
LongClass = Kit.classOrNull("java.lang.Long"),
88
NumberClass = Kit.classOrNull("java.lang.Number"),
89
ObjectClass = Kit.classOrNull("java.lang.Object"),
90
ShortClass = Kit.classOrNull("java.lang.Short"),
91
StringClass = Kit.classOrNull("java.lang.String"),
93
SerializableClass = Kit.classOrNull("java.io.Serializable"),
95
DateClass = Kit.classOrNull("java.util.Date");
97
public final static Class
98
ContextClass = Kit.classOrNull("org.mozilla.javascript.Context"),
99
FunctionClass = Kit.classOrNull("org.mozilla.javascript.Function"),
100
ScriptableClass = Kit.classOrNull("org.mozilla.javascript.Scriptable"),
101
ScriptableObjectClass = Kit.classOrNull("org.mozilla.javascript.ScriptableObject"),
102
UndefinedClass = Kit.classOrNull("org.mozilla.javascript.Undefined");
104
private static final String
105
XML_INIT_CLASS = "org.mozilla.javascript.xmlimpl.XMLLibImpl";
107
private static final String[] lazilyNames = {
108
"RegExp", "org.mozilla.javascript.regexp.NativeRegExp",
109
"Packages", "org.mozilla.javascript.NativeJavaTopPackage",
110
"java", "org.mozilla.javascript.NativeJavaTopPackage",
111
"getClass", "org.mozilla.javascript.NativeJavaTopPackage",
112
"JavaAdapter", "org.mozilla.javascript.JavaAdapter",
113
"JavaImporter", "org.mozilla.javascript.ImporterTopLevel",
114
"XML", XML_INIT_CLASS,
115
"XMLList", XML_INIT_CLASS,
116
"Namespace", XML_INIT_CLASS,
117
"QName", XML_INIT_CLASS,
120
private static final Object LIBRARY_SCOPE_KEY = new Object();
121
private static final Object CONTEXT_FACTORY_KEY = new Object();
123
public static ScriptableObject initStandardObjects(Context cx,
124
ScriptableObject scope,
128
scope = new NativeObject();
130
scope.associateValue(LIBRARY_SCOPE_KEY, scope);
131
ContextFactory factory = cx.getFactory();
132
if (factory == null) {
133
// factory is null for Context asociated with the current thread
134
// via Context.enter()
135
factory = ContextFactory.getGlobal();
137
scope.associateValue(CONTEXT_FACTORY_KEY, factory);
138
(new ClassCache()).associate(scope);
140
BaseFunction.init(cx, scope, sealed);
141
NativeObject.init(cx, scope, sealed);
143
Scriptable objectProto = ScriptableObject.getObjectPrototype(scope);
145
// Function.prototype.__proto__ should be Object.prototype
146
Scriptable functionProto = ScriptableObject.getFunctionPrototype(scope);
147
functionProto.setPrototype(objectProto);
149
// Set the prototype of the object passed in if need be
150
if (scope.getPrototype() == null)
151
scope.setPrototype(objectProto);
153
// must precede NativeGlobal since it's needed therein
154
NativeError.init(cx, scope, sealed);
155
NativeGlobal.init(cx, scope, sealed);
157
NativeArray.init(cx, scope, sealed);
158
NativeString.init(cx, scope, sealed);
159
NativeBoolean.init(cx, scope, sealed);
160
NativeNumber.init(cx, scope, sealed);
161
NativeDate.init(cx, scope, sealed);
162
NativeMath.init(cx, scope, sealed);
164
NativeWith.init(cx, scope, sealed);
165
NativeCall.init(cx, scope, sealed);
166
NativeScript.init(cx, scope, sealed);
168
boolean withXml = cx.hasFeature(Context.FEATURE_E4X);
170
for (int i = 0; i != lazilyNames.length; i += 2) {
171
String topProperty = lazilyNames[i];
172
String className = lazilyNames[i + 1];
173
if (!withXml && className == XML_INIT_CLASS) {
176
new LazilyLoadedCtor(scope, topProperty, className, sealed);
179
Continuation.init(cx, scope, sealed);
184
public static ScriptableObject getLibraryScopeOrNull(Scriptable scope)
186
ScriptableObject libScope;
187
libScope = (ScriptableObject)ScriptableObject.
188
getTopScopeValue(scope, LIBRARY_SCOPE_KEY);
192
public static ContextFactory getContextFactory(Scriptable scope)
194
ContextFactory factory;
195
factory = (ContextFactory)ScriptableObject.
196
getTopScopeValue(scope, CONTEXT_FACTORY_KEY);
197
if (factory == null) {
198
throw new IllegalStateException("Failed to find ContextFactory");
203
// It is public so NativeRegExp can access it .
204
public static boolean isJSLineTerminator(int c)
206
// Optimization for faster check for eol character:
207
// they do not have 0xDFD0 bits set
208
if ((c & 0xDFD0) != 0) {
211
return c == '\n' || c == '\r' || c == 0x2028 || c == 0x2029;
214
public static Boolean wrapBoolean(boolean b)
216
return b ? Boolean.TRUE : Boolean.FALSE;
219
public static Integer wrapInt(int i)
221
return new Integer(i);
224
public static Number wrapNumber(double x)
227
return ScriptRuntime.NaNobj;
229
return new Double(x);
233
* Convert the value to a boolean.
237
public static boolean toBoolean(Object val) {
240
if (val instanceof Boolean)
241
return ((Boolean) val).booleanValue();
242
if (val instanceof Scriptable) {
243
if (Context.getContext().isVersionECMA1()) {
245
return val != Undefined.instance;
248
val = ((Scriptable) val).getDefaultValue(BooleanClass);
249
if (val instanceof Scriptable)
250
throw errorWithClassName("msg.primitive.expected", val);
251
if (val instanceof Boolean)
252
return ((Boolean) val).booleanValue();
255
if (val instanceof String)
256
return ((String) val).length() != 0;
257
if (val instanceof Number) {
258
double d = ((Number) val).doubleValue();
259
return (d == d && d != 0.0);
262
warnAboutNonJSObject(val);
266
public static boolean toBoolean(Object[] args, int index) {
267
return (index < args.length) ? toBoolean(args[index]) : false;
270
* Convert the value to a number.
274
public static double toNumber(Object val)
276
if (val instanceof Number)
277
return ((Number) val).doubleValue();
280
if (val instanceof Scriptable) {
281
val = ((Scriptable) val).getDefaultValue(NumberClass);
282
if (val != null && val instanceof Scriptable)
283
throw errorWithClassName("msg.primitive.expected", val);
284
if (val instanceof Number)
285
return ((Number) val).doubleValue();
288
if (val instanceof String)
289
return toNumber((String) val);
290
if (val instanceof Boolean)
291
return ((Boolean) val).booleanValue() ? 1 : +0.0;
293
warnAboutNonJSObject(val);
297
public static double toNumber(Object[] args, int index) {
298
return (index < args.length) ? toNumber(args[index]) : NaN;
301
// Can not use Double.NaN defined as 0.0d / 0.0 as under the Microsoft VM,
302
// versions 2.01 and 3.0P1, that causes some uses (returns at least) of
303
// Double.NaN to be converted to 1.0.
304
// So we use ScriptRuntime.NaN instead of Double.NaN.
305
public static final double
306
NaN = Double.longBitsToDouble(0x7ff8000000000000L);
308
// A similar problem exists for negative zero.
309
public static final double
310
negativeZero = Double.longBitsToDouble(0x8000000000000000L);
312
public static final Double NaNobj = new Double(NaN);
315
* Helper function for toNumber, parseInt, and TokenStream.getToken.
317
static double stringToNumber(String s, int start, int radix) {
319
char lowerCaseBound = 'a';
320
char upperCaseBound = 'A';
321
int len = s.length();
323
digitMax = (char) ('0' + radix - 1);
326
lowerCaseBound = (char) ('a' + radix - 10);
327
upperCaseBound = (char) ('A' + radix - 10);
331
for (end=start; end < len; end++) {
332
char c = s.charAt(end);
334
if ('0' <= c && c <= digitMax)
336
else if ('a' <= c && c < lowerCaseBound)
337
newDigit = c - 'a' + 10;
338
else if ('A' <= c && c < upperCaseBound)
339
newDigit = c - 'A' + 10;
342
sum = sum*radix + newDigit;
347
if (sum >= 9007199254740992.0) {
349
/* If we're accumulating a decimal number and the number
350
* is >= 2^53, then the result from the repeated multiply-add
351
* above may be inaccurate. Call Java to get the correct
355
return Double.valueOf(s.substring(start, end)).doubleValue();
356
} catch (NumberFormatException nfe) {
359
} else if (radix == 2 || radix == 4 || radix == 8 ||
360
radix == 16 || radix == 32)
362
/* The number may also be inaccurate for one of these bases.
363
* This happens if the addition in value*radix + digit causes
364
* a round-down to an even least significant mantissa bit
365
* when the first dropped bit is a one. If any of the
366
* following digits in the number (which haven't been added
367
* in yet) are nonzero then the correct action would have
368
* been to round up instead of down. An example of this
369
* occurs when reading the number 0x1000000000000081, which
370
* rounds to 0x1000000000000000 instead of 0x1000000000000100.
372
int bitShiftInChar = 1;
375
final int SKIP_LEADING_ZEROS = 0;
376
final int FIRST_EXACT_53_BITS = 1;
377
final int AFTER_BIT_53 = 2;
378
final int ZEROS_AFTER_54 = 3;
379
final int MIXED_AFTER_54 = 4;
381
int state = SKIP_LEADING_ZEROS;
382
int exactBitsLimit = 53;
384
boolean bit53 = false;
385
// bit54 is the 54th bit (the first dropped from the mantissa)
386
boolean bit54 = false;
389
if (bitShiftInChar == 1) {
392
digit = s.charAt(start++);
393
if ('0' <= digit && digit <= '9')
395
else if ('a' <= digit && digit <= 'z')
399
bitShiftInChar = radix;
401
bitShiftInChar >>= 1;
402
boolean bit = (digit & bitShiftInChar) != 0;
405
case SKIP_LEADING_ZEROS:
409
state = FIRST_EXACT_53_BITS;
412
case FIRST_EXACT_53_BITS:
417
if (exactBitsLimit == 0) {
419
state = AFTER_BIT_53;
425
state = ZEROS_AFTER_54;
429
state = MIXED_AFTER_54;
438
case SKIP_LEADING_ZEROS:
441
case FIRST_EXACT_53_BITS:
446
// x1.1 -> x1 + 1 (round up)
447
// x0.1 -> x0 (round down)
453
// x.100...1.. -> x + 1 (round up)
454
// x.0anything -> x (round down)
461
/* We don't worry about inaccurate numbers for any other base. */
468
* ToNumber applied to the String type
472
public static double toNumber(String s) {
473
int len = s.length();
478
// Empty or contains only whitespace
481
startChar = s.charAt(start);
482
if (!Character.isWhitespace(startChar))
487
if (startChar == '0') {
488
if (start + 2 < len) {
489
int c1 = s.charAt(start + 1);
490
if (c1 == 'x' || c1 == 'X') {
491
// A hexadecimal number
492
return stringToNumber(s, start + 2, 16);
495
} else if (startChar == '+' || startChar == '-') {
496
if (start + 3 < len && s.charAt(start + 1) == '0') {
497
int c2 = s.charAt(start + 2);
498
if (c2 == 'x' || c2 == 'X') {
499
// A hexadecimal number with sign
500
double val = stringToNumber(s, start + 3, 16);
501
return startChar == '-' ? -val : val;
508
while (Character.isWhitespace(endChar = s.charAt(end)))
510
if (endChar == 'y') {
511
// check for "Infinity"
512
if (startChar == '+' || startChar == '-')
514
if (start + 7 == end && s.regionMatches(start, "Infinity", 0, 8))
515
return startChar == '-'
516
? Double.NEGATIVE_INFINITY
517
: Double.POSITIVE_INFINITY;
520
// A non-hexadecimal, non-infinity number:
521
// just try a normal floating point conversion
522
String sub = s.substring(start, end+1);
523
if (MSJVM_BUG_WORKAROUNDS) {
524
// The MS JVM will accept non-conformant strings
525
// rather than throwing a NumberFormatException
527
for (int i=sub.length()-1; i >= 0; i--) {
528
char c = sub.charAt(i);
529
if (('0' <= c && c <= '9') || c == '.' ||
530
c == 'e' || c == 'E' ||
531
c == '+' || c == '-')
537
return Double.valueOf(sub).doubleValue();
538
} catch (NumberFormatException ex) {
544
* Helper function for builtin objects that use the varargs form.
545
* ECMA function formal arguments are undefined if not supplied;
546
* this function pads the argument array out to the expected
547
* length, if necessary.
549
public static Object[] padArguments(Object[] args, int count) {
550
if (count < args.length)
554
Object[] result = new Object[count];
555
for (i = 0; i < args.length; i++) {
559
for (; i < count; i++) {
560
result[i] = Undefined.instance;
566
/* Work around Microsoft Java VM bugs. */
567
private final static boolean MSJVM_BUG_WORKAROUNDS = true;
569
public static String escapeString(String s)
571
return escapeString(s, '"');
575
* For escaping strings printed by object and array literals; not quite
576
* the same as 'escape.'
578
public static String escapeString(String s, char escapeQuote)
580
if (!(escapeQuote == '"' || escapeQuote == '\'')) Kit.codeBug();
581
StringBuffer sb = null;
583
for(int i = 0, L = s.length(); i != L; ++i) {
586
if (' ' <= c && c <= '~' && c != escapeQuote && c != '\\') {
587
// an ordinary print character (like C isprint()) and not "
595
sb = new StringBuffer(L + 3);
602
case '\b': escape = 'b'; break;
603
case '\f': escape = 'f'; break;
604
case '\n': escape = 'n'; break;
605
case '\r': escape = 'r'; break;
606
case '\t': escape = 't'; break;
607
case 0xb: escape = 'v'; break; // Java lacks \v.
608
case ' ': escape = ' '; break;
609
case '\\': escape = '\\'; break;
612
// an \escaped sort of character
614
sb.append((char)escape);
615
} else if (c == escapeQuote) {
617
sb.append(escapeQuote);
629
// append hexadecimal form of c left-padded with 0
630
for (int shift = (hexSize - 1) * 4; shift >= 0; shift -= 4) {
631
int digit = 0xf & (c >> shift);
632
int hc = (digit < 10) ? '0' + digit : 'a' - 10 + digit;
637
return (sb == null) ? s : sb.toString();
640
static boolean isValidIdentifierName(String s)
645
if (!Character.isJavaIdentifierStart(s.charAt(0)))
647
for (int i = 1; i != L; ++i) {
648
if (!Character.isJavaIdentifierPart(s.charAt(i)))
651
return !TokenStream.isKeyword(s);
655
* Convert the value to a string.
659
public static String toString(Object val) {
663
if (val instanceof Scriptable) {
664
val = ((Scriptable) val).getDefaultValue(StringClass);
665
if (val != Undefined.instance && val instanceof Scriptable) {
666
throw errorWithClassName("msg.primitive.expected", val);
670
if (val instanceof Number) {
671
// XXX should we just teach NativeNumber.stringValue()
673
return numberToString(((Number) val).doubleValue(), 10);
675
return val.toString();
679
static String defaultObjectToString(Scriptable obj)
681
return "[object " + obj.getClassName() + "]";
684
public static String toString(Object[] args, int index) {
685
return (index < args.length) ? toString(args[index]) : "undefined";
689
* Optimized version of toString(Object) for numbers.
691
public static String toString(double val) {
692
return numberToString(val, 10);
695
public static String numberToString(double d, int base) {
698
if (d == Double.POSITIVE_INFINITY)
700
if (d == Double.NEGATIVE_INFINITY)
705
if ((base < 2) || (base > 36)) {
706
throw Context.reportRuntimeError1(
707
"msg.bad.radix", Integer.toString(base));
711
return DToA.JS_dtobasestr(base, d);
713
StringBuffer result = new StringBuffer();
714
DToA.JS_dtostr(result, DToA.DTOSTR_STANDARD, 0, d);
715
return result.toString();
720
static String uneval(Context cx, Scriptable scope, Object value)
725
if (value instanceof String) {
726
String escaped = escapeString((String)value);
727
StringBuffer sb = new StringBuffer(escaped.length() + 2);
731
return sb.toString();
733
if (value instanceof Number) {
734
double d = ((Number)value).doubleValue();
735
if (d == 0 && 1 / d < 0) {
740
if (value instanceof Boolean) {
741
return toString(value);
743
if (value == Undefined.instance) {
746
if (value instanceof Scriptable) {
747
Scriptable obj = (Scriptable)value;
748
Object v = ScriptableObject.getProperty(obj, "toSource");
749
if (v instanceof Function) {
750
Function f = (Function)v;
751
return toString(f.call(cx, scope, obj, emptyArgs));
753
return toString(value);
755
warnAboutNonJSObject(value);
756
return value.toString();
759
static String defaultObjectToSource(Context cx, Scriptable scope,
760
Scriptable thisObj, Object[] args)
762
boolean toplevel, iterating;
763
if (cx.iterating == null) {
766
cx.iterating = new ObjToIntMap(31);
769
iterating = cx.iterating.has(thisObj);
772
StringBuffer result = new StringBuffer(128);
778
// Make sure cx.iterating is set to null when done
779
// so we don't leak memory
782
cx.iterating.intern(thisObj); // stop recursion.
783
Object[] ids = thisObj.getIds();
784
for(int i=0; i < ids.length; i++) {
789
if (id instanceof Integer) {
790
int intId = ((Integer)id).intValue();
791
value = thisObj.get(intId, thisObj);
792
result.append(intId);
794
String strId = (String)id;
795
value = thisObj.get(strId, thisObj);
796
if (ScriptRuntime.isValidIdentifierName(strId)) {
797
result.append(strId);
801
ScriptRuntime.escapeString(strId, '\''));
806
result.append(ScriptRuntime.uneval(cx, scope, value));
819
return result.toString();
822
public static Scriptable toObject(Scriptable scope, Object val)
824
if (val instanceof Scriptable && val != Undefined.instance) {
825
return (Scriptable)val;
827
return toObject(Context.getContext(), scope, val);
830
public static Scriptable toObjectOrNull(Context cx, Object obj)
832
if (obj instanceof Scriptable) {
833
Scriptable sobj = (Scriptable)obj;
834
if (sobj != Undefined.instance) {
837
} else if (obj != null) {
838
return toObject(cx, getTopCallScope(cx), obj);
844
* @deprecated Use {@link #toObject(Scriptable, Object)} instead.
846
public static Scriptable toObject(Scriptable scope, Object val,
849
if (val instanceof Scriptable && val != Undefined.instance) {
850
return (Scriptable)val;
852
return toObject(Context.getContext(), scope, val);
856
* Convert the value to an object.
860
public static Scriptable toObject(Context cx, Scriptable scope, Object val)
862
if (val instanceof Scriptable) {
863
if (val == Undefined.instance) {
864
throw typeError0("msg.undef.to.object");
866
return (Scriptable) val;
869
throw typeError0("msg.null.to.object");
872
String className = val instanceof String ? "String" :
873
val instanceof Number ? "Number" :
874
val instanceof Boolean ? "Boolean" :
876
if (className != null) {
877
Object[] args = { val };
878
scope = ScriptableObject.getTopLevelScope(scope);
879
return newObject(cx, scope, className, args);
882
// Extension: Wrap as a LiveConnect object.
883
Object wrapped = cx.getWrapFactory().wrap(cx, scope, val, null);
884
if (wrapped instanceof Scriptable)
885
return (Scriptable) wrapped;
886
throw errorWithClassName("msg.invalid.type", val);
890
* @deprecated Use {@link #toObject(Context, Scriptable, Object)} instead.
892
public static Scriptable toObject(Context cx, Scriptable scope, Object val,
895
return toObject(cx, scope, val);
899
* @deprecated The method is only present for compatibility.
901
public static Object call(Context cx, Object fun, Object thisArg,
902
Object[] args, Scriptable scope)
904
if (!(fun instanceof Function)) {
905
throw notFunctionError(toString(fun));
907
Function function = (Function)fun;
908
Scriptable thisObj = toObjectOrNull(cx, thisArg);
909
if (thisObj == null) {
910
throw undefCallError(thisObj, "function");
912
return function.call(cx, scope, thisObj, args);
915
public static Scriptable newObject(Context cx, Scriptable scope,
916
String constructorName, Object[] args)
918
scope = ScriptableObject.getTopLevelScope(scope);
919
Function ctor = getExistingCtor(cx, scope, constructorName);
920
if (args == null) { args = ScriptRuntime.emptyArgs; }
921
return ctor.construct(cx, scope, args);
928
public static double toInteger(Object val) {
929
return toInteger(toNumber(val));
932
// convenience method
933
public static double toInteger(double d) {
939
d == Double.POSITIVE_INFINITY ||
940
d == Double.NEGATIVE_INFINITY)
944
return Math.floor(d);
949
public static double toInteger(Object[] args, int index) {
950
return (index < args.length) ? toInteger(args[index]) : +0.0;
957
public static int toInt32(Object val)
959
// short circuit for common integer values
960
if (val instanceof Integer)
961
return ((Integer)val).intValue();
963
return toInt32(toNumber(val));
966
public static int toInt32(Object[] args, int index) {
967
return (index < args.length) ? toInt32(args[index]) : 0;
970
public static int toInt32(double d) {
973
// This covers -0.0 as well
978
|| d == Double.POSITIVE_INFINITY
979
|| d == Double.NEGATIVE_INFINITY)
984
d = (d >= 0) ? Math.floor(d) : Math.ceil(d);
986
double two32 = 4294967296.0;
987
d = Math.IEEEremainder(d, two32);
988
// (double)(long)d == d should hold here
991
// returning (int)d does not work as d can be outside int range
992
// but the result must always be 32 lower bits of l
998
* @return long value representing 32 bits unsigned integer
1000
public static long toUint32(double d) {
1003
// This covers -0.0 as well
1004
return l & 0xffffffffL;
1008
|| d == Double.POSITIVE_INFINITY
1009
|| d == Double.NEGATIVE_INFINITY)
1014
d = (d >= 0) ? Math.floor(d) : Math.ceil(d);
1016
// 0x100000000 gives me a numeric overflow...
1017
double two32 = 4294967296.0;
1018
l = (long)Math.IEEEremainder(d, two32);
1020
return l & 0xffffffffL;
1023
public static long toUint32(Object val) {
1024
return toUint32(toNumber(val));
1031
public static char toUint16(Object val) {
1032
double d = toNumber(val);
1040
|| d == Double.POSITIVE_INFINITY
1041
|| d == Double.NEGATIVE_INFINITY)
1046
d = (d >= 0) ? Math.floor(d) : Math.ceil(d);
1048
int int16 = 0x10000;
1049
i = (int)Math.IEEEremainder(d, int16);
1054
// XXX: this is until setDefaultNamespace will learn how to store NS
1055
// properly and separates namespace form Scriptable.get etc.
1056
private static final String DEFAULT_NS_TAG = "__default_namespace__";
1058
public static Object setDefaultNamespace(Object namespace, Context cx)
1060
Scriptable scope = cx.currentActivationCall;
1061
if (scope == null) {
1062
scope = getTopCallScope(cx);
1065
XMLLib xmlLib = currentXMLLib(cx);
1066
Object ns = xmlLib.toDefaultXmlNamespace(cx, namespace);
1068
// XXX : this should be in separated namesapce from Scriptable.get/put
1069
if (!scope.has(DEFAULT_NS_TAG, scope)) {
1070
// XXX: this is racy of cause
1071
ScriptableObject.defineProperty(scope, DEFAULT_NS_TAG, ns,
1072
ScriptableObject.PERMANENT
1073
| ScriptableObject.DONTENUM);
1075
scope.put(DEFAULT_NS_TAG, scope, ns);
1078
return Undefined.instance;
1081
public static Object searchDefaultNamespace(Context cx)
1083
Scriptable scope = cx.currentActivationCall;
1084
if (scope == null) {
1085
scope = getTopCallScope(cx);
1089
Scriptable parent = scope.getParentScope();
1090
if (parent == null) {
1091
nsObject = ScriptableObject.getProperty(scope, DEFAULT_NS_TAG);
1092
if (nsObject == Scriptable.NOT_FOUND) {
1097
nsObject = scope.get(DEFAULT_NS_TAG, scope);
1098
if (nsObject != Scriptable.NOT_FOUND) {
1106
public static Object getTopLevelProp(Scriptable scope, String id) {
1107
scope = ScriptableObject.getTopLevelScope(scope);
1108
return ScriptableObject.getProperty(scope, id);
1111
static Function getExistingCtor(Context cx, Scriptable scope,
1112
String constructorName)
1114
Object ctorVal = ScriptableObject.getProperty(scope, constructorName);
1115
if (ctorVal instanceof Function) {
1116
return (Function)ctorVal;
1118
if (ctorVal == Scriptable.NOT_FOUND) {
1119
throw cx.reportRuntimeError1("msg.ctor.not.found", constructorName);
1121
throw cx.reportRuntimeError1("msg.not.ctor", constructorName);
1126
* Return -1L if str is not an index or the index value as lower 32
1127
* bits of the result.
1129
private static long indexFromString(String str)
1131
// The length of the decimal string representation of
1132
// Integer.MAX_VALUE, 2147483647
1133
final int MAX_VALUE_LENGTH = 10;
1135
int len = str.length();
1138
boolean negate = false;
1139
int c = str.charAt(0);
1148
if (0 <= c && c <= 9
1149
&& len <= (negate ? MAX_VALUE_LENGTH + 1 : MAX_VALUE_LENGTH))
1151
// Use negative numbers to accumulate index to handle
1152
// Integer.MIN_VALUE that is greater by 1 in absolute value
1153
// then Integer.MAX_VALUE
1158
// Note that 00, 01, 000 etc. are not indexes
1159
while (i != len && 0 <= (c = str.charAt(i) - '0') && c <= 9)
1162
index = 10 * index - c;
1166
// Make sure all characters were consumed and that it couldn't
1169
(oldIndex > (Integer.MIN_VALUE / 10) ||
1170
(oldIndex == (Integer.MIN_VALUE / 10) &&
1171
c <= (negate ? -(Integer.MIN_VALUE % 10)
1172
: (Integer.MAX_VALUE % 10)))))
1174
return 0xFFFFFFFFL & (negate ? index : -index);
1182
* If str is a decimal presentation of Uint32 value, return it as long.
1183
* Othewise return -1L;
1185
public static long testUint32String(String str)
1187
// The length of the decimal string representation of
1188
// UINT32_MAX_VALUE, 4294967296
1189
final int MAX_VALUE_LENGTH = 10;
1191
int len = str.length();
1192
if (1 <= len && len <= MAX_VALUE_LENGTH) {
1193
int c = str.charAt(0);
1196
// Note that 00,01 etc. are not valid Uint32 presentations
1197
return (len == 1) ? 0L : -1L;
1199
if (1 <= c && c <= 9) {
1201
for (int i = 1; i != len; ++i) {
1202
c = str.charAt(i) - '0';
1203
if (!(0 <= c && c <= 9)) {
1208
// Check for overflow
1209
if ((v >>> 32) == 0) {
1218
* If s represents index, then return index value wrapped as Integer
1219
* and othewise return s.
1221
static Object getIndexObject(String s)
1223
long indexTest = indexFromString(s);
1224
if (indexTest >= 0) {
1225
return new Integer((int)indexTest);
1231
* If d is exact int value, return its value wrapped as Integer
1232
* and othewise return d converted to String.
1234
static Object getIndexObject(double d)
1237
if ((double)i == d) {
1238
return new Integer((int)i);
1244
* If toString(id) is a decimal presentation of int32 value, then id
1245
* is index. In this case return null and make the index available
1246
* as ScriptRuntime.lastIndexResult(cx). Otherwise return toString(id).
1248
static String toStringIdOrIndex(Context cx, Object id)
1250
if (id instanceof Number) {
1251
double d = ((Number)id).doubleValue();
1253
if (((double)index) == d) {
1254
storeIndexResult(cx, index);
1257
return toString(id);
1260
if (id instanceof String) {
1265
long indexTest = indexFromString(s);
1266
if (indexTest >= 0) {
1267
storeIndexResult(cx, (int)indexTest);
1275
* Call obj.[[Get]](id)
1277
public static Object getObjectElem(Object obj, Object elem, Context cx)
1279
Scriptable sobj = toObjectOrNull(cx, obj);
1281
throw undefReadError(obj, elem);
1283
return getObjectElem(sobj, elem, cx);
1286
public static Object getObjectElem(Scriptable obj, Object elem,
1289
if (obj instanceof XMLObject) {
1290
XMLObject xmlObject = (XMLObject)obj;
1291
return xmlObject.ecmaGet(cx, elem);
1296
String s = toStringIdOrIndex(cx, elem);
1298
int index = lastIndexResult(cx);
1299
result = ScriptableObject.getProperty(obj, index);
1301
result = ScriptableObject.getProperty(obj, s);
1304
if (result == Scriptable.NOT_FOUND) {
1305
result = Undefined.instance;
1312
* Version of getObjectElem when elem is a valid JS identifier name.
1314
public static Object getObjectProp(Object obj, String property,
1317
Scriptable sobj = toObjectOrNull(cx, obj);
1319
throw undefReadError(obj, property);
1321
return getObjectProp(sobj, property, cx);
1324
public static Object getObjectProp(Scriptable obj, String property,
1327
if (obj instanceof XMLObject) {
1328
XMLObject xmlObject = (XMLObject)obj;
1329
return xmlObject.ecmaGet(cx, property);
1332
Object result = ScriptableObject.getProperty(obj, property);
1333
if (result == Scriptable.NOT_FOUND) {
1334
result = Undefined.instance;
1341
* A cheaper and less general version of the above for well-known argument
1344
public static Object getObjectIndex(Object obj, double dblIndex,
1347
Scriptable sobj = toObjectOrNull(cx, obj);
1349
throw undefReadError(obj, toString(dblIndex));
1352
int index = (int)dblIndex;
1353
if ((double)index == dblIndex) {
1354
return getObjectIndex(sobj, index, cx);
1356
String s = toString(dblIndex);
1357
return getObjectProp(sobj, s, cx);
1361
public static Object getObjectIndex(Scriptable obj, int index,
1364
if (obj instanceof XMLObject) {
1365
XMLObject xmlObject = (XMLObject)obj;
1366
return xmlObject.ecmaGet(cx, new Integer(index));
1369
Object result = ScriptableObject.getProperty(obj, index);
1370
if (result == Scriptable.NOT_FOUND) {
1371
result = Undefined.instance;
1378
* Call obj.[[Put]](id, value)
1380
public static Object setObjectElem(Object obj, Object elem, Object value,
1383
Scriptable sobj = toObjectOrNull(cx, obj);
1385
throw undefWriteError(obj, elem, value);
1387
return setObjectElem(sobj, elem, value, cx);
1390
public static Object setObjectElem(Scriptable obj, Object elem,
1391
Object value, Context cx)
1393
if (obj instanceof XMLObject) {
1394
XMLObject xmlObject = (XMLObject)obj;
1395
xmlObject.ecmaPut(cx, elem, value);
1399
String s = toStringIdOrIndex(cx, elem);
1401
int index = lastIndexResult(cx);
1402
ScriptableObject.putProperty(obj, index, value);
1404
ScriptableObject.putProperty(obj, s, value);
1411
* Version of setObjectElem when elem is a valid JS identifier name.
1413
public static Object setObjectProp(Object obj, String property,
1414
Object value, Context cx)
1416
Scriptable sobj = toObjectOrNull(cx, obj);
1418
throw undefWriteError(obj, property, value);
1420
return setObjectProp(sobj, property, value, cx);
1423
public static Object setObjectProp(Scriptable obj, String property,
1424
Object value, Context cx)
1426
if (obj instanceof XMLObject) {
1427
XMLObject xmlObject = (XMLObject)obj;
1428
xmlObject.ecmaPut(cx, property, value);
1430
ScriptableObject.putProperty(obj, property, value);
1436
* A cheaper and less general version of the above for well-known argument
1439
public static Object setObjectIndex(Object obj, double dblIndex,
1440
Object value, Context cx)
1442
Scriptable sobj = toObjectOrNull(cx, obj);
1444
throw undefWriteError(obj, String.valueOf(dblIndex), value);
1447
int index = (int)dblIndex;
1448
if ((double)index == dblIndex) {
1449
return setObjectIndex(sobj, index, value, cx);
1451
String s = toString(dblIndex);
1452
return setObjectProp(sobj, s, value, cx);
1456
public static Object setObjectIndex(Scriptable obj, int index, Object value,
1459
if (obj instanceof XMLObject) {
1460
XMLObject xmlObject = (XMLObject)obj;
1461
xmlObject.ecmaPut(cx, new Integer(index), value);
1463
ScriptableObject.putProperty(obj, index, value);
1468
public static boolean deleteObjectElem(Scriptable target, Object elem,
1472
if (target instanceof XMLObject) {
1473
XMLObject xmlObject = (XMLObject)target;
1474
result = xmlObject.ecmaDelete(cx, elem);
1476
String s = toStringIdOrIndex(cx, elem);
1478
int index = lastIndexResult(cx);
1479
result = ScriptableObject.deleteProperty(target, index);
1481
result = ScriptableObject.deleteProperty(target, s);
1487
public static boolean hasObjectElem(Scriptable target, Object elem,
1492
if (target instanceof XMLObject) {
1493
XMLObject xmlObject = (XMLObject)target;
1494
result = xmlObject.ecmaHas(cx, elem);
1496
String s = toStringIdOrIndex(cx, elem);
1498
int index = lastIndexResult(cx);
1499
result = ScriptableObject.hasProperty(target, index);
1501
result = ScriptableObject.hasProperty(target, s);
1508
public static Object refGet(Ref ref, Scriptable target, Context cx)
1510
return ref.get(cx, target);
1513
public static Object refSet(Ref ref, Scriptable target,
1514
Object value, Context cx)
1516
return ref.set(cx, target, value);
1519
public static Object refDel(Ref ref, Scriptable target, Context cx)
1521
return wrapBoolean(ref.delete(cx, target));
1524
static boolean isSpecialProperty(String s)
1526
return s.equals("__proto__") || s.equals("__parent__");
1529
public static Ref specialRef(Object obj, String specialProperty,
1532
return SpecialRef.createSpecial(cx, obj, specialProperty);
1536
* The delete operator
1540
* In ECMA 0.19, the description of the delete operator (11.4.1)
1541
* assumes that the [[Delete]] method returns a value. However,
1542
* the definition of the [[Delete]] operator (8.6.2.5) does not
1543
* define a return value. Here we assume that the [[Delete]]
1544
* method doesn't return a value.
1546
public static Object delete(Object obj, Object id, Context cx)
1548
Scriptable sobj = toObjectOrNull(cx, obj);
1550
String idStr = (id == null) ? "null" : id.toString();
1551
throw typeError2("msg.undef.prop.delete", toString(obj), idStr);
1553
boolean result = deleteObjectElem(sobj, id, cx);
1554
return wrapBoolean(result);
1558
* Looks up a name in the scope chain and returns its value.
1560
public static Object name(Context cx, Scriptable scope, String name)
1562
Scriptable parent = scope.getParentScope();
1563
if (parent == null) {
1564
Object result = topScopeName(cx, scope, name);
1565
if (result == Scriptable.NOT_FOUND) {
1566
throw notFoundError(scope, name);
1571
return nameOrFunction(cx, scope, parent, name, false);
1574
private static Object nameOrFunction(Context cx, Scriptable scope,
1575
Scriptable parentScope, String name,
1576
boolean asFunctionCall)
1579
Scriptable thisObj = scope; // It is used only if asFunctionCall==true.
1581
XMLObject firstXMLObject = null;
1583
if (scope instanceof NativeWith) {
1584
Scriptable withObj = scope.getPrototype();
1585
if (withObj instanceof XMLObject) {
1586
XMLObject xmlObj = (XMLObject)withObj;
1587
if (xmlObj.ecmaHas(cx, name)) {
1588
// function this should be the target object of with
1590
result = xmlObj.ecmaGet(cx, name);
1593
if (firstXMLObject == null) {
1594
firstXMLObject = xmlObj;
1597
result = ScriptableObject.getProperty(withObj, name);
1598
if (result != Scriptable.NOT_FOUND) {
1599
// function this should be the target object of with
1604
} else if (scope instanceof NativeCall) {
1605
// NativeCall does not prototype chain and Scriptable.get
1606
// can be called directly.
1607
result = scope.get(name, scope);
1608
if (result != Scriptable.NOT_FOUND) {
1609
if (asFunctionCall) {
1610
// ECMA 262 requires that this for nested funtions
1611
// should be top scope
1612
thisObj = ScriptableObject.
1613
getTopLevelScope(parentScope);
1618
// Can happen if Rhino embedding decided that nested
1619
// scopes are useful for what ever reasons.
1620
result = ScriptableObject.getProperty(scope, name);
1621
if (result != Scriptable.NOT_FOUND) {
1626
scope = parentScope;
1627
parentScope = parentScope.getParentScope();
1628
if (parentScope == null) {
1629
result = topScopeName(cx, scope, name);
1630
if (result == Scriptable.NOT_FOUND) {
1631
if (firstXMLObject == null || asFunctionCall) {
1632
throw notFoundError(scope, name);
1634
// The name was not found, but we did find an XML
1635
// object in the scope chain and we are looking for name,
1636
// not function. The result should be an empty XMLList
1638
result = firstXMLObject.ecmaGet(cx, name);
1640
// For top scope thisObj for functions is always scope itself.
1646
if (asFunctionCall) {
1647
if (!(result instanceof Function)) {
1648
throw notFunctionError(result, name);
1650
storeScriptable(cx, thisObj);
1656
private static Object topScopeName(Context cx, Scriptable scope,
1659
if (cx.useDynamicScope) {
1660
scope = checkDynamicScope(cx.topCallScope, scope);
1662
return ScriptableObject.getProperty(scope, name);
1667
* Returns the object in the scope chain that has a given property.
1669
* The order of evaluation of an assignment expression involves
1670
* evaluating the lhs to a reference, evaluating the rhs, and then
1671
* modifying the reference with the rhs value. This method is used
1672
* to 'bind' the given name to an object containing that property
1673
* so that the side effects of evaluating the rhs do not affect
1674
* which property is modified.
1675
* Typically used in conjunction with setName.
1679
public static Scriptable bind(Context cx, Scriptable scope, String id)
1681
Scriptable firstXMLObject = null;
1682
Scriptable parent = scope.getParentScope();
1683
childScopesChecks: if (parent != null) {
1684
// Check for possibly nested "with" scopes first
1685
while (scope instanceof NativeWith) {
1686
Scriptable withObj = scope.getPrototype();
1687
if (withObj instanceof XMLObject) {
1688
XMLObject xmlObject = (XMLObject)withObj;
1689
if (xmlObject.ecmaHas(cx, id)) {
1692
if (firstXMLObject == null) {
1693
firstXMLObject = xmlObject;
1696
if (ScriptableObject.hasProperty(withObj, id)) {
1701
parent = parent.getParentScope();
1702
if (parent == null) {
1703
break childScopesChecks;
1707
if (ScriptableObject.hasProperty(scope, id)) {
1711
parent = parent.getParentScope();
1712
if (parent == null) {
1713
break childScopesChecks;
1717
// scope here is top scope
1718
if (cx.useDynamicScope) {
1719
scope = checkDynamicScope(cx.topCallScope, scope);
1721
if (ScriptableObject.hasProperty(scope, id)) {
1724
// Nothing was found, but since XML objects always bind
1725
// return one if found
1726
return firstXMLObject;
1729
public static Object setName(Scriptable bound, Object value,
1730
Context cx, Scriptable scope, String id)
1732
if (bound != null) {
1733
if (bound instanceof XMLObject) {
1734
XMLObject xmlObject = (XMLObject)bound;
1735
xmlObject.ecmaPut(cx, id, value);
1737
ScriptableObject.putProperty(bound, id, value);
1740
// "newname = 7;", where 'newname' has not yet
1741
// been defined, creates a new property in the
1742
// top scope unless strict mode is specified.
1743
if (cx.hasFeature(Context.FEATURE_STRICT_VARS)) {
1744
throw Context.reportRuntimeError1("msg.assn.create.strict", id);
1746
// Find the top scope by walking up the scope chain.
1747
bound = ScriptableObject.getTopLevelScope(scope);
1748
if (cx.useDynamicScope) {
1749
bound = checkDynamicScope(cx.topCallScope, bound);
1751
bound.put(id, bound, value);
1757
* This is the enumeration needed by the for..in statement.
1761
* IdEnumeration maintains a ObjToIntMap to make sure a given
1762
* id is enumerated only once across multiple objects in a
1765
* XXX - ECMA delete doesn't hide properties in the prototype,
1766
* but js/ref does. This means that the js/ref for..in can
1767
* avoid maintaining a hash table and instead perform lookups
1768
* to see if a given property has already been enumerated.
1771
private static class IdEnumeration
1781
public static Object enumInit(Object value, Context cx, boolean enumValues)
1783
IdEnumeration x = new IdEnumeration();
1784
x.obj = toObjectOrNull(cx, value);
1785
if (x.obj != null) {
1786
// null or undefined do not cause errors but rather lead to empty
1788
x.enumValues = enumValues;
1789
// enumInit should read all initial ids before returning
1790
// or "for (a.i in a)" would wrongly enumerate i in a as well
1791
enumChangeObject(x);
1796
public static Boolean enumNext(Object enumObj)
1798
// OPT this could be more efficient
1800
IdEnumeration x = (IdEnumeration)enumObj;
1802
if (x.obj == null) {
1806
if (x.index == x.ids.length) {
1807
x.obj = x.obj.getPrototype();
1808
enumChangeObject(x);
1811
Object id = x.ids[x.index++];
1812
if (x.used != null && x.used.has(id)) {
1815
if (id instanceof String) {
1816
String strId = (String)id;
1817
if (!x.obj.has(strId, x.obj))
1818
continue; // must have been deleted
1819
x.currentId = strId;
1821
int intId = ((Number)id).intValue();
1822
if (!x.obj.has(intId, x.obj))
1823
continue; // must have been deleted
1824
x.currentId = String.valueOf(intId);
1829
return wrapBoolean(result);
1832
public static Object enumId(Object enumObj, Context cx)
1834
IdEnumeration x = (IdEnumeration)enumObj;
1835
if (!x.enumValues) return x.currentId;
1839
String s = toStringIdOrIndex(cx, x.currentId);
1841
int index = lastIndexResult(cx);
1842
result = x.obj.get(index, x.obj);
1844
result = x.obj.get(s, x.obj);
1850
private static void enumChangeObject(IdEnumeration x)
1852
Object[] ids = null;
1853
while (x.obj != null) {
1854
ids = x.obj.getIds();
1855
if (ids.length != 0) {
1858
x.obj = x.obj.getPrototype();
1860
if (x.obj != null && x.ids != null) {
1861
Object[] previous = x.ids;
1862
int L = previous.length;
1863
if (x.used == null) {
1864
x.used = new ObjToIntMap(L);
1866
for (int i = 0; i != L; ++i) {
1867
x.used.intern(previous[i]);
1875
* Prepare for calling name(...): return function corresponding to
1876
* name and make current top scope available
1877
* as ScriptRuntime.lastStoredScriptable() for consumption as thisObj.
1878
* The caller must call ScriptRuntime.lastStoredScriptable() immediately
1879
* after calling this method.
1881
public static Function getNameFunctionAndThis(String name,
1885
Scriptable parent = scope.getParentScope();
1886
if (parent == null) {
1887
Object result = topScopeName(cx, scope, name);
1888
if (!(result instanceof Function)) {
1889
if (result == Scriptable.NOT_FOUND) {
1890
throw notFoundError(scope, name);
1892
throw notFunctionError(result, name);
1895
// Top scope is not NativeWith or NativeCall => thisObj == scope
1896
Scriptable thisObj = scope;
1897
storeScriptable(cx, thisObj);
1898
return (Function)result;
1901
// name will call storeScriptable(cx, thisObj);
1902
return (Function)nameOrFunction(cx, scope, parent, name, true);
1906
* Prepare for calling obj[id](...): return function corresponding to
1907
* obj[id] and make obj properly converted to Scriptable available
1908
* as ScriptRuntime.lastStoredScriptable() for consumption as thisObj.
1909
* The caller must call ScriptRuntime.lastStoredScriptable() immediately
1910
* after calling this method.
1912
public static Function getElemFunctionAndThis(Object obj,
1916
String s = toStringIdOrIndex(cx, elem);
1918
return getPropFunctionAndThis(obj, s, cx);
1920
int index = lastIndexResult(cx);
1922
Scriptable thisObj = toObjectOrNull(cx, obj);
1923
if (thisObj == null) {
1924
throw undefCallError(obj, String.valueOf(index));
1929
// Ignore XML lookup as requred by ECMA 357, 11.2.2.1
1930
value = ScriptableObject.getProperty(thisObj, index);
1931
if (value != Scriptable.NOT_FOUND) {
1934
if (!(thisObj instanceof XMLObject)) {
1937
XMLObject xmlObject = (XMLObject)thisObj;
1938
Scriptable extra = xmlObject.getExtraMethodSource(cx);
1939
if (extra == null) {
1944
if (!(value instanceof Function)) {
1945
throw notFunctionError(value, elem);
1948
storeScriptable(cx, thisObj);
1949
return (Function)value;
1953
* Prepare for calling obj.property(...): return function corresponding to
1954
* obj.property and make obj properly converted to Scriptable available
1955
* as ScriptRuntime.lastStoredScriptable() for consumption as thisObj.
1956
* The caller must call ScriptRuntime.lastStoredScriptable() immediately
1957
* after calling this method.
1959
public static Function getPropFunctionAndThis(Object obj,
1963
Scriptable thisObj = toObjectOrNull(cx, obj);
1964
if (thisObj == null) {
1965
throw undefCallError(obj, property);
1970
// Ignore XML lookup as requred by ECMA 357, 11.2.2.1
1971
value = ScriptableObject.getProperty(thisObj, property);
1972
if (value != Scriptable.NOT_FOUND) {
1975
if (!(thisObj instanceof XMLObject)) {
1978
XMLObject xmlObject = (XMLObject)thisObj;
1979
Scriptable extra = xmlObject.getExtraMethodSource(cx);
1980
if (extra == null) {
1986
if (!(value instanceof Function)) {
1987
throw notFunctionError(value, property);
1990
storeScriptable(cx, thisObj);
1991
return (Function)value;
1995
* Prepare for calling <expression>(...): return function corresponding to
1996
* <expression> and make parent scope of the function available
1997
* as ScriptRuntime.lastStoredScriptable() for consumption as thisObj.
1998
* The caller must call ScriptRuntime.lastStoredScriptable() immediately
1999
* after calling this method.
2001
public static Function getValueFunctionAndThis(Object value, Context cx)
2003
if (!(value instanceof Function)) {
2004
throw notFunctionError(value);
2007
Function f = (Function)value;
2008
Scriptable thisObj = f.getParentScope();
2009
if (thisObj.getParentScope() != null) {
2010
if (thisObj instanceof NativeWith) {
2011
// functions defined inside with should have with target
2013
} else if (thisObj instanceof NativeCall) {
2014
// nested functions should have top scope as their thisObj
2015
thisObj = ScriptableObject.getTopLevelScope(thisObj);
2018
storeScriptable(cx, thisObj);
2023
* Perform function call in reference context. Should always
2024
* return value that can be passed to
2025
* {@link #refGet(Object)} or @link #refSet(Object, Object)}
2026
* arbitrary number of times.
2027
* The args array reference should not be stored in any object that is
2028
* can be GC-reachable after this method returns. If this is necessary,
2029
* store args.clone(), not args array itself.
2031
public static Object callRef(Function function, Scriptable thisObj,
2032
Object[] args, Context cx, Scriptable scope)
2034
if (function instanceof BaseFunction) {
2035
BaseFunction bf = (BaseFunction)function;
2036
Ref ref = bf.callRef(cx, scope, thisObj, args);
2038
storeScriptable(cx, thisObj);
2042
// No runtime support for now
2043
String msg = getMessage1("msg.no.ref.from.function",
2044
toString(function));
2045
throw constructError("ReferenceError", msg);
2053
public static Scriptable newObject(Object fun, Context cx,
2054
Scriptable scope, Object[] args)
2056
if (!(fun instanceof Function)) {
2057
throw notFunctionError(fun);
2059
Function function = (Function)fun;
2060
return function.construct(cx, scope, args);
2063
public static Object callSpecial(Context cx, Function fun,
2065
Object[] args, Scriptable scope,
2066
Scriptable callerThis, int callType,
2067
String filename, int lineNumber)
2069
if (callType == Node.SPECIALCALL_EVAL) {
2070
if (NativeGlobal.isEvalFunction(fun)) {
2071
return evalSpecial(cx, scope, callerThis, args,
2072
filename, lineNumber);
2074
} else if (callType == Node.SPECIALCALL_WITH) {
2075
if (NativeWith.isWithFunction(fun)) {
2076
throw Context.reportRuntimeError1("msg.only.from.new",
2080
throw Kit.codeBug();
2083
return fun.call(cx, scope, thisObj, args);
2086
public static Object newSpecial(Context cx, Object fun,
2087
Object[] args, Scriptable scope,
2090
if (callType == Node.SPECIALCALL_EVAL) {
2091
if (NativeGlobal.isEvalFunction(fun)) {
2092
throw typeError1("msg.not.ctor", "eval");
2094
} else if (callType == Node.SPECIALCALL_WITH) {
2095
if (NativeWith.isWithFunction(fun)) {
2096
return NativeWith.newWithSpecial(cx, scope, args);
2099
throw Kit.codeBug();
2102
return newObject(fun, cx, scope, args);
2106
* Function.prototype.apply and Function.prototype.call
2108
* See Ecma 15.3.4.[34]
2110
public static Object applyOrCall(boolean isApply,
2111
Context cx, Scriptable scope,
2112
Scriptable thisObj, Object[] args)
2114
int L = args.length;
2116
if (thisObj instanceof Function) {
2117
function = (Function)thisObj;
2119
Object value = thisObj.getDefaultValue(ScriptRuntime.FunctionClass);
2120
if (!(value instanceof Function)) {
2121
throw ScriptRuntime.notFunctionError(value, thisObj);
2123
function = (Function)value;
2126
Scriptable callThis = null;
2128
callThis = toObjectOrNull(cx, args[0]);
2130
if (callThis == null) {
2131
// This covers the case of args[0] == (null|undefined) as well.
2132
callThis = getTopCallScope(cx);
2137
// Follow Ecma 15.3.4.3
2139
callArgs = ScriptRuntime.emptyArgs;
2141
Object arg1 = args[1];
2142
if (arg1 == null || arg1 == Undefined.instance) {
2143
callArgs = ScriptRuntime.emptyArgs;
2144
} else if (arg1 instanceof NativeArray
2145
|| arg1 instanceof Arguments)
2147
callArgs = cx.getElements((Scriptable) arg1);
2149
throw ScriptRuntime.typeError0("msg.arg.isnt.array");
2153
// Follow Ecma 15.3.4.4
2155
callArgs = ScriptRuntime.emptyArgs;
2157
callArgs = new Object[L - 1];
2158
System.arraycopy(args, 1, callArgs, 0, L - 1);
2162
return function.call(cx, scope, callThis, callArgs);
2166
* The eval function property of the global object.
2170
public static Object evalSpecial(Context cx, Scriptable scope,
2171
Object thisArg, Object[] args,
2172
String filename, int lineNumber)
2174
if (args.length < 1)
2175
return Undefined.instance;
2177
if (!(x instanceof String)) {
2178
if (cx.hasFeature(Context.FEATURE_STRICT_EVAL)) {
2179
throw Context.reportRuntimeError0("msg.eval.nonstring.strict");
2181
String message = ScriptRuntime.getMessage0("msg.eval.nonstring");
2182
Context.reportWarning(message);
2185
if (filename == null) {
2186
int[] linep = new int[1];
2187
filename = Context.getSourcePositionFromStack(linep);
2188
if (filename != null) {
2189
lineNumber = linep[0];
2194
String sourceName = ScriptRuntime.
2195
makeUrlForGeneratedScript(true, filename, lineNumber);
2197
ErrorReporter reporter;
2198
reporter = DefaultErrorReporter.forEval(cx.getErrorReporter());
2200
// Compile with explicit interpreter instance to force interpreter
2202
Script script = cx.compileString((String)x, new Interpreter(),
2203
reporter, sourceName, 1, null);
2204
((InterpretedFunction)script).idata.evalScriptFlag = true;
2206
// if the compile fails, an error has been reported by the
2207
// compiler, but we need to stop execution to avoid
2208
// infinite looping on while(true) { eval('foo bar') } -
2209
// so we throw an EvaluatorException.
2210
if (script == null) {
2211
String message = ScriptRuntime.getMessage0("msg.syntax");
2212
throw new EvaluatorException(message, filename, lineNumber,
2216
Callable c = (Callable)script;
2217
return c.call(cx, scope, (Scriptable)thisArg, ScriptRuntime.emptyArgs);
2221
* The typeof operator
2223
public static String typeof(Object value)
2225
if (value == Undefined.instance)
2229
if (value instanceof Scriptable)
2231
if (value instanceof XMLObject)
2234
return (value instanceof Function) ? "function" : "object";
2236
if (value instanceof String)
2238
if (value instanceof Number)
2240
if (value instanceof Boolean)
2242
throw errorWithClassName("msg.invalid.type", value);
2246
* The typeof operator that correctly handles the undefined case
2248
public static String typeofName(Scriptable scope, String id)
2250
Context cx = Context.getContext();
2251
Scriptable val = bind(cx, scope, id);
2254
return typeof(getObjectProp(val, id, cx));
2258
// implement the '-' operator inline in the caller
2259
// as "-toNumber(val)"
2262
// implement the '!' operator inline in the caller
2263
// as "!toBoolean(val)"
2266
// implement the '~' operator inline in the caller
2267
// as "~toInt32(val)"
2269
public static Object add(Object val1, Object val2, Context cx)
2271
if(val1 instanceof Number && val2 instanceof Number) {
2272
return wrapNumber(((Number)val1).doubleValue() +
2273
((Number)val2).doubleValue());
2275
if (val1 instanceof XMLObject) {
2276
Object test = ((XMLObject)val1).addValues(cx, true, val2);
2277
if (test != Scriptable.NOT_FOUND) {
2281
if (val2 instanceof XMLObject) {
2282
Object test = ((XMLObject)val2).addValues(cx, false, val1);
2283
if (test != Scriptable.NOT_FOUND) {
2287
if (val1 instanceof Scriptable)
2288
val1 = ((Scriptable) val1).getDefaultValue(null);
2289
if (val2 instanceof Scriptable)
2290
val2 = ((Scriptable) val2).getDefaultValue(null);
2291
if (!(val1 instanceof String) && !(val2 instanceof String))
2292
if ((val1 instanceof Number) && (val2 instanceof Number))
2293
return wrapNumber(((Number)val1).doubleValue() +
2294
((Number)val2).doubleValue());
2296
return wrapNumber(toNumber(val1) + toNumber(val2));
2297
return toString(val1).concat(toString(val2));
2300
public static Object nameIncrDecr(Scriptable scopeChain, String id,
2307
target = scopeChain;
2309
value = target.get(id, scopeChain);
2310
if (value != Scriptable.NOT_FOUND) {
2313
target = target.getPrototype();
2314
} while (target != null);
2315
scopeChain = scopeChain.getParentScope();
2316
} while (scopeChain != null);
2317
throw notFoundError(scopeChain, id);
2319
return doScriptableIncrDecr(target, id, scopeChain, value,
2323
public static Object propIncrDecr(Object obj, String id,
2324
Context cx, int incrDecrMask)
2326
Scriptable start = toObjectOrNull(cx, obj);
2327
if (start == null) {
2328
throw undefReadError(obj, id);
2331
Scriptable target = start;
2335
value = target.get(id, start);
2336
if (value != Scriptable.NOT_FOUND) {
2339
target = target.getPrototype();
2340
} while (target != null);
2341
start.put(id, start, NaNobj);
2344
return doScriptableIncrDecr(target, id, start, value,
2348
private static Object doScriptableIncrDecr(Scriptable target,
2350
Scriptable protoChainStart,
2354
boolean post = ((incrDecrMask & Node.POST_FLAG) != 0);
2356
if (value instanceof Number) {
2357
number = ((Number)value).doubleValue();
2359
number = toNumber(value);
2361
// convert result to number
2362
value = wrapNumber(number);
2365
if ((incrDecrMask & Node.DECR_FLAG) == 0) {
2370
Number result = wrapNumber(number);
2371
target.put(id, protoChainStart, result);
2379
public static Object elemIncrDecr(Object obj, Object index,
2380
Context cx, int incrDecrMask)
2382
Object value = getObjectElem(obj, index, cx);
2383
boolean post = ((incrDecrMask & Node.POST_FLAG) != 0);
2385
if (value instanceof Number) {
2386
number = ((Number)value).doubleValue();
2388
number = toNumber(value);
2390
// convert result to number
2391
value = wrapNumber(number);
2394
if ((incrDecrMask & Node.DECR_FLAG) == 0) {
2399
Number result = wrapNumber(number);
2400
setObjectElem(obj, index, result, cx);
2408
public static Object refIncrDecr(Ref ref, Scriptable target,
2409
Context cx, int incrDecrMask)
2411
Object value = ref.get(cx, target);
2412
boolean post = ((incrDecrMask & Node.POST_FLAG) != 0);
2414
if (value instanceof Number) {
2415
number = ((Number)value).doubleValue();
2417
number = toNumber(value);
2419
// convert result to number
2420
value = wrapNumber(number);
2423
if ((incrDecrMask & Node.DECR_FLAG) == 0) {
2428
Number result = wrapNumber(number);
2429
ref.set(cx, target, result);
2437
public static Object toPrimitive(Object val) {
2438
if (val == null || !(val instanceof Scriptable)) {
2441
Scriptable s = (Scriptable)val;
2442
Object result = s.getDefaultValue(null);
2443
if (result instanceof Scriptable)
2444
throw typeError0("msg.bad.default.value");
2453
public static boolean eq(Object x, Object y)
2455
if (x == null || x == Undefined.instance) {
2456
if (y == null || y == Undefined.instance) {
2459
if (y instanceof ScriptableObject) {
2460
Object test = ((ScriptableObject)y).equivalentValues(x);
2461
if (test != Scriptable.NOT_FOUND) {
2462
return ((Boolean)test).booleanValue();
2466
} else if (x instanceof Number) {
2467
return eqNumber(((Number)x).doubleValue(), y);
2468
} else if (x instanceof String) {
2469
return eqString((String)x, y);
2470
} else if (x instanceof Boolean) {
2471
boolean b = ((Boolean)x).booleanValue();
2472
if (y instanceof Boolean) {
2473
return b == ((Boolean)y).booleanValue();
2475
if (y instanceof ScriptableObject) {
2476
Object test = ((ScriptableObject)y).equivalentValues(x);
2477
if (test != Scriptable.NOT_FOUND) {
2478
return ((Boolean)test).booleanValue();
2481
return eqNumber(b ? 1.0 : 0.0, y);
2482
} else if (x instanceof Scriptable) {
2483
if (y instanceof Scriptable) {
2484
// Generic test also works for y == Undefined.instance
2488
if (x instanceof ScriptableObject) {
2489
Object test = ((ScriptableObject)x).equivalentValues(y);
2490
if (test != Scriptable.NOT_FOUND) {
2491
return ((Boolean)test).booleanValue();
2494
if (y instanceof ScriptableObject) {
2495
Object test = ((ScriptableObject)y).equivalentValues(x);
2496
if (test != Scriptable.NOT_FOUND) {
2497
return ((Boolean)test).booleanValue();
2500
if (x instanceof Wrapper && y instanceof Wrapper) {
2501
return ((Wrapper)x).unwrap() == ((Wrapper)y).unwrap();
2504
} else if (y instanceof Boolean) {
2505
if (x instanceof ScriptableObject) {
2506
Object test = ((ScriptableObject)x).equivalentValues(y);
2507
if (test != Scriptable.NOT_FOUND) {
2508
return ((Boolean)test).booleanValue();
2511
double d = ((Boolean)y).booleanValue() ? 1.0 : 0.0;
2512
return eqNumber(d, x);
2513
} else if (y instanceof Number) {
2514
return eqNumber(((Number)y).doubleValue(), x);
2515
} else if (y instanceof String) {
2516
return eqString((String)y, x);
2520
warnAboutNonJSObject(x);
2525
static boolean eqNumber(double x, Object y)
2530
} else if (y instanceof Number) {
2531
return x == ((Number)y).doubleValue();
2532
} else if (y instanceof String) {
2533
return x == toNumber(y);
2534
} else if (y instanceof Boolean) {
2535
return x == (((Boolean)y).booleanValue() ? 1.0 : +0.0);
2536
} else if (y instanceof Scriptable) {
2537
if (y == Undefined.instance) { return false; }
2538
if (y instanceof ScriptableObject) {
2539
Object xval = wrapNumber(x);
2540
Object test = ((ScriptableObject)y).equivalentValues(xval);
2541
if (test != Scriptable.NOT_FOUND) {
2542
return ((Boolean)test).booleanValue();
2547
warnAboutNonJSObject(y);
2553
private static boolean eqString(String x, Object y)
2558
} else if (y instanceof String) {
2560
} else if (y instanceof Number) {
2561
return toNumber(x) == ((Number)y).doubleValue();
2562
} else if (y instanceof Boolean) {
2563
return toNumber(x) == (((Boolean)y).booleanValue() ? 1.0 : 0.0);
2564
} else if (y instanceof Scriptable) {
2565
if (y == Undefined.instance) { return false; }
2566
if (y instanceof ScriptableObject) {
2567
Object test = ((ScriptableObject)y).equivalentValues(x);
2568
if (test != Scriptable.NOT_FOUND) {
2569
return ((Boolean)test).booleanValue();
2575
warnAboutNonJSObject(y);
2580
public static boolean shallowEq(Object x, Object y)
2583
if (!(x instanceof Number)) {
2587
double d = ((Number)x).doubleValue();
2592
} else if (x instanceof Number) {
2593
if (y instanceof Number) {
2594
return ((Number)x).doubleValue() == ((Number)y).doubleValue();
2596
} else if (x instanceof String) {
2597
if (y instanceof String) {
2600
} else if (x instanceof Boolean) {
2601
if (y instanceof Boolean) {
2604
} else if (x instanceof Scriptable) {
2605
// x == Undefined.instance goes here as well
2606
if (x instanceof Wrapper && y instanceof Wrapper) {
2607
return ((Wrapper)x).unwrap() == ((Wrapper)y).unwrap();
2610
warnAboutNonJSObject(x);
2617
* The instanceof operator.
2619
* @return a instanceof b
2621
public static boolean instanceOf(Object a, Object b,
2622
Context cx, Scriptable scope)
2624
// Check RHS is an object
2625
if (! (b instanceof Scriptable)) {
2626
throw typeError0("msg.instanceof.not.object");
2629
// for primitive values on LHS, return false
2630
// XXX we may want to change this so that
2631
// 5 instanceof Number == true
2632
if (! (a instanceof Scriptable))
2635
return ((Scriptable)b).hasInstance((Scriptable)a);
2641
* @return true iff rhs appears in lhs' proto chain
2643
protected static boolean jsDelegatesTo(Scriptable lhs, Scriptable rhs) {
2644
Scriptable proto = lhs.getPrototype();
2646
while (proto != null) {
2647
if (proto.equals(rhs)) return true;
2648
proto = proto.getPrototype();
2657
* This is a new JS 1.3 language feature. The in operator mirrors
2658
* the operation of the for .. in construct, and tests whether the
2659
* rhs has the property given by the lhs. It is different from the
2660
* for .. in construct in that:
2661
* <BR> - it doesn't perform ToObject on the right hand side
2662
* <BR> - it returns true for DontEnum properties.
2663
* @param a the left hand operand
2664
* @param b the right hand operand
2666
* @return true if property name or element number a is a property of b
2668
public static boolean in(Object a, Object b, Context cx, Scriptable scope)
2670
if (!(b instanceof Scriptable)) {
2671
throw typeError0("msg.instanceof.not.object");
2674
return hasObjectElem((Scriptable)b, a, cx);
2677
public static boolean cmp_LT(Object val1, Object val2)
2680
if (val1 instanceof Number && val2 instanceof Number) {
2681
d1 = ((Number)val1).doubleValue();
2682
d2 = ((Number)val2).doubleValue();
2684
if (val1 instanceof Scriptable)
2685
val1 = ((Scriptable) val1).getDefaultValue(NumberClass);
2686
if (val2 instanceof Scriptable)
2687
val2 = ((Scriptable) val2).getDefaultValue(NumberClass);
2688
if (val1 instanceof String && val2 instanceof String) {
2689
return ((String)val1).compareTo((String)val2) < 0;
2691
d1 = toNumber(val1);
2692
d2 = toNumber(val2);
2697
public static boolean cmp_LE(Object val1, Object val2)
2700
if (val1 instanceof Number && val2 instanceof Number) {
2701
d1 = ((Number)val1).doubleValue();
2702
d2 = ((Number)val2).doubleValue();
2704
if (val1 instanceof Scriptable)
2705
val1 = ((Scriptable) val1).getDefaultValue(NumberClass);
2706
if (val2 instanceof Scriptable)
2707
val2 = ((Scriptable) val2).getDefaultValue(NumberClass);
2708
if (val1 instanceof String && val2 instanceof String) {
2709
return ((String)val1).compareTo((String)val2) <= 0;
2711
d1 = toNumber(val1);
2712
d2 = toNumber(val2);
2717
// ------------------
2719
// ------------------
2721
public static ScriptableObject getGlobal(Context cx) {
2722
final String GLOBAL_CLASS = "org.mozilla.javascript.tools.shell.Global";
2723
Class globalClass = Kit.classOrNull(GLOBAL_CLASS);
2724
if (globalClass != null) {
2726
Class[] parm = { ScriptRuntime.ContextClass };
2727
Constructor globalClassCtor = globalClass.getConstructor(parm);
2728
Object[] arg = { cx };
2729
return (ScriptableObject) globalClassCtor.newInstance(arg);
2730
} catch (NoSuchMethodException e) {
2732
} catch (InvocationTargetException e) {
2734
} catch (IllegalAccessException e) {
2736
} catch (InstantiationException e) {
2740
return new ImporterTopLevel(cx);
2743
public static boolean hasTopCall(Context cx)
2745
return (cx.topCallScope != null);
2748
public static Scriptable getTopCallScope(Context cx)
2750
Scriptable scope = cx.topCallScope;
2751
if (scope == null) {
2752
throw new IllegalStateException();
2757
public static Object doTopCall(Callable callable,
2758
Context cx, Scriptable scope,
2759
Scriptable thisObj, Object[] args)
2761
if (scope == null) throw new IllegalArgumentException();
2762
if (cx.topCallScope != null) throw new IllegalStateException();
2765
cx.topCallScope = ScriptableObject.getTopLevelScope(scope);
2766
cx.useDynamicScope = cx.hasFeature(Context.FEATURE_DYNAMIC_SCOPE);
2767
ContextFactory f = cx.getFactory();
2769
result = f.doTopCall(callable, cx, scope, thisObj, args);
2771
cx.topCallScope = null;
2772
// Cleanup cached references
2773
cx.cachedXMLLib = null;
2775
if (cx.currentActivationCall != null) {
2776
// Function should always call exitActivationFunction
2777
// if it creates activation record
2778
throw new IllegalStateException();
2785
* Return <tt>possibleDynamicScope</tt> if <tt>staticTopScope</tt>
2786
* is present on its prototype chain and return <tt>staticTopScope</tt>
2788
* Should only be called when <tt>staticTopScope</tt> is top scope.
2790
static Scriptable checkDynamicScope(Scriptable possibleDynamicScope,
2791
Scriptable staticTopScope)
2793
// Return cx.topCallScope if scope
2794
if (possibleDynamicScope == staticTopScope) {
2795
return possibleDynamicScope;
2797
Scriptable proto = possibleDynamicScope;
2799
proto = proto.getPrototype();
2800
if (proto == staticTopScope) {
2801
return possibleDynamicScope;
2803
if (proto == null) {
2804
return staticTopScope;
2809
public static void initScript(NativeFunction funObj, Scriptable thisObj,
2810
Context cx, Scriptable scope,
2813
if (cx.topCallScope == null)
2814
throw new IllegalStateException();
2816
int varCount = funObj.getParamAndVarCount();
2817
if (varCount != 0) {
2819
Scriptable varScope = scope;
2820
// Never define any variables from var statements inside with
2821
// object. See bug 38590.
2822
while (varScope instanceof NativeWith) {
2823
varScope = varScope.getParentScope();
2826
for (int i = varCount; i-- != 0;) {
2827
String name = funObj.getParamOrVarName(i);
2828
// Don't overwrite existing def if already defined in object
2829
// or prototypes of object.
2830
if (!ScriptableObject.hasProperty(scope, name)) {
2832
// Global var definitions are supposed to be DONTDELETE
2833
ScriptableObject.defineProperty(
2834
varScope, name, Undefined.instance,
2835
ScriptableObject.PERMANENT);
2837
varScope.put(name, varScope, Undefined.instance);
2844
public static Scriptable createFunctionActivation(NativeFunction funObj,
2848
return new NativeCall(funObj, scope, args);
2852
public static void enterActivationFunction(Context cx,
2853
Scriptable activation)
2855
if (cx.topCallScope == null)
2856
throw new IllegalStateException();
2858
NativeCall call = (NativeCall)activation;
2859
call.parentActivationCall = cx.currentActivationCall;
2860
cx.currentActivationCall = call;
2863
public static void exitActivationFunction(Context cx)
2865
NativeCall call = cx.currentActivationCall;
2866
cx.currentActivationCall = call.parentActivationCall;
2867
call.parentActivationCall = null;
2870
static NativeCall findFunctionActivation(Context cx, Function f)
2872
NativeCall call = cx.currentActivationCall;
2873
while (call != null) {
2874
if (call.function == f)
2876
call = call.parentActivationCall;
2881
public static Scriptable newCatchScope(Throwable t,
2882
Scriptable lastCatchScope,
2883
String exceptionName,
2884
Context cx, Scriptable scope)
2890
if (t instanceof JavaScriptException) {
2892
obj = ((JavaScriptException)t).getValue();
2896
// Create wrapper object unless it was associated with
2897
// the previous scope object
2899
if (lastCatchScope != null) {
2900
NativeObject last = (NativeObject)lastCatchScope;
2901
obj = last.getAssociatedValue(t);
2902
if (obj == null) Kit.codeBug();
2909
Throwable javaException = null;
2911
if (t instanceof EcmaError) {
2912
EcmaError ee = (EcmaError)t;
2914
errorName = ee.getName();
2915
errorMsg = ee.getErrorMessage();
2916
} else if (t instanceof WrappedException) {
2917
WrappedException we = (WrappedException)t;
2919
javaException = we.getWrappedException();
2920
errorName = "JavaException";
2921
errorMsg = javaException.getClass().getName()
2922
+": "+javaException.getMessage();
2923
} else if (t instanceof EvaluatorException) {
2924
// Pure evaluator exception, nor WrappedException instance
2925
EvaluatorException ee = (EvaluatorException)t;
2927
errorName = "InternalError";
2928
errorMsg = ee.getMessage();
2930
// Script can catch only instances of JavaScriptException,
2931
// EcmaError and EvaluatorException
2932
throw Kit.codeBug();
2935
String sourceUri = re.sourceName();
2936
if (sourceUri == null) {
2939
int line = re.lineNumber();
2942
args = new Object[] { errorMsg, sourceUri, new Integer(line) };
2944
args = new Object[] { errorMsg, sourceUri };
2947
Scriptable errorObject = cx.newObject(scope, errorName, args);
2948
ScriptableObject.putProperty(errorObject, "name", errorName);
2950
if (javaException != null) {
2951
Object wrap = cx.getWrapFactory().wrap(cx, scope, javaException,
2953
ScriptableObject.defineProperty(
2954
errorObject, "javaException", wrap,
2955
ScriptableObject.PERMANENT | ScriptableObject.READONLY);
2962
NativeObject catchScopeObject = new NativeObject();
2964
catchScopeObject.defineProperty(
2965
exceptionName, obj, ScriptableObject.PERMANENT);
2967
catchScopeObject.associateValue(t, obj);
2969
return catchScopeObject;
2972
public static Scriptable enterWith(Object obj, Context cx,
2975
Scriptable sobj = toObjectOrNull(cx, obj);
2977
throw typeError1("msg.undef.with", toString(obj));
2979
if (sobj instanceof XMLObject) {
2980
XMLObject xmlObject = (XMLObject)sobj;
2981
return xmlObject.enterWith(scope);
2983
return new NativeWith(scope, sobj);
2986
public static Scriptable leaveWith(Scriptable scope)
2988
NativeWith nw = (NativeWith)scope;
2989
return nw.getParentScope();
2992
public static Scriptable enterDotQuery(Object value, Scriptable scope)
2994
if (!(value instanceof XMLObject)) {
2995
throw notXmlError(value);
2997
XMLObject object = (XMLObject)value;
2998
return object.enterDotQuery(scope);
3001
public static Object updateDotQuery(boolean value, Scriptable scope)
3003
// Return null to continue looping
3004
NativeWith nw = (NativeWith)scope;
3005
return nw.updateDotQuery(value);
3008
public static Scriptable leaveDotQuery(Scriptable scope)
3010
NativeWith nw = (NativeWith)scope;
3011
return nw.getParentScope();
3014
public static void setFunctionProtoAndParent(BaseFunction fn,
3017
fn.setParentScope(scope);
3018
fn.setPrototype(ScriptableObject.getFunctionPrototype(scope));
3021
public static void setObjectProtoAndParent(ScriptableObject object,
3024
// Compared with function it always sets the scope to top scope
3025
scope = ScriptableObject.getTopLevelScope(scope);
3026
object.setParentScope(scope);
3028
= ScriptableObject.getClassPrototype(scope, object.getClassName());
3029
object.setPrototype(proto);
3032
public static void initFunction(Context cx, Scriptable scope,
3033
NativeFunction function, int type,
3034
boolean fromEvalCode)
3036
if (type == FunctionNode.FUNCTION_STATEMENT) {
3037
String name = function.functionName;
3038
if (name != null && name.length() != 0) {
3039
if (!fromEvalCode) {
3040
// ECMA specifies that functions defined in global and
3041
// function scope outside eval should have DONTDELETE set.
3042
ScriptableObject.defineProperty
3043
(scope, name, function, ScriptableObject.PERMANENT);
3045
scope.put(name, scope, function);
3048
} else if (type == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) {
3049
String name = function.functionName;
3050
if (name != null && name.length() != 0) {
3051
// Always put function expression statements into initial
3052
// activation object ignoring the with statement to follow
3054
while (scope instanceof NativeWith) {
3055
scope = scope.getParentScope();
3057
scope.put(name, scope, function);
3060
throw Kit.codeBug();
3064
public static Scriptable newArrayLiteral(Object[] objects,
3066
Context cx, Scriptable scope)
3068
int count = objects.length;
3070
if (skipIndexces != null) {
3071
skipCount = skipIndexces.length;
3073
int length = count + skipCount;
3074
Integer lengthObj = new Integer(length);
3075
Scriptable arrayObj;
3077
* If the version is 120, then new Array(4) means create a new
3078
* array with 4 as the first element. In this case, we have to
3079
* set length property manually.
3081
if (cx.getLanguageVersion() == Context.VERSION_1_2) {
3082
arrayObj = cx.newObject(scope, "Array", ScriptRuntime.emptyArgs);
3083
ScriptableObject.putProperty(arrayObj, "length", lengthObj);
3085
arrayObj = cx.newObject(scope, "Array", new Object[] { lengthObj });
3088
for (int i = 0, j = 0; i != length; ++i) {
3089
if (skip != skipCount && skipIndexces[skip] == i) {
3093
ScriptableObject.putProperty(arrayObj, i, objects[j]);
3099
public static Scriptable newObjectLiteral(Object[] propertyIds,
3100
Object[] propertyValues,
3101
Context cx, Scriptable scope)
3103
Scriptable object = cx.newObject(scope);
3104
for (int i = 0, end = propertyIds.length; i != end; ++i) {
3105
Object id = propertyIds[i];
3106
Object value = propertyValues[i];
3107
if (id instanceof String) {
3108
ScriptableObject.putProperty(object, (String)id, value);
3110
int index = ((Integer)id).intValue();
3111
ScriptableObject.putProperty(object, index, value);
3117
public static boolean isArrayObject(Object obj)
3119
return obj instanceof NativeArray || obj instanceof Arguments;
3122
public static Object[] getArrayElements(Scriptable object)
3124
Context cx = Context.getContext();
3125
long longLen = NativeArray.getLengthProperty(cx, object);
3126
if (longLen > Integer.MAX_VALUE) {
3127
// arrays beyond MAX_INT is not in Java in any case
3128
throw new IllegalArgumentException();
3130
int len = (int) longLen;
3132
return ScriptRuntime.emptyArgs;
3134
Object[] result = new Object[len];
3135
for (int i=0; i < len; i++) {
3136
Object elem = ScriptableObject.getProperty(object, i);
3137
result[i] = (elem == Scriptable.NOT_FOUND) ? Undefined.instance
3144
static void checkDeprecated(Context cx, String name) {
3145
int version = cx.getLanguageVersion();
3146
if (version >= Context.VERSION_1_4 || version == Context.VERSION_DEFAULT) {
3147
String msg = getMessage1("msg.deprec.ctor", name);
3148
if (version == Context.VERSION_DEFAULT)
3149
Context.reportWarning(msg);
3151
throw Context.reportRuntimeError(msg);
3155
public static String getMessage0(String messageId)
3157
return getMessage(messageId, null);
3160
public static String getMessage1(String messageId, Object arg1)
3162
Object[] arguments = {arg1};
3163
return getMessage(messageId, arguments);
3166
public static String getMessage2(
3167
String messageId, Object arg1, Object arg2)
3169
Object[] arguments = {arg1, arg2};
3170
return getMessage(messageId, arguments);
3173
public static String getMessage3(
3174
String messageId, Object arg1, Object arg2, Object arg3)
3176
Object[] arguments = {arg1, arg2, arg3};
3177
return getMessage(messageId, arguments);
3180
public static String getMessage4(
3181
String messageId, Object arg1, Object arg2, Object arg3, Object arg4)
3183
Object[] arguments = {arg1, arg2, arg3, arg4};
3184
return getMessage(messageId, arguments);
3187
/* OPT there's a noticable delay for the first error! Maybe it'd
3188
* make sense to use a ListResourceBundle instead of a properties
3189
* file to avoid (synchronized) text parsing.
3191
public static String getMessage(String messageId, Object[] arguments)
3193
final String defaultResource
3194
= "org.mozilla.javascript.resources.Messages";
3196
Context cx = Context.getCurrentContext();
3197
Locale locale = cx != null ? cx.getLocale() : Locale.getDefault();
3199
// ResourceBundle does cacheing.
3200
ResourceBundle rb = ResourceBundle.getBundle(defaultResource, locale);
3202
String formatString;
3204
formatString = rb.getString(messageId);
3205
} catch (java.util.MissingResourceException mre) {
3206
throw new RuntimeException
3207
("no message resource found for message property "+ messageId);
3211
* It's OK to format the string, even if 'arguments' is null;
3212
* we need to format it anyway, to make double ''s collapse to
3215
// TODO: MessageFormat is not available on pJava
3216
MessageFormat formatter = new MessageFormat(formatString);
3217
return formatter.format(arguments);
3220
public static EcmaError constructError(String error, String message)
3223
String filename = null;
3224
Context cx = Context.getCurrentContext();
3226
int[] linep = new int[1];
3227
filename = cx.getSourcePositionFromStack(linep);
3230
return constructError(error, message, filename, line, null, 0);
3233
public static EcmaError constructError(String error,
3240
return new EcmaError(error, message, sourceName,
3241
lineNumber, lineSource, columnNumber);
3244
public static EcmaError typeError(String message)
3246
return constructError("TypeError", message);
3249
public static EcmaError typeError0(String messageId)
3251
String msg = getMessage0(messageId);
3252
return typeError(msg);
3255
public static EcmaError typeError1(String messageId, String arg1)
3257
String msg = getMessage1(messageId, arg1);
3258
return typeError(msg);
3261
public static EcmaError typeError2(String messageId, String arg1,
3264
String msg = getMessage2(messageId, arg1, arg2);
3265
return typeError(msg);
3268
public static EcmaError typeError3(String messageId, String arg1,
3269
String arg2, String arg3)
3271
String msg = getMessage3(messageId, arg1, arg2, arg3);
3272
return typeError(msg);
3275
public static RuntimeException undefReadError(Object object, Object id)
3277
String idStr = (id == null) ? "null" : id.toString();
3278
return typeError2("msg.undef.prop.read", toString(object), idStr);
3281
public static RuntimeException undefCallError(Object object, Object id)
3283
String idStr = (id == null) ? "null" : id.toString();
3284
return typeError2("msg.undef.method.call", toString(object), idStr);
3287
public static RuntimeException undefWriteError(Object object,
3291
String idStr = (id == null) ? "null" : id.toString();
3292
String valueStr = (value instanceof Scriptable)
3293
? value.toString() : toString(value);
3294
return typeError3("msg.undef.prop.write", toString(object), idStr,
3298
public static RuntimeException notFoundError(Scriptable object,
3301
// XXX: use object to improve the error message
3302
String msg = getMessage1("msg.is.not.defined", property);
3303
throw constructError("ReferenceError", msg);
3306
public static RuntimeException notFunctionError(Object value)
3308
return notFunctionError(value, value);
3311
public static RuntimeException notFunctionError(Object value,
3312
Object messageHelper)
3314
// XXX Use value for better error reporting
3315
String msg = (messageHelper == null)
3316
? "null" : messageHelper.toString();
3317
return typeError1("msg.isnt.function", msg);
3320
private static RuntimeException notXmlError(Object value)
3322
throw typeError1("msg.isnt.xml.object", ScriptRuntime.toString(value));
3325
private static void warnAboutNonJSObject(Object nonJSObject)
3328
"RHINO USAGE WARNING: Missed Context.javaToJS() conversion:\n"
3329
+"Rhino runtime detected object "+nonJSObject+" of class "+nonJSObject.getClass().getName()+" where it expected String, Number, Boolean or Scriptable instance. Please check your code for missig Context.javaToJS() call.";
3330
Context.reportWarning(message);
3331
// Just to be sure that it would be noticed
3332
System.err.println(message);
3335
public static RegExpProxy getRegExpProxy(Context cx)
3337
return cx.getRegExpProxy();
3340
public static RegExpProxy checkRegExpProxy(Context cx)
3342
RegExpProxy result = getRegExpProxy(cx);
3343
if (result == null) {
3344
throw cx.reportRuntimeError0("msg.no.regexp");
3349
private static XMLLib currentXMLLib(Context cx)
3351
// Scripts should be running to access this
3352
if (cx.topCallScope == null)
3353
throw new IllegalStateException();
3355
XMLLib xmlLib = cx.cachedXMLLib;
3356
if (xmlLib == null) {
3357
xmlLib = XMLLib.extractFromScope(cx.topCallScope);
3359
throw new IllegalStateException();
3360
cx.cachedXMLLib = xmlLib;
3367
* Escapes the reserved characters in a value of an attribute
3369
* @param value Unescaped text
3370
* @return The escaped text
3372
public static String escapeAttributeValue(Object value, Context cx)
3374
XMLLib xmlLib = currentXMLLib(cx);
3375
return xmlLib.escapeAttributeValue(value);
3379
* Escapes the reserved characters in a value of a text node
3381
* @param value Unescaped text
3382
* @return The escaped text
3384
public static String escapeTextValue(Object value, Context cx)
3386
XMLLib xmlLib = currentXMLLib(cx);
3387
return xmlLib.escapeTextValue(value);
3390
public static Ref memberRef(Object obj, Object elem,
3391
Context cx, int memberTypeFlags)
3393
if (!(obj instanceof XMLObject)) {
3394
throw notXmlError(obj);
3396
XMLObject xmlObject = (XMLObject)obj;
3397
return xmlObject.memberRef(cx, elem, memberTypeFlags);
3400
public static Ref memberRef(Object obj, Object namespace, Object elem,
3401
Context cx, int memberTypeFlags)
3403
if (!(obj instanceof XMLObject)) {
3404
throw notXmlError(obj);
3406
XMLObject xmlObject = (XMLObject)obj;
3407
return xmlObject.memberRef(cx, namespace, elem, memberTypeFlags);
3410
public static Ref nameRef(Object name, Context cx,
3411
Scriptable scope, int memberTypeFlags)
3413
XMLLib xmlLib = currentXMLLib(cx);
3414
return xmlLib.nameRef(cx, name, scope, memberTypeFlags);
3417
public static Ref nameRef(Object namespace, Object name, Context cx,
3418
Scriptable scope, int memberTypeFlags)
3420
XMLLib xmlLib = currentXMLLib(cx);
3421
return xmlLib.nameRef(cx, namespace, name, scope, memberTypeFlags);
3424
private static void storeIndexResult(Context cx, int index)
3426
cx.scratchIndex = index;
3429
static int lastIndexResult(Context cx)
3431
return cx.scratchIndex;
3434
public static void storeUint32Result(Context cx, long value)
3436
if ((value >>> 32) != 0)
3437
throw new IllegalArgumentException();
3438
cx.scratchUint32 = value;
3441
public static long lastUint32Result(Context cx)
3443
long value = cx.scratchUint32;
3444
if ((value >>> 32) != 0)
3445
throw new IllegalStateException();
3449
private static void storeScriptable(Context cx, Scriptable value)
3451
// The previosly stored scratchScriptable should be consumed
3452
if (cx.scratchScriptable != null)
3453
throw new IllegalStateException();
3454
cx.scratchScriptable = value;
3457
public static Scriptable lastStoredScriptable(Context cx)
3459
Scriptable result = cx.scratchScriptable;
3460
cx.scratchScriptable = null;
3464
static String makeUrlForGeneratedScript
3465
(boolean isEval, String masterScriptUrl, int masterScriptLine)
3468
return masterScriptUrl+'#'+masterScriptLine+"(eval)";
3470
return masterScriptUrl+'#'+masterScriptLine+"(Function)";
3474
static boolean isGeneratedScript(String sourceUrl) {
3475
// ALERT: this may clash with a valid URL containing (eval) or
3477
return sourceUrl.indexOf("(eval)") >= 0
3478
|| sourceUrl.indexOf("(Function)") >= 0;
3481
private static RuntimeException errorWithClassName(String msg, Object val)
3483
return Context.reportRuntimeError1(msg, val.getClass().getName());
3486
public static final Object[] emptyArgs = new Object[0];
3487
public static final String[] emptyStrings = new String[0];