0001: /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
0002: *
0003: * ***** BEGIN LICENSE BLOCK *****
0004: * Version: MPL 1.1/GPL 2.0
0005: *
0006: * The contents of this file are subject to the Mozilla Public License Version
0007: * 1.1 (the "License"); you may not use this file except in compliance with
0008: * the License. You may obtain a copy of the License at
0009: * http://www.mozilla.org/MPL/
0010: *
0011: * Software distributed under the License is distributed on an "AS IS" basis,
0012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
0013: * for the specific language governing rights and limitations under the
0014: * License.
0015: *
0016: * The Original Code is Rhino code, released
0017: * May 6, 1999.
0018: *
0019: * The Initial Developer of the Original Code is
0020: * Netscape Communications Corporation.
0021: * Portions created by the Initial Developer are Copyright (C) 1997-2000
0022: * the Initial Developer. All Rights Reserved.
0023: *
0024: * Contributor(s):
0025: * Patrick Beard
0026: * Norris Boyd
0027: * Igor Bukanov
0028: * Ethan Hugg
0029: * Bob Jervis
0030: * Roger Lawrence
0031: * Terry Lucas
0032: * Frank Mitchell
0033: * Milen Nankov
0034: * Hannes Wallnoefer
0035: * Andrew Wason
0036: *
0037: * Alternatively, the contents of this file may be used under the terms of
0038: * the GNU General Public License Version 2 or later (the "GPL"), in which
0039: * case the provisions of the GPL are applicable instead of those above. If
0040: * you wish to allow use of your version of this file only under the terms of
0041: * the GPL and not to allow others to use your version of this file under the
0042: * MPL, indicate your decision by deleting the provisions above and replacing
0043: * them with the notice and other provisions required by the GPL. If you do
0044: * not delete the provisions above, a recipient may use your version of this
0045: * file under either the MPL or the GPL.
0046: *
0047: * ***** END LICENSE BLOCK ***** */
0048:
0049: package org.mozilla.javascript;
0050:
0051: import java.lang.reflect.*;
0052: import java.text.MessageFormat;
0053: import java.util.Locale;
0054: import java.util.ResourceBundle;
0055:
0056: import org.mozilla.javascript.xml.XMLObject;
0057: import org.mozilla.javascript.xml.XMLLib;
0058: import org.mozilla.javascript.continuations.Continuation;
0059:
0060: /**
0061: * This is the class that implements the runtime.
0062: *
0063: * @author Norris Boyd
0064: */
0065:
0066: public class ScriptRuntime {
0067:
0068: /**
0069: * No instances should be created.
0070: */
0071: protected ScriptRuntime() {
0072: }
0073:
0074: private static class NoSuchMethodShim implements Callable {
0075: String methodName;
0076: Callable noSuchMethodMethod;
0077:
0078: NoSuchMethodShim(Callable noSuchMethodMethod, String methodName) {
0079: this .noSuchMethodMethod = noSuchMethodMethod;
0080: this .methodName = methodName;
0081: }
0082:
0083: /**
0084: * Perform the call.
0085: *
0086: * @param cx the current Context for this thread
0087: * @param scope the scope to use to resolve properties.
0088: * @param thisObj the JavaScript <code>this</code> object
0089: * @param args the array of arguments
0090: * @return the result of the call
0091: */
0092: public Object call(Context cx, Scriptable scope,
0093: Scriptable this Obj, Object[] args) {
0094: Object[] nestedArgs = new Object[2];
0095:
0096: nestedArgs[0] = methodName;
0097: nestedArgs[1] = newArrayLiteral(args, null, cx, scope);
0098: return noSuchMethodMethod.call(cx, scope, this Obj,
0099: nestedArgs);
0100: }
0101:
0102: }
0103:
0104: /*
0105: * There's such a huge space (and some time) waste for the Foo.class
0106: * syntax: the compiler sticks in a test of a static field in the
0107: * enclosing class for null and the code for creating the class value.
0108: * It has to do this since the reference has to get pushed off til
0109: * executiontime (i.e. can't force an early load), but for the
0110: * 'standard' classes - especially those in java.lang, we can trust
0111: * that they won't cause problems by being loaded early.
0112: */
0113:
0114: public final static Class BooleanClass = Kit
0115: .classOrNull("java.lang.Boolean"), ByteClass = Kit
0116: .classOrNull("java.lang.Byte"), CharacterClass = Kit
0117: .classOrNull("java.lang.Character"), ClassClass = Kit
0118: .classOrNull("java.lang.Class"), DoubleClass = Kit
0119: .classOrNull("java.lang.Double"), FloatClass = Kit
0120: .classOrNull("java.lang.Float"), IntegerClass = Kit
0121: .classOrNull("java.lang.Integer"), LongClass = Kit
0122: .classOrNull("java.lang.Long"), NumberClass = Kit
0123: .classOrNull("java.lang.Number"), ObjectClass = Kit
0124: .classOrNull("java.lang.Object"), ShortClass = Kit
0125: .classOrNull("java.lang.Short"), StringClass = Kit
0126: .classOrNull("java.lang.String"), DateClass = Kit
0127: .classOrNull("java.util.Date");
0128:
0129: public final static Class ContextClass = Kit
0130: .classOrNull("org.mozilla.javascript.Context"),
0131: ContextFactoryClass = Kit
0132: .classOrNull("org.mozilla.javascript.ContextFactory"),
0133: FunctionClass = Kit
0134: .classOrNull("org.mozilla.javascript.Function"),
0135: ScriptableClass = Kit
0136: .classOrNull("org.mozilla.javascript.Scriptable"),
0137: ScriptableObjectClass = Kit
0138: .classOrNull("org.mozilla.javascript.ScriptableObject");
0139:
0140: private static final String[] lazilyNames = { "RegExp",
0141: "org.mozilla.javascript.regexp.NativeRegExp", "Packages",
0142: "org.mozilla.javascript.NativeJavaTopPackage", "java",
0143: "org.mozilla.javascript.NativeJavaTopPackage", "getClass",
0144: "org.mozilla.javascript.NativeJavaTopPackage",
0145: "JavaAdapter", "org.mozilla.javascript.JavaAdapter",
0146: "JavaImporter",
0147: "org.mozilla.javascript.ImporterTopLevel",
0148: // TODO Grotesque hack using literal string (xml) just to minimize
0149: // changes for now
0150: "XML", "(xml)", "XMLList", "(xml)", "Namespace", "(xml)",
0151: "QName", "(xml)", };
0152:
0153: private static final Object LIBRARY_SCOPE_KEY = new Object();
0154:
0155: public static boolean isRhinoRuntimeType(Class cl) {
0156: if (cl.isPrimitive()) {
0157: return (cl != Character.TYPE);
0158: } else {
0159: return (cl == StringClass || cl == BooleanClass
0160: || NumberClass.isAssignableFrom(cl) || ScriptableClass
0161: .isAssignableFrom(cl));
0162: }
0163: }
0164:
0165: public static ScriptableObject initStandardObjects(Context cx,
0166: ScriptableObject scope, boolean sealed) {
0167: if (scope == null) {
0168: scope = new NativeObject();
0169: }
0170: scope.associateValue(LIBRARY_SCOPE_KEY, scope);
0171: (new ClassCache()).associate(scope);
0172:
0173: BaseFunction.init(scope, sealed);
0174: NativeObject.init(scope, sealed);
0175:
0176: Scriptable objectProto = ScriptableObject
0177: .getObjectPrototype(scope);
0178:
0179: // Function.prototype.__proto__ should be Object.prototype
0180: Scriptable functionProto = ScriptableObject
0181: .getFunctionPrototype(scope);
0182: functionProto.setPrototype(objectProto);
0183:
0184: // Set the prototype of the object passed in if need be
0185: if (scope.getPrototype() == null)
0186: scope.setPrototype(objectProto);
0187:
0188: // must precede NativeGlobal since it's needed therein
0189: NativeError.init(scope, sealed);
0190: NativeGlobal.init(cx, scope, sealed);
0191:
0192: NativeArray.init(scope, sealed);
0193: NativeString.init(scope, sealed);
0194: NativeBoolean.init(scope, sealed);
0195: NativeNumber.init(scope, sealed);
0196: NativeDate.init(scope, sealed);
0197: NativeMath.init(scope, sealed);
0198:
0199: NativeWith.init(scope, sealed);
0200: NativeCall.init(scope, sealed);
0201: NativeScript.init(scope, sealed);
0202:
0203: boolean withXml = cx.hasFeature(Context.FEATURE_E4X)
0204: && cx.getE4xImplementationFactory() != null;
0205:
0206: for (int i = 0; i != lazilyNames.length; i += 2) {
0207: String topProperty = lazilyNames[i];
0208: String className = lazilyNames[i + 1];
0209: if (!withXml && className.equals("(xml)")) {
0210: continue;
0211: } else if (withXml && className.equals("(xml)")) {
0212: className = cx.getE4xImplementationFactory()
0213: .getImplementationClassName();
0214: }
0215: new LazilyLoadedCtor(scope, topProperty, className, sealed);
0216: }
0217:
0218: Continuation.init(scope, sealed);
0219:
0220: return scope;
0221: }
0222:
0223: public static ScriptableObject getLibraryScopeOrNull(
0224: Scriptable scope) {
0225: ScriptableObject libScope;
0226: libScope = (ScriptableObject) ScriptableObject
0227: .getTopScopeValue(scope, LIBRARY_SCOPE_KEY);
0228: return libScope;
0229: }
0230:
0231: // It is public so NativeRegExp can access it .
0232: public static boolean isJSLineTerminator(int c) {
0233: // Optimization for faster check for eol character:
0234: // they do not have 0xDFD0 bits set
0235: if ((c & 0xDFD0) != 0) {
0236: return false;
0237: }
0238: return c == '\n' || c == '\r' || c == 0x2028 || c == 0x2029;
0239: }
0240:
0241: public static Boolean wrapBoolean(boolean b) {
0242: return b ? Boolean.TRUE : Boolean.FALSE;
0243: }
0244:
0245: public static Integer wrapInt(int i) {
0246: return new Integer(i);
0247: }
0248:
0249: public static Number wrapNumber(double x) {
0250: if (x != x) {
0251: return ScriptRuntime.NaNobj;
0252: }
0253: return new Double(x);
0254: }
0255:
0256: /**
0257: * Convert the value to a boolean.
0258: *
0259: * See ECMA 9.2.
0260: */
0261: public static boolean toBoolean(Object val) {
0262: for (;;) {
0263: if (val instanceof Boolean)
0264: return ((Boolean) val).booleanValue();
0265: if (val == null || val == Undefined.instance)
0266: return false;
0267: if (val instanceof String)
0268: return ((String) val).length() != 0;
0269: if (val instanceof Number) {
0270: double d = ((Number) val).doubleValue();
0271: return (d == d && d != 0.0);
0272: }
0273: if (val instanceof Scriptable) {
0274: if (Context.getContext().isVersionECMA1()) {
0275: // pure ECMA
0276: return true;
0277: }
0278: // ECMA extension
0279: val = ((Scriptable) val).getDefaultValue(BooleanClass);
0280: if (val instanceof Scriptable)
0281: throw errorWithClassName("msg.primitive.expected",
0282: val);
0283: continue;
0284: }
0285: warnAboutNonJSObject(val);
0286: return true;
0287: }
0288: }
0289:
0290: public static boolean toBoolean(Object[] args, int index) {
0291: return (index < args.length) ? toBoolean(args[index]) : false;
0292: }
0293:
0294: /**
0295: * Convert the value to a number.
0296: *
0297: * See ECMA 9.3.
0298: */
0299: public static double toNumber(Object val) {
0300: for (;;) {
0301: if (val instanceof Number)
0302: return ((Number) val).doubleValue();
0303: if (val == null)
0304: return +0.0;
0305: if (val == Undefined.instance)
0306: return NaN;
0307: if (val instanceof String)
0308: return toNumber((String) val);
0309: if (val instanceof Boolean)
0310: return ((Boolean) val).booleanValue() ? 1 : +0.0;
0311: if (val instanceof Scriptable) {
0312: val = ((Scriptable) val).getDefaultValue(NumberClass);
0313: if (val instanceof Scriptable)
0314: throw errorWithClassName("msg.primitive.expected",
0315: val);
0316: continue;
0317: }
0318: warnAboutNonJSObject(val);
0319: return NaN;
0320: }
0321: }
0322:
0323: public static double toNumber(Object[] args, int index) {
0324: return (index < args.length) ? toNumber(args[index]) : NaN;
0325: }
0326:
0327: // Can not use Double.NaN defined as 0.0d / 0.0 as under the Microsoft VM,
0328: // versions 2.01 and 3.0P1, that causes some uses (returns at least) of
0329: // Double.NaN to be converted to 1.0.
0330: // So we use ScriptRuntime.NaN instead of Double.NaN.
0331: public static final double NaN = Double
0332: .longBitsToDouble(0x7ff8000000000000L);
0333:
0334: // A similar problem exists for negative zero.
0335: public static final double negativeZero = Double
0336: .longBitsToDouble(0x8000000000000000L);
0337:
0338: public static final Double NaNobj = new Double(NaN);
0339:
0340: /*
0341: * Helper function for toNumber, parseInt, and TokenStream.getToken.
0342: */
0343: static double stringToNumber(String s, int start, int radix) {
0344: char digitMax = '9';
0345: char lowerCaseBound = 'a';
0346: char upperCaseBound = 'A';
0347: int len = s.length();
0348: if (radix < 10) {
0349: digitMax = (char) ('0' + radix - 1);
0350: }
0351: if (radix > 10) {
0352: lowerCaseBound = (char) ('a' + radix - 10);
0353: upperCaseBound = (char) ('A' + radix - 10);
0354: }
0355: int end;
0356: double sum = 0.0;
0357: for (end = start; end < len; end++) {
0358: char c = s.charAt(end);
0359: int newDigit;
0360: if ('0' <= c && c <= digitMax)
0361: newDigit = c - '0';
0362: else if ('a' <= c && c < lowerCaseBound)
0363: newDigit = c - 'a' + 10;
0364: else if ('A' <= c && c < upperCaseBound)
0365: newDigit = c - 'A' + 10;
0366: else
0367: break;
0368: sum = sum * radix + newDigit;
0369: }
0370: if (start == end) {
0371: return NaN;
0372: }
0373: if (sum >= 9007199254740992.0) {
0374: if (radix == 10) {
0375: /* If we're accumulating a decimal number and the number
0376: * is >= 2^53, then the result from the repeated multiply-add
0377: * above may be inaccurate. Call Java to get the correct
0378: * answer.
0379: */
0380: try {
0381: return Double.valueOf(s.substring(start, end))
0382: .doubleValue();
0383: } catch (NumberFormatException nfe) {
0384: return NaN;
0385: }
0386: } else if (radix == 2 || radix == 4 || radix == 8
0387: || radix == 16 || radix == 32) {
0388: /* The number may also be inaccurate for one of these bases.
0389: * This happens if the addition in value*radix + digit causes
0390: * a round-down to an even least significant mantissa bit
0391: * when the first dropped bit is a one. If any of the
0392: * following digits in the number (which haven't been added
0393: * in yet) are nonzero then the correct action would have
0394: * been to round up instead of down. An example of this
0395: * occurs when reading the number 0x1000000000000081, which
0396: * rounds to 0x1000000000000000 instead of 0x1000000000000100.
0397: */
0398: int bitShiftInChar = 1;
0399: int digit = 0;
0400:
0401: final int SKIP_LEADING_ZEROS = 0;
0402: final int FIRST_EXACT_53_BITS = 1;
0403: final int AFTER_BIT_53 = 2;
0404: final int ZEROS_AFTER_54 = 3;
0405: final int MIXED_AFTER_54 = 4;
0406:
0407: int state = SKIP_LEADING_ZEROS;
0408: int exactBitsLimit = 53;
0409: double factor = 0.0;
0410: boolean bit53 = false;
0411: // bit54 is the 54th bit (the first dropped from the mantissa)
0412: boolean bit54 = false;
0413:
0414: for (;;) {
0415: if (bitShiftInChar == 1) {
0416: if (start == end)
0417: break;
0418: digit = s.charAt(start++);
0419: if ('0' <= digit && digit <= '9')
0420: digit -= '0';
0421: else if ('a' <= digit && digit <= 'z')
0422: digit -= 'a' - 10;
0423: else
0424: digit -= 'A' - 10;
0425: bitShiftInChar = radix;
0426: }
0427: bitShiftInChar >>= 1;
0428: boolean bit = (digit & bitShiftInChar) != 0;
0429:
0430: switch (state) {
0431: case SKIP_LEADING_ZEROS:
0432: if (bit) {
0433: --exactBitsLimit;
0434: sum = 1.0;
0435: state = FIRST_EXACT_53_BITS;
0436: }
0437: break;
0438: case FIRST_EXACT_53_BITS:
0439: sum *= 2.0;
0440: if (bit)
0441: sum += 1.0;
0442: --exactBitsLimit;
0443: if (exactBitsLimit == 0) {
0444: bit53 = bit;
0445: state = AFTER_BIT_53;
0446: }
0447: break;
0448: case AFTER_BIT_53:
0449: bit54 = bit;
0450: factor = 2.0;
0451: state = ZEROS_AFTER_54;
0452: break;
0453: case ZEROS_AFTER_54:
0454: if (bit) {
0455: state = MIXED_AFTER_54;
0456: }
0457: // fallthrough
0458: case MIXED_AFTER_54:
0459: factor *= 2;
0460: break;
0461: }
0462: }
0463: switch (state) {
0464: case SKIP_LEADING_ZEROS:
0465: sum = 0.0;
0466: break;
0467: case FIRST_EXACT_53_BITS:
0468: case AFTER_BIT_53:
0469: // do nothing
0470: break;
0471: case ZEROS_AFTER_54:
0472: // x1.1 -> x1 + 1 (round up)
0473: // x0.1 -> x0 (round down)
0474: if (bit54 & bit53)
0475: sum += 1.0;
0476: sum *= factor;
0477: break;
0478: case MIXED_AFTER_54:
0479: // x.100...1.. -> x + 1 (round up)
0480: // x.0anything -> x (round down)
0481: if (bit54)
0482: sum += 1.0;
0483: sum *= factor;
0484: break;
0485: }
0486: }
0487: /* We don't worry about inaccurate numbers for any other base. */
0488: }
0489: return sum;
0490: }
0491:
0492: /**
0493: * ToNumber applied to the String type
0494: *
0495: * See ECMA 9.3.1
0496: */
0497: public static double toNumber(String s) {
0498: int len = s.length();
0499: int start = 0;
0500: char startChar;
0501: for (;;) {
0502: if (start == len) {
0503: // Empty or contains only whitespace
0504: return +0.0;
0505: }
0506: startChar = s.charAt(start);
0507: if (!Character.isWhitespace(startChar))
0508: break;
0509: start++;
0510: }
0511:
0512: if (startChar == '0') {
0513: if (start + 2 < len) {
0514: int c1 = s.charAt(start + 1);
0515: if (c1 == 'x' || c1 == 'X') {
0516: // A hexadecimal number
0517: return stringToNumber(s, start + 2, 16);
0518: }
0519: }
0520: } else if (startChar == '+' || startChar == '-') {
0521: if (start + 3 < len && s.charAt(start + 1) == '0') {
0522: int c2 = s.charAt(start + 2);
0523: if (c2 == 'x' || c2 == 'X') {
0524: // A hexadecimal number with sign
0525: double val = stringToNumber(s, start + 3, 16);
0526: return startChar == '-' ? -val : val;
0527: }
0528: }
0529: }
0530:
0531: int end = len - 1;
0532: char endChar;
0533: while (Character.isWhitespace(endChar = s.charAt(end)))
0534: end--;
0535: if (endChar == 'y') {
0536: // check for "Infinity"
0537: if (startChar == '+' || startChar == '-')
0538: start++;
0539: if (start + 7 == end
0540: && s.regionMatches(start, "Infinity", 0, 8))
0541: return startChar == '-' ? Double.NEGATIVE_INFINITY
0542: : Double.POSITIVE_INFINITY;
0543: return NaN;
0544: }
0545: // A non-hexadecimal, non-infinity number:
0546: // just try a normal floating point conversion
0547: String sub = s.substring(start, end + 1);
0548: if (MSJVM_BUG_WORKAROUNDS) {
0549: // The MS JVM will accept non-conformant strings
0550: // rather than throwing a NumberFormatException
0551: // as it should.
0552: for (int i = sub.length() - 1; i >= 0; i--) {
0553: char c = sub.charAt(i);
0554: if (('0' <= c && c <= '9') || c == '.' || c == 'e'
0555: || c == 'E' || c == '+' || c == '-')
0556: continue;
0557: return NaN;
0558: }
0559: }
0560: try {
0561: return Double.valueOf(sub).doubleValue();
0562: } catch (NumberFormatException ex) {
0563: return NaN;
0564: }
0565: }
0566:
0567: /**
0568: * Helper function for builtin objects that use the varargs form.
0569: * ECMA function formal arguments are undefined if not supplied;
0570: * this function pads the argument array out to the expected
0571: * length, if necessary.
0572: */
0573: public static Object[] padArguments(Object[] args, int count) {
0574: if (count < args.length)
0575: return args;
0576:
0577: int i;
0578: Object[] result = new Object[count];
0579: for (i = 0; i < args.length; i++) {
0580: result[i] = args[i];
0581: }
0582:
0583: for (; i < count; i++) {
0584: result[i] = Undefined.instance;
0585: }
0586:
0587: return result;
0588: }
0589:
0590: /* Work around Microsoft Java VM bugs. */
0591: private final static boolean MSJVM_BUG_WORKAROUNDS = true;
0592:
0593: public static String escapeString(String s) {
0594: return escapeString(s, '"');
0595: }
0596:
0597: /**
0598: * For escaping strings printed by object and array literals; not quite
0599: * the same as 'escape.'
0600: */
0601: public static String escapeString(String s, char escapeQuote) {
0602: if (!(escapeQuote == '"' || escapeQuote == '\''))
0603: Kit.codeBug();
0604: StringBuffer sb = null;
0605:
0606: for (int i = 0, L = s.length(); i != L; ++i) {
0607: int c = s.charAt(i);
0608:
0609: if (' ' <= c && c <= '~' && c != escapeQuote && c != '\\') {
0610: // an ordinary print character (like C isprint()) and not "
0611: // or \ .
0612: if (sb != null) {
0613: sb.append((char) c);
0614: }
0615: continue;
0616: }
0617: if (sb == null) {
0618: sb = new StringBuffer(L + 3);
0619: sb.append(s);
0620: sb.setLength(i);
0621: }
0622:
0623: int escape = -1;
0624: switch (c) {
0625: case '\b':
0626: escape = 'b';
0627: break;
0628: case '\f':
0629: escape = 'f';
0630: break;
0631: case '\n':
0632: escape = 'n';
0633: break;
0634: case '\r':
0635: escape = 'r';
0636: break;
0637: case '\t':
0638: escape = 't';
0639: break;
0640: case 0xb:
0641: escape = 'v';
0642: break; // Java lacks \v.
0643: case ' ':
0644: escape = ' ';
0645: break;
0646: case '\\':
0647: escape = '\\';
0648: break;
0649: }
0650: if (escape >= 0) {
0651: // an \escaped sort of character
0652: sb.append('\\');
0653: sb.append((char) escape);
0654: } else if (c == escapeQuote) {
0655: sb.append('\\');
0656: sb.append(escapeQuote);
0657: } else {
0658: int hexSize;
0659: if (c < 256) {
0660: // 2-digit hex
0661: sb.append("\\x");
0662: hexSize = 2;
0663: } else {
0664: // Unicode.
0665: sb.append("\\u");
0666: hexSize = 4;
0667: }
0668: // append hexadecimal form of c left-padded with 0
0669: for (int shift = (hexSize - 1) * 4; shift >= 0; shift -= 4) {
0670: int digit = 0xf & (c >> shift);
0671: int hc = (digit < 10) ? '0' + digit
0672: : 'a' - 10 + digit;
0673: sb.append((char) hc);
0674: }
0675: }
0676: }
0677: return (sb == null) ? s : sb.toString();
0678: }
0679:
0680: static boolean isValidIdentifierName(String s) {
0681: int L = s.length();
0682: if (L == 0)
0683: return false;
0684: if (!Character.isJavaIdentifierStart(s.charAt(0)))
0685: return false;
0686: for (int i = 1; i != L; ++i) {
0687: if (!Character.isJavaIdentifierPart(s.charAt(i)))
0688: return false;
0689: }
0690: return !TokenStream.isKeyword(s);
0691: }
0692:
0693: /**
0694: * Convert the value to a string.
0695: *
0696: * See ECMA 9.8.
0697: */
0698: public static String toString(Object val) {
0699: for (;;) {
0700: if (val == null) {
0701: return "null";
0702: }
0703: if (val == Undefined.instance) {
0704: return "undefined";
0705: }
0706: if (val instanceof String) {
0707: return (String) val;
0708: }
0709: if (val instanceof Number) {
0710: // XXX should we just teach NativeNumber.stringValue()
0711: // about Numbers?
0712: return numberToString(((Number) val).doubleValue(), 10);
0713: }
0714: if (val instanceof Scriptable) {
0715: val = ((Scriptable) val).getDefaultValue(StringClass);
0716: if (val instanceof Scriptable) {
0717: throw errorWithClassName("msg.primitive.expected",
0718: val);
0719: }
0720: continue;
0721: }
0722: return val.toString();
0723: }
0724: }
0725:
0726: static String defaultObjectToString(Scriptable obj) {
0727: return "[object " + obj.getClassName() + ']';
0728: }
0729:
0730: public static String toString(Object[] args, int index) {
0731: return (index < args.length) ? toString(args[index])
0732: : "undefined";
0733: }
0734:
0735: /**
0736: * Optimized version of toString(Object) for numbers.
0737: */
0738: public static String toString(double val) {
0739: return numberToString(val, 10);
0740: }
0741:
0742: public static String numberToString(double d, int base) {
0743: if (d != d)
0744: return "NaN";
0745: if (d == Double.POSITIVE_INFINITY)
0746: return "Infinity";
0747: if (d == Double.NEGATIVE_INFINITY)
0748: return "-Infinity";
0749: if (d == 0.0)
0750: return "0";
0751:
0752: if ((base < 2) || (base > 36)) {
0753: throw Context.reportRuntimeError1("msg.bad.radix", Integer
0754: .toString(base));
0755: }
0756:
0757: if (base != 10) {
0758: return DToA.JS_dtobasestr(base, d);
0759: } else {
0760: StringBuffer result = new StringBuffer();
0761: DToA.JS_dtostr(result, DToA.DTOSTR_STANDARD, 0, d);
0762: return result.toString();
0763: }
0764:
0765: }
0766:
0767: static String uneval(Context cx, Scriptable scope, Object value) {
0768: if (value == null) {
0769: return "null";
0770: }
0771: if (value == Undefined.instance) {
0772: return "undefined";
0773: }
0774: if (value instanceof String) {
0775: String escaped = escapeString((String) value);
0776: StringBuffer sb = new StringBuffer(escaped.length() + 2);
0777: sb.append('\"');
0778: sb.append(escaped);
0779: sb.append('\"');
0780: return sb.toString();
0781: }
0782: if (value instanceof Number) {
0783: double d = ((Number) value).doubleValue();
0784: if (d == 0 && 1 / d < 0) {
0785: return "-0";
0786: }
0787: return toString(d);
0788: }
0789: if (value instanceof Boolean) {
0790: return toString(value);
0791: }
0792: if (value instanceof Scriptable) {
0793: Scriptable obj = (Scriptable) value;
0794: Object v = ScriptableObject.getProperty(obj, "toSource");
0795: if (v instanceof Function) {
0796: Function f = (Function) v;
0797: return toString(f.call(cx, scope, obj, emptyArgs));
0798: }
0799: return toString(value);
0800: }
0801: warnAboutNonJSObject(value);
0802: return value.toString();
0803: }
0804:
0805: static String defaultObjectToSource(Context cx, Scriptable scope,
0806: Scriptable this Obj, Object[] args) {
0807: boolean toplevel, iterating;
0808: if (cx.iterating == null) {
0809: toplevel = true;
0810: iterating = false;
0811: cx.iterating = new ObjToIntMap(31);
0812: } else {
0813: toplevel = false;
0814: iterating = cx.iterating.has(this Obj);
0815: }
0816:
0817: StringBuffer result = new StringBuffer(128);
0818: if (toplevel) {
0819: result.append("(");
0820: }
0821: result.append('{');
0822:
0823: // Make sure cx.iterating is set to null when done
0824: // so we don't leak memory
0825: try {
0826: if (!iterating) {
0827: cx.iterating.intern(this Obj); // stop recursion.
0828: Object[] ids = this Obj.getIds();
0829: for (int i = 0; i < ids.length; i++) {
0830: Object id = ids[i];
0831: Object value;
0832: if (id instanceof Integer) {
0833: int intId = ((Integer) id).intValue();
0834: value = this Obj.get(intId, this Obj);
0835: if (value == Scriptable.NOT_FOUND)
0836: continue; // a property has been removed
0837: if (i > 0)
0838: result.append(", ");
0839: result.append(intId);
0840: } else {
0841: String strId = (String) id;
0842: value = this Obj.get(strId, this Obj);
0843: if (value == Scriptable.NOT_FOUND)
0844: continue; // a property has been removed
0845: if (i > 0)
0846: result.append(", ");
0847: if (ScriptRuntime.isValidIdentifierName(strId)) {
0848: result.append(strId);
0849: } else {
0850: result.append('\'');
0851: result.append(ScriptRuntime.escapeString(
0852: strId, '\''));
0853: result.append('\'');
0854: }
0855: }
0856: result.append(':');
0857: result.append(ScriptRuntime
0858: .uneval(cx, scope, value));
0859: }
0860: }
0861: } finally {
0862: if (toplevel) {
0863: cx.iterating = null;
0864: }
0865: }
0866:
0867: result.append('}');
0868: if (toplevel) {
0869: result.append(')');
0870: }
0871: return result.toString();
0872: }
0873:
0874: public static Scriptable toObject(Scriptable scope, Object val) {
0875: if (val instanceof Scriptable) {
0876: return (Scriptable) val;
0877: }
0878: return toObject(Context.getContext(), scope, val);
0879: }
0880:
0881: public static Scriptable toObjectOrNull(Context cx, Object obj) {
0882: if (obj instanceof Scriptable) {
0883: return (Scriptable) obj;
0884: } else if (obj != null && obj != Undefined.instance) {
0885: return toObject(cx, getTopCallScope(cx), obj);
0886: }
0887: return null;
0888: }
0889:
0890: /**
0891: * @deprecated Use {@link #toObject(Scriptable, Object)} instead.
0892: */
0893: public static Scriptable toObject(Scriptable scope, Object val,
0894: Class staticClass) {
0895: if (val instanceof Scriptable) {
0896: return (Scriptable) val;
0897: }
0898: return toObject(Context.getContext(), scope, val);
0899: }
0900:
0901: /**
0902: * Convert the value to an object.
0903: *
0904: * See ECMA 9.9.
0905: */
0906: public static Scriptable toObject(Context cx, Scriptable scope,
0907: Object val) {
0908: if (val instanceof Scriptable) {
0909: return (Scriptable) val;
0910: }
0911: if (val == null) {
0912: throw typeError0("msg.null.to.object");
0913: }
0914: if (val == Undefined.instance) {
0915: throw typeError0("msg.undef.to.object");
0916: }
0917: String className = val instanceof String ? "String"
0918: : val instanceof Number ? "Number"
0919: : val instanceof Boolean ? "Boolean" : null;
0920: if (className != null) {
0921: Object[] args = { val };
0922: scope = ScriptableObject.getTopLevelScope(scope);
0923: return newObject(cx, scope, className, args);
0924: }
0925:
0926: // Extension: Wrap as a LiveConnect object.
0927: Object wrapped = cx.getWrapFactory().wrap(cx, scope, val, null);
0928: if (wrapped instanceof Scriptable)
0929: return (Scriptable) wrapped;
0930: throw errorWithClassName("msg.invalid.type", val);
0931: }
0932:
0933: /**
0934: * @deprecated Use {@link #toObject(Context, Scriptable, Object)} instead.
0935: */
0936: public static Scriptable toObject(Context cx, Scriptable scope,
0937: Object val, Class staticClass) {
0938: return toObject(cx, scope, val);
0939: }
0940:
0941: /**
0942: * @deprecated The method is only present for compatibility.
0943: */
0944: public static Object call(Context cx, Object fun, Object this Arg,
0945: Object[] args, Scriptable scope) {
0946: if (!(fun instanceof Function)) {
0947: throw notFunctionError(toString(fun));
0948: }
0949: Function function = (Function) fun;
0950: Scriptable this Obj = toObjectOrNull(cx, this Arg);
0951: if (this Obj == null) {
0952: throw undefCallError(this Obj, "function");
0953: }
0954: return function.call(cx, scope, this Obj, args);
0955: }
0956:
0957: public static Scriptable newObject(Context cx, Scriptable scope,
0958: String constructorName, Object[] args) {
0959: scope = ScriptableObject.getTopLevelScope(scope);
0960: Function ctor = getExistingCtor(cx, scope, constructorName);
0961: if (args == null) {
0962: args = ScriptRuntime.emptyArgs;
0963: }
0964: return ctor.construct(cx, scope, args);
0965: }
0966:
0967: /**
0968: *
0969: * See ECMA 9.4.
0970: */
0971: public static double toInteger(Object val) {
0972: return toInteger(toNumber(val));
0973: }
0974:
0975: // convenience method
0976: public static double toInteger(double d) {
0977: // if it's NaN
0978: if (d != d)
0979: return +0.0;
0980:
0981: if (d == 0.0 || d == Double.POSITIVE_INFINITY
0982: || d == Double.NEGATIVE_INFINITY)
0983: return d;
0984:
0985: if (d > 0.0)
0986: return Math.floor(d);
0987: else
0988: return Math.ceil(d);
0989: }
0990:
0991: public static double toInteger(Object[] args, int index) {
0992: return (index < args.length) ? toInteger(args[index]) : +0.0;
0993: }
0994:
0995: /**
0996: *
0997: * See ECMA 9.5.
0998: */
0999: public static int toInt32(Object val) {
1000: // short circuit for common integer values
1001: if (val instanceof Integer)
1002: return ((Integer) val).intValue();
1003:
1004: return toInt32(toNumber(val));
1005: }
1006:
1007: public static int toInt32(Object[] args, int index) {
1008: return (index < args.length) ? toInt32(args[index]) : 0;
1009: }
1010:
1011: public static int toInt32(double d) {
1012: int id = (int) d;
1013: if (id == d) {
1014: // This covers -0.0 as well
1015: return id;
1016: }
1017:
1018: if (d != d || d == Double.POSITIVE_INFINITY
1019: || d == Double.NEGATIVE_INFINITY) {
1020: return 0;
1021: }
1022:
1023: d = (d >= 0) ? Math.floor(d) : Math.ceil(d);
1024:
1025: double two32 = 4294967296.0;
1026: d = Math.IEEEremainder(d, two32);
1027: // (double)(long)d == d should hold here
1028:
1029: long l = (long) d;
1030: // returning (int)d does not work as d can be outside int range
1031: // but the result must always be 32 lower bits of l
1032: return (int) l;
1033: }
1034:
1035: /**
1036: * See ECMA 9.6.
1037: * @return long value representing 32 bits unsigned integer
1038: */
1039: public static long toUint32(double d) {
1040: long l = (long) d;
1041: if (l == d) {
1042: // This covers -0.0 as well
1043: return l & 0xffffffffL;
1044: }
1045:
1046: if (d != d || d == Double.POSITIVE_INFINITY
1047: || d == Double.NEGATIVE_INFINITY) {
1048: return 0;
1049: }
1050:
1051: d = (d >= 0) ? Math.floor(d) : Math.ceil(d);
1052:
1053: // 0x100000000 gives me a numeric overflow...
1054: double two32 = 4294967296.0;
1055: l = (long) Math.IEEEremainder(d, two32);
1056:
1057: return l & 0xffffffffL;
1058: }
1059:
1060: public static long toUint32(Object val) {
1061: return toUint32(toNumber(val));
1062: }
1063:
1064: /**
1065: *
1066: * See ECMA 9.7.
1067: */
1068: public static char toUint16(Object val) {
1069: double d = toNumber(val);
1070:
1071: int i = (int) d;
1072: if (i == d) {
1073: return (char) i;
1074: }
1075:
1076: if (d != d || d == Double.POSITIVE_INFINITY
1077: || d == Double.NEGATIVE_INFINITY) {
1078: return 0;
1079: }
1080:
1081: d = (d >= 0) ? Math.floor(d) : Math.ceil(d);
1082:
1083: int int16 = 0x10000;
1084: i = (int) Math.IEEEremainder(d, int16);
1085:
1086: return (char) i;
1087: }
1088:
1089: // XXX: this is until setDefaultNamespace will learn how to store NS
1090: // properly and separates namespace form Scriptable.get etc.
1091: private static final String DEFAULT_NS_TAG = "__default_namespace__";
1092:
1093: public static Object setDefaultNamespace(Object namespace,
1094: Context cx) {
1095: Scriptable scope = cx.currentActivationCall;
1096: if (scope == null) {
1097: scope = getTopCallScope(cx);
1098: }
1099:
1100: XMLLib xmlLib = currentXMLLib(cx);
1101: Object ns = xmlLib.toDefaultXmlNamespace(cx, namespace);
1102:
1103: // XXX : this should be in separated namesapce from Scriptable.get/put
1104: if (!scope.has(DEFAULT_NS_TAG, scope)) {
1105: // XXX: this is racy of cause
1106: ScriptableObject.defineProperty(scope, DEFAULT_NS_TAG, ns,
1107: ScriptableObject.PERMANENT
1108: | ScriptableObject.DONTENUM);
1109: } else {
1110: scope.put(DEFAULT_NS_TAG, scope, ns);
1111: }
1112:
1113: return Undefined.instance;
1114: }
1115:
1116: public static Object searchDefaultNamespace(Context cx) {
1117: Scriptable scope = cx.currentActivationCall;
1118: if (scope == null) {
1119: scope = getTopCallScope(cx);
1120: }
1121: Object nsObject;
1122: for (;;) {
1123: Scriptable parent = scope.getParentScope();
1124: if (parent == null) {
1125: nsObject = ScriptableObject.getProperty(scope,
1126: DEFAULT_NS_TAG);
1127: if (nsObject == Scriptable.NOT_FOUND) {
1128: return null;
1129: }
1130: break;
1131: }
1132: nsObject = scope.get(DEFAULT_NS_TAG, scope);
1133: if (nsObject != Scriptable.NOT_FOUND) {
1134: break;
1135: }
1136: scope = parent;
1137: }
1138: return nsObject;
1139: }
1140:
1141: public static Object getTopLevelProp(Scriptable scope, String id) {
1142: scope = ScriptableObject.getTopLevelScope(scope);
1143: return ScriptableObject.getProperty(scope, id);
1144: }
1145:
1146: static Function getExistingCtor(Context cx, Scriptable scope,
1147: String constructorName) {
1148: Object ctorVal = ScriptableObject.getProperty(scope,
1149: constructorName);
1150: if (ctorVal instanceof Function) {
1151: return (Function) ctorVal;
1152: }
1153: if (ctorVal == Scriptable.NOT_FOUND) {
1154: throw Context.reportRuntimeError1("msg.ctor.not.found",
1155: constructorName);
1156: } else {
1157: throw Context.reportRuntimeError1("msg.not.ctor",
1158: constructorName);
1159: }
1160: }
1161:
1162: /**
1163: * Return -1L if str is not an index or the index value as lower 32
1164: * bits of the result.
1165: */
1166: private static long indexFromString(String str) {
1167: // The length of the decimal string representation of
1168: // Integer.MAX_VALUE, 2147483647
1169: final int MAX_VALUE_LENGTH = 10;
1170:
1171: int len = str.length();
1172: if (len > 0) {
1173: int i = 0;
1174: boolean negate = false;
1175: int c = str.charAt(0);
1176: if (c == '-') {
1177: if (len > 1) {
1178: c = str.charAt(1);
1179: i = 1;
1180: negate = true;
1181: }
1182: }
1183: c -= '0';
1184: if (0 <= c
1185: && c <= 9
1186: && len <= (negate ? MAX_VALUE_LENGTH + 1
1187: : MAX_VALUE_LENGTH)) {
1188: // Use negative numbers to accumulate index to handle
1189: // Integer.MIN_VALUE that is greater by 1 in absolute value
1190: // then Integer.MAX_VALUE
1191: int index = -c;
1192: int oldIndex = 0;
1193: i++;
1194: if (index != 0) {
1195: // Note that 00, 01, 000 etc. are not indexes
1196: while (i != len && 0 <= (c = str.charAt(i) - '0')
1197: && c <= 9) {
1198: oldIndex = index;
1199: index = 10 * index - c;
1200: i++;
1201: }
1202: }
1203: // Make sure all characters were consumed and that it couldn't
1204: // have overflowed.
1205: if (i == len
1206: && (oldIndex > (Integer.MIN_VALUE / 10) || (oldIndex == (Integer.MIN_VALUE / 10) && c <= (negate ? -(Integer.MIN_VALUE % 10)
1207: : (Integer.MAX_VALUE % 10))))) {
1208: return 0xFFFFFFFFL & (negate ? index : -index);
1209: }
1210: }
1211: }
1212: return -1L;
1213: }
1214:
1215: /**
1216: * If str is a decimal presentation of Uint32 value, return it as long.
1217: * Othewise return -1L;
1218: */
1219: public static long testUint32String(String str) {
1220: // The length of the decimal string representation of
1221: // UINT32_MAX_VALUE, 4294967296
1222: final int MAX_VALUE_LENGTH = 10;
1223:
1224: int len = str.length();
1225: if (1 <= len && len <= MAX_VALUE_LENGTH) {
1226: int c = str.charAt(0);
1227: c -= '0';
1228: if (c == 0) {
1229: // Note that 00,01 etc. are not valid Uint32 presentations
1230: return (len == 1) ? 0L : -1L;
1231: }
1232: if (1 <= c && c <= 9) {
1233: long v = c;
1234: for (int i = 1; i != len; ++i) {
1235: c = str.charAt(i) - '0';
1236: if (!(0 <= c && c <= 9)) {
1237: return -1;
1238: }
1239: v = 10 * v + c;
1240: }
1241: // Check for overflow
1242: if ((v >>> 32) == 0) {
1243: return v;
1244: }
1245: }
1246: }
1247: return -1;
1248: }
1249:
1250: /**
1251: * If s represents index, then return index value wrapped as Integer
1252: * and othewise return s.
1253: */
1254: static Object getIndexObject(String s) {
1255: long indexTest = indexFromString(s);
1256: if (indexTest >= 0) {
1257: return new Integer((int) indexTest);
1258: }
1259: return s;
1260: }
1261:
1262: /**
1263: * If d is exact int value, return its value wrapped as Integer
1264: * and othewise return d converted to String.
1265: */
1266: static Object getIndexObject(double d) {
1267: int i = (int) d;
1268: if (i == d) {
1269: return new Integer(i);
1270: }
1271: return toString(d);
1272: }
1273:
1274: /**
1275: * If toString(id) is a decimal presentation of int32 value, then id
1276: * is index. In this case return null and make the index available
1277: * as ScriptRuntime.lastIndexResult(cx). Otherwise return toString(id).
1278: */
1279: static String toStringIdOrIndex(Context cx, Object id) {
1280: if (id instanceof Number) {
1281: double d = ((Number) id).doubleValue();
1282: int index = (int) d;
1283: if (index == d) {
1284: storeIndexResult(cx, index);
1285: return null;
1286: }
1287: return toString(id);
1288: } else {
1289: String s;
1290: if (id instanceof String) {
1291: s = (String) id;
1292: } else {
1293: s = toString(id);
1294: }
1295: long indexTest = indexFromString(s);
1296: if (indexTest >= 0) {
1297: storeIndexResult(cx, (int) indexTest);
1298: return null;
1299: }
1300: return s;
1301: }
1302: }
1303:
1304: /**
1305: * Call obj.[[Get]](id)
1306: */
1307: public static Object getObjectElem(Object obj, Object elem,
1308: Context cx) {
1309: Scriptable sobj = toObjectOrNull(cx, obj);
1310: if (sobj == null) {
1311: throw undefReadError(obj, elem);
1312: }
1313: return getObjectElem(sobj, elem, cx);
1314: }
1315:
1316: public static Object getObjectElem(Scriptable obj, Object elem,
1317: Context cx) {
1318: if (obj instanceof XMLObject) {
1319: XMLObject xmlObject = (XMLObject) obj;
1320: return xmlObject.ecmaGet(cx, elem);
1321: }
1322:
1323: Object result;
1324:
1325: String s = toStringIdOrIndex(cx, elem);
1326: if (s == null) {
1327: int index = lastIndexResult(cx);
1328: result = ScriptableObject.getProperty(obj, index);
1329: } else {
1330: result = ScriptableObject.getProperty(obj, s);
1331: }
1332:
1333: if (result == Scriptable.NOT_FOUND) {
1334: result = Undefined.instance;
1335: }
1336:
1337: return result;
1338: }
1339:
1340: /**
1341: * Version of getObjectElem when elem is a valid JS identifier name.
1342: */
1343: public static Object getObjectProp(Object obj, String property,
1344: Context cx) {
1345: Scriptable sobj = toObjectOrNull(cx, obj);
1346: if (sobj == null) {
1347: throw undefReadError(obj, property);
1348: }
1349: return getObjectProp(sobj, property, cx);
1350: }
1351:
1352: public static Object getObjectProp(Scriptable obj, String property,
1353: Context cx) {
1354: if (obj instanceof XMLObject) {
1355: XMLObject xmlObject = (XMLObject) obj;
1356: return xmlObject.ecmaGet(cx, property);
1357: }
1358:
1359: Object result = ScriptableObject.getProperty(obj, property);
1360: if (result == Scriptable.NOT_FOUND) {
1361: if (cx.hasFeature(Context.FEATURE_STRICT_MODE)) {
1362: Context.reportWarning(ScriptRuntime.getMessage1(
1363: "msg.ref.undefined.prop", property)
1364: // <netbeans>
1365: , "msg.ref.undefined.prop", property
1366: // </netbeans>
1367: );
1368: }
1369: result = Undefined.instance;
1370: }
1371:
1372: return result;
1373: }
1374:
1375: /*
1376: * A cheaper and less general version of the above for well-known argument
1377: * types.
1378: */
1379: public static Object getObjectIndex(Object obj, double dblIndex,
1380: Context cx) {
1381: Scriptable sobj = toObjectOrNull(cx, obj);
1382: if (sobj == null) {
1383: throw undefReadError(obj, toString(dblIndex));
1384: }
1385:
1386: int index = (int) dblIndex;
1387: if (index == dblIndex) {
1388: return getObjectIndex(sobj, index, cx);
1389: } else {
1390: String s = toString(dblIndex);
1391: return getObjectProp(sobj, s, cx);
1392: }
1393: }
1394:
1395: public static Object getObjectIndex(Scriptable obj, int index,
1396: Context cx) {
1397: if (obj instanceof XMLObject) {
1398: XMLObject xmlObject = (XMLObject) obj;
1399: return xmlObject.ecmaGet(cx, new Integer(index));
1400: }
1401:
1402: Object result = ScriptableObject.getProperty(obj, index);
1403: if (result == Scriptable.NOT_FOUND) {
1404: result = Undefined.instance;
1405: }
1406:
1407: return result;
1408: }
1409:
1410: /*
1411: * Call obj.[[Put]](id, value)
1412: */
1413: public static Object setObjectElem(Object obj, Object elem,
1414: Object value, Context cx) {
1415: Scriptable sobj = toObjectOrNull(cx, obj);
1416: if (sobj == null) {
1417: throw undefWriteError(obj, elem, value);
1418: }
1419: return setObjectElem(sobj, elem, value, cx);
1420: }
1421:
1422: public static Object setObjectElem(Scriptable obj, Object elem,
1423: Object value, Context cx) {
1424: if (obj instanceof XMLObject) {
1425: XMLObject xmlObject = (XMLObject) obj;
1426: xmlObject.ecmaPut(cx, elem, value);
1427: return value;
1428: }
1429:
1430: String s = toStringIdOrIndex(cx, elem);
1431: if (s == null) {
1432: int index = lastIndexResult(cx);
1433: ScriptableObject.putProperty(obj, index, value);
1434: } else {
1435: ScriptableObject.putProperty(obj, s, value);
1436: }
1437:
1438: return value;
1439: }
1440:
1441: /**
1442: * Version of setObjectElem when elem is a valid JS identifier name.
1443: */
1444: public static Object setObjectProp(Object obj, String property,
1445: Object value, Context cx) {
1446: Scriptable sobj = toObjectOrNull(cx, obj);
1447: if (sobj == null) {
1448: throw undefWriteError(obj, property, value);
1449: }
1450: return setObjectProp(sobj, property, value, cx);
1451: }
1452:
1453: public static Object setObjectProp(Scriptable obj, String property,
1454: Object value, Context cx) {
1455: if (obj instanceof XMLObject) {
1456: XMLObject xmlObject = (XMLObject) obj;
1457: xmlObject.ecmaPut(cx, property, value);
1458: } else {
1459: ScriptableObject.putProperty(obj, property, value);
1460: }
1461: return value;
1462: }
1463:
1464: /*
1465: * A cheaper and less general version of the above for well-known argument
1466: * types.
1467: */
1468: public static Object setObjectIndex(Object obj, double dblIndex,
1469: Object value, Context cx) {
1470: Scriptable sobj = toObjectOrNull(cx, obj);
1471: if (sobj == null) {
1472: throw undefWriteError(obj, String.valueOf(dblIndex), value);
1473: }
1474:
1475: int index = (int) dblIndex;
1476: if (index == dblIndex) {
1477: return setObjectIndex(sobj, index, value, cx);
1478: } else {
1479: String s = toString(dblIndex);
1480: return setObjectProp(sobj, s, value, cx);
1481: }
1482: }
1483:
1484: public static Object setObjectIndex(Scriptable obj, int index,
1485: Object value, Context cx) {
1486: if (obj instanceof XMLObject) {
1487: XMLObject xmlObject = (XMLObject) obj;
1488: xmlObject.ecmaPut(cx, new Integer(index), value);
1489: } else {
1490: ScriptableObject.putProperty(obj, index, value);
1491: }
1492: return value;
1493: }
1494:
1495: public static boolean deleteObjectElem(Scriptable target,
1496: Object elem, Context cx) {
1497: boolean result;
1498: if (target instanceof XMLObject) {
1499: XMLObject xmlObject = (XMLObject) target;
1500: result = xmlObject.ecmaDelete(cx, elem);
1501: } else {
1502: String s = toStringIdOrIndex(cx, elem);
1503: if (s == null) {
1504: int index = lastIndexResult(cx);
1505: result = ScriptableObject.deleteProperty(target, index);
1506: } else {
1507: result = ScriptableObject.deleteProperty(target, s);
1508: }
1509: }
1510: return result;
1511: }
1512:
1513: public static boolean hasObjectElem(Scriptable target, Object elem,
1514: Context cx) {
1515: boolean result;
1516:
1517: if (target instanceof XMLObject) {
1518: XMLObject xmlObject = (XMLObject) target;
1519: result = xmlObject.ecmaHas(cx, elem);
1520: } else {
1521: String s = toStringIdOrIndex(cx, elem);
1522: if (s == null) {
1523: int index = lastIndexResult(cx);
1524: result = ScriptableObject.hasProperty(target, index);
1525: } else {
1526: result = ScriptableObject.hasProperty(target, s);
1527: }
1528: }
1529:
1530: return result;
1531: }
1532:
1533: public static Object refGet(Ref ref, Context cx) {
1534: return ref.get(cx);
1535: }
1536:
1537: public static Object refSet(Ref ref, Object value, Context cx) {
1538: return ref.set(cx, value);
1539: }
1540:
1541: public static Object refDel(Ref ref, Context cx) {
1542: return wrapBoolean(ref.delete(cx));
1543: }
1544:
1545: static boolean isSpecialProperty(String s) {
1546: return s.equals("__proto__") || s.equals("__parent__");
1547: }
1548:
1549: public static Ref specialRef(Object obj, String specialProperty,
1550: Context cx) {
1551: return SpecialRef.createSpecial(cx, obj, specialProperty);
1552: }
1553:
1554: /**
1555: * The delete operator
1556: *
1557: * See ECMA 11.4.1
1558: *
1559: * In ECMA 0.19, the description of the delete operator (11.4.1)
1560: * assumes that the [[Delete]] method returns a value. However,
1561: * the definition of the [[Delete]] operator (8.6.2.5) does not
1562: * define a return value. Here we assume that the [[Delete]]
1563: * method doesn't return a value.
1564: */
1565: public static Object delete(Object obj, Object id, Context cx) {
1566: Scriptable sobj = toObjectOrNull(cx, obj);
1567: if (sobj == null) {
1568: String idStr = (id == null) ? "null" : id.toString();
1569: throw typeError2("msg.undef.prop.delete", toString(obj),
1570: idStr);
1571: }
1572: boolean result = deleteObjectElem(sobj, id, cx);
1573: return wrapBoolean(result);
1574: }
1575:
1576: /**
1577: * Looks up a name in the scope chain and returns its value.
1578: */
1579: public static Object name(Context cx, Scriptable scope, String name) {
1580: Scriptable parent = scope.getParentScope();
1581: if (parent == null) {
1582: Object result = topScopeName(cx, scope, name);
1583: if (result == Scriptable.NOT_FOUND) {
1584: throw notFoundError(scope, name);
1585: }
1586: return result;
1587: }
1588:
1589: return nameOrFunction(cx, scope, parent, name, false);
1590: }
1591:
1592: private static Object nameOrFunction(Context cx, Scriptable scope,
1593: Scriptable parentScope, String name, boolean asFunctionCall) {
1594: Object result;
1595: Scriptable this Obj = scope; // It is used only if asFunctionCall==true.
1596:
1597: XMLObject firstXMLObject = null;
1598: for (;;) {
1599: if (scope instanceof NativeWith) {
1600: Scriptable withObj = scope.getPrototype();
1601: if (withObj instanceof XMLObject) {
1602: XMLObject xmlObj = (XMLObject) withObj;
1603: if (xmlObj.ecmaHas(cx, name)) {
1604: // function this should be the target object of with
1605: this Obj = xmlObj;
1606: result = xmlObj.ecmaGet(cx, name);
1607: break;
1608: }
1609: if (firstXMLObject == null) {
1610: firstXMLObject = xmlObj;
1611: }
1612: } else {
1613: result = ScriptableObject
1614: .getProperty(withObj, name);
1615: if (result != Scriptable.NOT_FOUND) {
1616: // function this should be the target object of with
1617: this Obj = withObj;
1618: break;
1619: }
1620: }
1621: } else if (scope instanceof NativeCall) {
1622: // NativeCall does not prototype chain and Scriptable.get
1623: // can be called directly.
1624: result = scope.get(name, scope);
1625: if (result != Scriptable.NOT_FOUND) {
1626: if (asFunctionCall) {
1627: // ECMA 262 requires that this for nested funtions
1628: // should be top scope
1629: this Obj = ScriptableObject
1630: .getTopLevelScope(parentScope);
1631: }
1632: break;
1633: }
1634: } else {
1635: // Can happen if Rhino embedding decided that nested
1636: // scopes are useful for what ever reasons.
1637: result = ScriptableObject.getProperty(scope, name);
1638: if (result != Scriptable.NOT_FOUND) {
1639: this Obj = scope;
1640: break;
1641: }
1642: }
1643: scope = parentScope;
1644: parentScope = parentScope.getParentScope();
1645: if (parentScope == null) {
1646: result = topScopeName(cx, scope, name);
1647: if (result == Scriptable.NOT_FOUND) {
1648: if (firstXMLObject == null || asFunctionCall) {
1649: throw notFoundError(scope, name);
1650: }
1651: // The name was not found, but we did find an XML
1652: // object in the scope chain and we are looking for name,
1653: // not function. The result should be an empty XMLList
1654: // in name context.
1655: result = firstXMLObject.ecmaGet(cx, name);
1656: }
1657: // For top scope thisObj for functions is always scope itself.
1658: this Obj = scope;
1659: break;
1660: }
1661: }
1662:
1663: if (asFunctionCall) {
1664: if (!(result instanceof Callable)) {
1665: throw notFunctionError(result, name);
1666: }
1667: storeScriptable(cx, this Obj);
1668: }
1669:
1670: return result;
1671: }
1672:
1673: private static Object topScopeName(Context cx, Scriptable scope,
1674: String name) {
1675: if (cx.useDynamicScope) {
1676: scope = checkDynamicScope(cx.topCallScope, scope);
1677: }
1678: return ScriptableObject.getProperty(scope, name);
1679: }
1680:
1681: /**
1682: * Returns the object in the scope chain that has a given property.
1683: *
1684: * The order of evaluation of an assignment expression involves
1685: * evaluating the lhs to a reference, evaluating the rhs, and then
1686: * modifying the reference with the rhs value. This method is used
1687: * to 'bind' the given name to an object containing that property
1688: * so that the side effects of evaluating the rhs do not affect
1689: * which property is modified.
1690: * Typically used in conjunction with setName.
1691: *
1692: * See ECMA 10.1.4
1693: */
1694: public static Scriptable bind(Context cx, Scriptable scope,
1695: String id) {
1696: Scriptable firstXMLObject = null;
1697: Scriptable parent = scope.getParentScope();
1698: childScopesChecks: if (parent != null) {
1699: // Check for possibly nested "with" scopes first
1700: while (scope instanceof NativeWith) {
1701: Scriptable withObj = scope.getPrototype();
1702: if (withObj instanceof XMLObject) {
1703: XMLObject xmlObject = (XMLObject) withObj;
1704: if (xmlObject.ecmaHas(cx, id)) {
1705: return xmlObject;
1706: }
1707: if (firstXMLObject == null) {
1708: firstXMLObject = xmlObject;
1709: }
1710: } else {
1711: if (ScriptableObject.hasProperty(withObj, id)) {
1712: return withObj;
1713: }
1714: }
1715: scope = parent;
1716: parent = parent.getParentScope();
1717: if (parent == null) {
1718: break childScopesChecks;
1719: }
1720: }
1721: for (;;) {
1722: if (ScriptableObject.hasProperty(scope, id)) {
1723: return scope;
1724: }
1725: scope = parent;
1726: parent = parent.getParentScope();
1727: if (parent == null) {
1728: break childScopesChecks;
1729: }
1730: }
1731: }
1732: // scope here is top scope
1733: if (cx.useDynamicScope) {
1734: scope = checkDynamicScope(cx.topCallScope, scope);
1735: }
1736: if (ScriptableObject.hasProperty(scope, id)) {
1737: return scope;
1738: }
1739: // Nothing was found, but since XML objects always bind
1740: // return one if found
1741: return firstXMLObject;
1742: }
1743:
1744: public static Object setName(Scriptable bound, Object value,
1745: Context cx, Scriptable scope, String id) {
1746: if (bound != null) {
1747: if (bound instanceof XMLObject) {
1748: XMLObject xmlObject = (XMLObject) bound;
1749: xmlObject.ecmaPut(cx, id, value);
1750: } else {
1751: ScriptableObject.putProperty(bound, id, value);
1752: }
1753: } else {
1754: // "newname = 7;", where 'newname' has not yet
1755: // been defined, creates a new property in the
1756: // top scope unless strict mode is specified.
1757: if (cx.hasFeature(Context.FEATURE_STRICT_MODE)
1758: || cx.hasFeature(Context.FEATURE_STRICT_VARS)) {
1759: Context.reportWarning(ScriptRuntime.getMessage1(
1760: "msg.assn.create.strict", id)
1761: // <netbeans>
1762: , "msg.assn.create.strict", id
1763: // </netbeans>
1764: );
1765: }
1766: // Find the top scope by walking up the scope chain.
1767: bound = ScriptableObject.getTopLevelScope(scope);
1768: if (cx.useDynamicScope) {
1769: bound = checkDynamicScope(cx.topCallScope, bound);
1770: }
1771: bound.put(id, bound, value);
1772: }
1773: return value;
1774: }
1775:
1776: public static Object setConst(Scriptable bound, Object value,
1777: Context cx, String id) {
1778: if (bound instanceof XMLObject) {
1779: XMLObject xmlObject = (XMLObject) bound;
1780: xmlObject.ecmaPut(cx, id, value);
1781: } else {
1782: ScriptableObject.putConstProperty(bound, id, value);
1783: }
1784: return value;
1785: }
1786:
1787: /**
1788: * This is the enumeration needed by the for..in statement.
1789: *
1790: * See ECMA 12.6.3.
1791: *
1792: * IdEnumeration maintains a ObjToIntMap to make sure a given
1793: * id is enumerated only once across multiple objects in a
1794: * prototype chain.
1795: *
1796: * XXX - ECMA delete doesn't hide properties in the prototype,
1797: * but js/ref does. This means that the js/ref for..in can
1798: * avoid maintaining a hash table and instead perform lookups
1799: * to see if a given property has already been enumerated.
1800: *
1801: */
1802: private static class IdEnumeration {
1803: Scriptable obj;
1804: Object[] ids;
1805: int index;
1806: ObjToIntMap used;
1807: String currentId;
1808: boolean enumValues;
1809: }
1810:
1811: public static Object enumInit(Object value, Context cx,
1812: boolean enumValues) {
1813: IdEnumeration x = new IdEnumeration();
1814: x.obj = toObjectOrNull(cx, value);
1815: if (x.obj != null) {
1816: // null or undefined do not cause errors but rather lead to empty
1817: // "for in" loop
1818: x.enumValues = enumValues;
1819: // enumInit should read all initial ids before returning
1820: // or "for (a.i in a)" would wrongly enumerate i in a as well
1821: enumChangeObject(x);
1822: }
1823: return x;
1824: }
1825:
1826: public static Boolean enumNext(Object enumObj) {
1827: // OPT this could be more efficient
1828: boolean result;
1829: IdEnumeration x = (IdEnumeration) enumObj;
1830: for (;;) {
1831: if (x.obj == null) {
1832: result = false;
1833: break;
1834: }
1835: if (x.index == x.ids.length) {
1836: x.obj = x.obj.getPrototype();
1837: enumChangeObject(x);
1838: continue;
1839: }
1840: Object id = x.ids[x.index++];
1841: if (x.used != null && x.used.has(id)) {
1842: continue;
1843: }
1844: if (id instanceof String) {
1845: String strId = (String) id;
1846: if (!x.obj.has(strId, x.obj))
1847: continue; // must have been deleted
1848: x.currentId = strId;
1849: } else {
1850: int intId = ((Number) id).intValue();
1851: if (!x.obj.has(intId, x.obj))
1852: continue; // must have been deleted
1853: x.currentId = String.valueOf(intId);
1854: }
1855: result = true;
1856: break;
1857: }
1858: return wrapBoolean(result);
1859: }
1860:
1861: public static Object enumId(Object enumObj, Context cx) {
1862: IdEnumeration x = (IdEnumeration) enumObj;
1863: if (!x.enumValues)
1864: return x.currentId;
1865:
1866: Object result;
1867:
1868: String s = toStringIdOrIndex(cx, x.currentId);
1869: if (s == null) {
1870: int index = lastIndexResult(cx);
1871: result = x.obj.get(index, x.obj);
1872: } else {
1873: result = x.obj.get(s, x.obj);
1874: }
1875:
1876: return result;
1877: }
1878:
1879: private static void enumChangeObject(IdEnumeration x) {
1880: Object[] ids = null;
1881: while (x.obj != null) {
1882: ids = x.obj.getIds();
1883: if (ids.length != 0) {
1884: break;
1885: }
1886: x.obj = x.obj.getPrototype();
1887: }
1888: if (x.obj != null && x.ids != null) {
1889: Object[] previous = x.ids;
1890: int L = previous.length;
1891: if (x.used == null) {
1892: x.used = new ObjToIntMap(L);
1893: }
1894: for (int i = 0; i != L; ++i) {
1895: x.used.intern(previous[i]);
1896: }
1897: }
1898: x.ids = ids;
1899: x.index = 0;
1900: }
1901:
1902: /**
1903: * Prepare for calling name(...): return function corresponding to
1904: * name and make current top scope available
1905: * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj.
1906: * The caller must call ScriptRuntime.lastStoredScriptable() immediately
1907: * after calling this method.
1908: */
1909: public static Callable getNameFunctionAndThis(String name,
1910: Context cx, Scriptable scope) {
1911: Scriptable parent = scope.getParentScope();
1912: if (parent == null) {
1913: Object result = topScopeName(cx, scope, name);
1914: if (!(result instanceof Callable)) {
1915: if (result == Scriptable.NOT_FOUND) {
1916: throw notFoundError(scope, name);
1917: } else {
1918: throw notFunctionError(result, name);
1919: }
1920: }
1921: // Top scope is not NativeWith or NativeCall => thisObj == scope
1922: Scriptable this Obj = scope;
1923: storeScriptable(cx, this Obj);
1924: return (Callable) result;
1925: }
1926:
1927: // name will call storeScriptable(cx, thisObj);
1928: return (Callable) nameOrFunction(cx, scope, parent, name, true);
1929: }
1930:
1931: /**
1932: * Prepare for calling obj[id](...): return function corresponding to
1933: * obj[id] and make obj properly converted to Scriptable available
1934: * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj.
1935: * The caller must call ScriptRuntime.lastStoredScriptable() immediately
1936: * after calling this method.
1937: */
1938: public static Callable getElemFunctionAndThis(Object obj,
1939: Object elem, Context cx) {
1940: String s = toStringIdOrIndex(cx, elem);
1941: if (s != null) {
1942: return getPropFunctionAndThis(obj, s, cx);
1943: }
1944: int index = lastIndexResult(cx);
1945:
1946: Scriptable this Obj = toObjectOrNull(cx, obj);
1947: if (this Obj == null) {
1948: throw undefCallError(obj, String.valueOf(index));
1949: }
1950:
1951: Object value;
1952: for (;;) {
1953: // Ignore XML lookup as requred by ECMA 357, 11.2.2.1
1954: value = ScriptableObject.getProperty(this Obj, index);
1955: if (value != Scriptable.NOT_FOUND) {
1956: break;
1957: }
1958: if (!(this Obj instanceof XMLObject)) {
1959: break;
1960: }
1961: XMLObject xmlObject = (XMLObject) this Obj;
1962: Scriptable extra = xmlObject.getExtraMethodSource(cx);
1963: if (extra == null) {
1964: break;
1965: }
1966: this Obj = extra;
1967: }
1968: if (!(value instanceof Callable)) {
1969: throw notFunctionError(value, elem);
1970: }
1971:
1972: storeScriptable(cx, this Obj);
1973: return (Callable) value;
1974: }
1975:
1976: /**
1977: * Prepare for calling obj.property(...): return function corresponding to
1978: * obj.property and make obj properly converted to Scriptable available
1979: * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj.
1980: * The caller must call ScriptRuntime.lastStoredScriptable() immediately
1981: * after calling this method.
1982: */
1983: public static Callable getPropFunctionAndThis(Object obj,
1984: String property, Context cx) {
1985: Scriptable this Obj = toObjectOrNull(cx, obj);
1986: if (this Obj == null) {
1987: throw undefCallError(obj, property);
1988: }
1989:
1990: Object value;
1991: for (;;) {
1992: // Ignore XML lookup as requred by ECMA 357, 11.2.2.1
1993: value = ScriptableObject.getProperty(this Obj, property);
1994: if (value != Scriptable.NOT_FOUND) {
1995: break;
1996: }
1997: if (!(this Obj instanceof XMLObject)) {
1998: break;
1999: }
2000: XMLObject xmlObject = (XMLObject) this Obj;
2001: Scriptable extra = xmlObject.getExtraMethodSource(cx);
2002: if (extra == null) {
2003: break;
2004: }
2005: this Obj = extra;
2006: }
2007:
2008: if (!(value instanceof Callable)) {
2009: Object noSuchMethod = ScriptableObject.getProperty(this Obj,
2010: "__noSuchMethod__");
2011: if (noSuchMethod instanceof Callable)
2012: value = new NoSuchMethodShim((Callable) noSuchMethod,
2013: property);
2014: else
2015: throw notFunctionError(value, property);
2016: }
2017:
2018: storeScriptable(cx, this Obj);
2019: return (Callable) value;
2020: }
2021:
2022: /**
2023: * Prepare for calling <expression>(...): return function corresponding to
2024: * <expression> and make parent scope of the function available
2025: * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj.
2026: * The caller must call ScriptRuntime.lastStoredScriptable() immediately
2027: * after calling this method.
2028: */
2029: public static Callable getValueFunctionAndThis(Object value,
2030: Context cx) {
2031: if (!(value instanceof Callable)) {
2032: throw notFunctionError(value);
2033: }
2034:
2035: Callable f = (Callable) value;
2036: Scriptable this Obj = null;
2037: if (f instanceof Scriptable) {
2038: this Obj = ((Scriptable) f).getParentScope();
2039: }
2040: if (this Obj == null) {
2041: if (cx.topCallScope == null)
2042: throw new IllegalStateException();
2043: this Obj = cx.topCallScope;
2044: }
2045: if (this Obj.getParentScope() != null) {
2046: if (this Obj instanceof NativeWith) {
2047: // functions defined inside with should have with target
2048: // as their thisObj
2049: } else if (this Obj instanceof NativeCall) {
2050: // nested functions should have top scope as their thisObj
2051: this Obj = ScriptableObject.getTopLevelScope(this Obj);
2052: }
2053: }
2054: storeScriptable(cx, this Obj);
2055: return f;
2056: }
2057:
2058: /**
2059: * Perform function call in reference context. Should always
2060: * return value that can be passed to
2061: * {@link #refGet(Ref, Context)} or {@link #refSet(Ref, Object, Context)}
2062: * arbitrary number of times.
2063: * The args array reference should not be stored in any object that is
2064: * can be GC-reachable after this method returns. If this is necessary,
2065: * store args.clone(), not args array itself.
2066: */
2067: public static Ref callRef(Callable function, Scriptable this Obj,
2068: Object[] args, Context cx) {
2069: if (function instanceof RefCallable) {
2070: RefCallable rfunction = (RefCallable) function;
2071: Ref ref = rfunction.refCall(cx, this Obj, args);
2072: if (ref == null) {
2073: throw new IllegalStateException(rfunction.getClass()
2074: .getName()
2075: + ".refCall() returned null");
2076: }
2077: return ref;
2078: }
2079: // No runtime support for now
2080: String msg = getMessage1("msg.no.ref.from.function",
2081: toString(function));
2082: throw constructError("ReferenceError", msg);
2083: }
2084:
2085: /**
2086: * Operator new.
2087: *
2088: * See ECMA 11.2.2
2089: */
2090: public static Scriptable newObject(Object fun, Context cx,
2091: Scriptable scope, Object[] args) {
2092: if (!(fun instanceof Function)) {
2093: throw notFunctionError(fun);
2094: }
2095: Function function = (Function) fun;
2096: return function.construct(cx, scope, args);
2097: }
2098:
2099: public static Object callSpecial(Context cx, Callable fun,
2100: Scriptable this Obj, Object[] args, Scriptable scope,
2101: Scriptable callerThis, int callType, String filename,
2102: int lineNumber) {
2103: if (callType == Node.SPECIALCALL_EVAL) {
2104: if (NativeGlobal.isEvalFunction(fun)) {
2105: return evalSpecial(cx, scope, callerThis, args,
2106: filename, lineNumber);
2107: }
2108: } else if (callType == Node.SPECIALCALL_WITH) {
2109: if (NativeWith.isWithFunction(fun)) {
2110: throw Context.reportRuntimeError1("msg.only.from.new",
2111: "With");
2112: }
2113: } else {
2114: throw Kit.codeBug();
2115: }
2116:
2117: return fun.call(cx, scope, this Obj, args);
2118: }
2119:
2120: public static Object newSpecial(Context cx, Object fun,
2121: Object[] args, Scriptable scope, int callType) {
2122: if (callType == Node.SPECIALCALL_EVAL) {
2123: if (NativeGlobal.isEvalFunction(fun)) {
2124: throw typeError1("msg.not.ctor", "eval");
2125: }
2126: } else if (callType == Node.SPECIALCALL_WITH) {
2127: if (NativeWith.isWithFunction(fun)) {
2128: return NativeWith.newWithSpecial(cx, scope, args);
2129: }
2130: } else {
2131: throw Kit.codeBug();
2132: }
2133:
2134: return newObject(fun, cx, scope, args);
2135: }
2136:
2137: /**
2138: * Function.prototype.apply and Function.prototype.call
2139: *
2140: * See Ecma 15.3.4.[34]
2141: */
2142: public static Object applyOrCall(boolean isApply, Context cx,
2143: Scriptable scope, Scriptable this Obj, Object[] args) {
2144: int L = args.length;
2145: Callable function;
2146: if (this Obj instanceof Callable) {
2147: function = (Callable) this Obj;
2148: } else {
2149: Object value = this Obj
2150: .getDefaultValue(ScriptRuntime.FunctionClass);
2151: if (!(value instanceof Callable)) {
2152: throw ScriptRuntime.notFunctionError(value, this Obj);
2153: }
2154: function = (Callable) value;
2155: }
2156:
2157: Scriptable callThis = null;
2158: if (L != 0) {
2159: callThis = toObjectOrNull(cx, args[0]);
2160: }
2161: if (callThis == null) {
2162: // This covers the case of args[0] == (null|undefined) as well.
2163: callThis = getTopCallScope(cx);
2164: }
2165:
2166: Object[] callArgs;
2167: if (isApply) {
2168: // Follow Ecma 15.3.4.3
2169: if (L <= 1) {
2170: callArgs = ScriptRuntime.emptyArgs;
2171: } else {
2172: Object arg1 = args[1];
2173: if (arg1 == null || arg1 == Undefined.instance) {
2174: callArgs = ScriptRuntime.emptyArgs;
2175: } else if (arg1 instanceof NativeArray
2176: || arg1 instanceof Arguments) {
2177: callArgs = cx.getElements((Scriptable) arg1);
2178: } else {
2179: throw ScriptRuntime
2180: .typeError0("msg.arg.isnt.array");
2181: }
2182: }
2183: } else {
2184: // Follow Ecma 15.3.4.4
2185: if (L <= 1) {
2186: callArgs = ScriptRuntime.emptyArgs;
2187: } else {
2188: callArgs = new Object[L - 1];
2189: System.arraycopy(args, 1, callArgs, 0, L - 1);
2190: }
2191: }
2192:
2193: return function.call(cx, scope, callThis, callArgs);
2194: }
2195:
2196: /**
2197: * The eval function property of the global object.
2198: *
2199: * See ECMA 15.1.2.1
2200: */
2201: public static Object evalSpecial(Context cx, Scriptable scope,
2202: Object this Arg, Object[] args, String filename,
2203: int lineNumber) {
2204: if (args.length < 1)
2205: return Undefined.instance;
2206: Object x = args[0];
2207: if (!(x instanceof String)) {
2208: if (cx.hasFeature(Context.FEATURE_STRICT_MODE)
2209: || cx.hasFeature(Context.FEATURE_STRICT_EVAL)) {
2210: throw Context
2211: .reportRuntimeError0("msg.eval.nonstring.strict");
2212: }
2213: String message = ScriptRuntime
2214: .getMessage0("msg.eval.nonstring");
2215: Context.reportWarning(message
2216: // <netbeans>
2217: , "msg.eval.nonstring", null
2218: // </netbeans>
2219: );
2220: return x;
2221: }
2222: if (filename == null) {
2223: int[] linep = new int[1];
2224: filename = Context.getSourcePositionFromStack(linep);
2225: if (filename != null) {
2226: lineNumber = linep[0];
2227: } else {
2228: filename = "";
2229: }
2230: }
2231: String sourceName = ScriptRuntime.makeUrlForGeneratedScript(
2232: true, filename, lineNumber);
2233:
2234: ErrorReporter reporter;
2235: reporter = DefaultErrorReporter.forEval(cx.getErrorReporter());
2236:
2237: // Compile with explicit interpreter instance to force interpreter
2238: // mode.
2239: Script script = cx.compileString((String) x, new Interpreter(),
2240: reporter, sourceName, 1, null);
2241: ((InterpretedFunction) script).idata.evalScriptFlag = true;
2242: Callable c = (Callable) script;
2243: return c.call(cx, scope, (Scriptable) this Arg,
2244: ScriptRuntime.emptyArgs);
2245: }
2246:
2247: /**
2248: * The typeof operator
2249: */
2250: public static String typeof(Object value) {
2251: if (value == null)
2252: return "object";
2253: if (value == Undefined.instance)
2254: return "undefined";
2255: if (value instanceof Scriptable) {
2256: if (value instanceof XMLObject)
2257: return "xml";
2258:
2259: return (value instanceof Callable) ? "function" : "object";
2260: }
2261: if (value instanceof String)
2262: return "string";
2263: if (value instanceof Number)
2264: return "number";
2265: if (value instanceof Boolean)
2266: return "boolean";
2267: throw errorWithClassName("msg.invalid.type", value);
2268: }
2269:
2270: /**
2271: * The typeof operator that correctly handles the undefined case
2272: */
2273: public static String typeofName(Scriptable scope, String id) {
2274: Context cx = Context.getContext();
2275: Scriptable val = bind(cx, scope, id);
2276: if (val == null)
2277: return "undefined";
2278: return typeof(getObjectProp(val, id, cx));
2279: }
2280:
2281: // neg:
2282: // implement the '-' operator inline in the caller
2283: // as "-toNumber(val)"
2284:
2285: // not:
2286: // implement the '!' operator inline in the caller
2287: // as "!toBoolean(val)"
2288:
2289: // bitnot:
2290: // implement the '~' operator inline in the caller
2291: // as "~toInt32(val)"
2292:
2293: public static Object add(Object val1, Object val2, Context cx) {
2294: if (val1 instanceof Number && val2 instanceof Number) {
2295: return wrapNumber(((Number) val1).doubleValue()
2296: + ((Number) val2).doubleValue());
2297: }
2298: if (val1 instanceof XMLObject) {
2299: Object test = ((XMLObject) val1).addValues(cx, true, val2);
2300: if (test != Scriptable.NOT_FOUND) {
2301: return test;
2302: }
2303: }
2304: if (val2 instanceof XMLObject) {
2305: Object test = ((XMLObject) val2).addValues(cx, false, val1);
2306: if (test != Scriptable.NOT_FOUND) {
2307: return test;
2308: }
2309: }
2310: if (val1 instanceof Scriptable)
2311: val1 = ((Scriptable) val1).getDefaultValue(null);
2312: if (val2 instanceof Scriptable)
2313: val2 = ((Scriptable) val2).getDefaultValue(null);
2314: if (!(val1 instanceof String) && !(val2 instanceof String))
2315: if ((val1 instanceof Number) && (val2 instanceof Number))
2316: return wrapNumber(((Number) val1).doubleValue()
2317: + ((Number) val2).doubleValue());
2318: else
2319: return wrapNumber(toNumber(val1) + toNumber(val2));
2320: return toString(val1).concat(toString(val2));
2321: }
2322:
2323: /**
2324: * @deprecated The method is only present for compatibility.
2325: */
2326: public static Object nameIncrDecr(Scriptable scopeChain, String id,
2327: int incrDecrMask) {
2328: return nameIncrDecr(scopeChain, id, Context.getContext(),
2329: incrDecrMask);
2330: }
2331:
2332: public static Object nameIncrDecr(Scriptable scopeChain, String id,
2333: Context cx, int incrDecrMask) {
2334: Scriptable target;
2335: Object value;
2336: search: {
2337: do {
2338: if (cx.useDynamicScope
2339: && scopeChain.getParentScope() == null) {
2340: scopeChain = checkDynamicScope(cx.topCallScope,
2341: scopeChain);
2342: }
2343: target = scopeChain;
2344: do {
2345: value = target.get(id, scopeChain);
2346: if (value != Scriptable.NOT_FOUND) {
2347: break search;
2348: }
2349: target = target.getPrototype();
2350: } while (target != null);
2351: scopeChain = scopeChain.getParentScope();
2352: } while (scopeChain != null);
2353: throw notFoundError(scopeChain, id);
2354: }
2355: return doScriptableIncrDecr(target, id, scopeChain, value,
2356: incrDecrMask);
2357: }
2358:
2359: public static Object propIncrDecr(Object obj, String id,
2360: Context cx, int incrDecrMask) {
2361: Scriptable start = toObjectOrNull(cx, obj);
2362: if (start == null) {
2363: throw undefReadError(obj, id);
2364: }
2365:
2366: Scriptable target = start;
2367: Object value;
2368: search: {
2369: do {
2370: value = target.get(id, start);
2371: if (value != Scriptable.NOT_FOUND) {
2372: break search;
2373: }
2374: target = target.getPrototype();
2375: } while (target != null);
2376: start.put(id, start, NaNobj);
2377: return NaNobj;
2378: }
2379: return doScriptableIncrDecr(target, id, start, value,
2380: incrDecrMask);
2381: }
2382:
2383: private static Object doScriptableIncrDecr(Scriptable target,
2384: String id, Scriptable protoChainStart, Object value,
2385: int incrDecrMask) {
2386: boolean post = ((incrDecrMask & Node.POST_FLAG) != 0);
2387: double number;
2388: if (value instanceof Number) {
2389: number = ((Number) value).doubleValue();
2390: } else {
2391: number = toNumber(value);
2392: if (post) {
2393: // convert result to number
2394: value = wrapNumber(number);
2395: }
2396: }
2397: if ((incrDecrMask & Node.DECR_FLAG) == 0) {
2398: ++number;
2399: } else {
2400: --number;
2401: }
2402: Number result = wrapNumber(number);
2403: target.put(id, protoChainStart, result);
2404: if (post) {
2405: return value;
2406: } else {
2407: return result;
2408: }
2409: }
2410:
2411: public static Object elemIncrDecr(Object obj, Object index,
2412: Context cx, int incrDecrMask) {
2413: Object value = getObjectElem(obj, index, cx);
2414: boolean post = ((incrDecrMask & Node.POST_FLAG) != 0);
2415: double number;
2416: if (value instanceof Number) {
2417: number = ((Number) value).doubleValue();
2418: } else {
2419: number = toNumber(value);
2420: if (post) {
2421: // convert result to number
2422: value = wrapNumber(number);
2423: }
2424: }
2425: if ((incrDecrMask & Node.DECR_FLAG) == 0) {
2426: ++number;
2427: } else {
2428: --number;
2429: }
2430: Number result = wrapNumber(number);
2431: setObjectElem(obj, index, result, cx);
2432: if (post) {
2433: return value;
2434: } else {
2435: return result;
2436: }
2437: }
2438:
2439: public static Object refIncrDecr(Ref ref, Context cx,
2440: int incrDecrMask) {
2441: Object value = ref.get(cx);
2442: boolean post = ((incrDecrMask & Node.POST_FLAG) != 0);
2443: double number;
2444: if (value instanceof Number) {
2445: number = ((Number) value).doubleValue();
2446: } else {
2447: number = toNumber(value);
2448: if (post) {
2449: // convert result to number
2450: value = wrapNumber(number);
2451: }
2452: }
2453: if ((incrDecrMask & Node.DECR_FLAG) == 0) {
2454: ++number;
2455: } else {
2456: --number;
2457: }
2458: Number result = wrapNumber(number);
2459: ref.set(cx, result);
2460: if (post) {
2461: return value;
2462: } else {
2463: return result;
2464: }
2465: }
2466:
2467: private static Object toPrimitive(Object val) {
2468: if (!(val instanceof Scriptable)) {
2469: return val;
2470: }
2471: Scriptable s = (Scriptable) val;
2472: Object result = s.getDefaultValue(null);
2473: if (result instanceof Scriptable)
2474: throw typeError0("msg.bad.default.value");
2475: return result;
2476: }
2477:
2478: /**
2479: * Equality
2480: *
2481: * See ECMA 11.9
2482: */
2483: public static boolean eq(Object x, Object y) {
2484: if (x == null || x == Undefined.instance) {
2485: if (y == null || y == Undefined.instance) {
2486: return true;
2487: }
2488: if (y instanceof ScriptableObject) {
2489: Object test = ((ScriptableObject) y)
2490: .equivalentValues(x);
2491: if (test != Scriptable.NOT_FOUND) {
2492: return ((Boolean) test).booleanValue();
2493: }
2494: }
2495: return false;
2496: } else if (x instanceof Number) {
2497: return eqNumber(((Number) x).doubleValue(), y);
2498: } else if (x instanceof String) {
2499: return eqString((String) x, y);
2500: } else if (x instanceof Boolean) {
2501: boolean b = ((Boolean) x).booleanValue();
2502: if (y instanceof Boolean) {
2503: return b == ((Boolean) y).booleanValue();
2504: }
2505: if (y instanceof ScriptableObject) {
2506: Object test = ((ScriptableObject) y)
2507: .equivalentValues(x);
2508: if (test != Scriptable.NOT_FOUND) {
2509: return ((Boolean) test).booleanValue();
2510: }
2511: }
2512: return eqNumber(b ? 1.0 : 0.0, y);
2513: } else if (x instanceof Scriptable) {
2514: if (y instanceof Scriptable) {
2515: if (x == y) {
2516: return true;
2517: }
2518: if (x instanceof ScriptableObject) {
2519: Object test = ((ScriptableObject) x)
2520: .equivalentValues(y);
2521: if (test != Scriptable.NOT_FOUND) {
2522: return ((Boolean) test).booleanValue();
2523: }
2524: }
2525: if (y instanceof ScriptableObject) {
2526: Object test = ((ScriptableObject) y)
2527: .equivalentValues(x);
2528: if (test != Scriptable.NOT_FOUND) {
2529: return ((Boolean) test).booleanValue();
2530: }
2531: }
2532: if (x instanceof Wrapper && y instanceof Wrapper) {
2533: return ((Wrapper) x).unwrap() == ((Wrapper) y)
2534: .unwrap();
2535: }
2536: return false;
2537: } else if (y instanceof Boolean) {
2538: if (x instanceof ScriptableObject) {
2539: Object test = ((ScriptableObject) x)
2540: .equivalentValues(y);
2541: if (test != Scriptable.NOT_FOUND) {
2542: return ((Boolean) test).booleanValue();
2543: }
2544: }
2545: double d = ((Boolean) y).booleanValue() ? 1.0 : 0.0;
2546: return eqNumber(d, x);
2547: } else if (y instanceof Number) {
2548: return eqNumber(((Number) y).doubleValue(), x);
2549: } else if (y instanceof String) {
2550: return eqString((String) y, x);
2551: }
2552: // covers the case when y == Undefined.instance as well
2553: return false;
2554: } else {
2555: warnAboutNonJSObject(x);
2556: return x == y;
2557: }
2558: }
2559:
2560: static boolean eqNumber(double x, Object y) {
2561: for (;;) {
2562: if (y == null || y == Undefined.instance) {
2563: return false;
2564: } else if (y instanceof Number) {
2565: return x == ((Number) y).doubleValue();
2566: } else if (y instanceof String) {
2567: return x == toNumber(y);
2568: } else if (y instanceof Boolean) {
2569: return x == (((Boolean) y).booleanValue() ? 1.0 : +0.0);
2570: } else if (y instanceof Scriptable) {
2571: if (y instanceof ScriptableObject) {
2572: Object xval = wrapNumber(x);
2573: Object test = ((ScriptableObject) y)
2574: .equivalentValues(xval);
2575: if (test != Scriptable.NOT_FOUND) {
2576: return ((Boolean) test).booleanValue();
2577: }
2578: }
2579: y = toPrimitive(y);
2580: } else {
2581: warnAboutNonJSObject(y);
2582: return false;
2583: }
2584: }
2585: }
2586:
2587: private static boolean eqString(String x, Object y) {
2588: for (;;) {
2589: if (y == null || y == Undefined.instance) {
2590: return false;
2591: } else if (y instanceof String) {
2592: return x.equals(y);
2593: } else if (y instanceof Number) {
2594: return toNumber(x) == ((Number) y).doubleValue();
2595: } else if (y instanceof Boolean) {
2596: return toNumber(x) == (((Boolean) y).booleanValue() ? 1.0
2597: : 0.0);
2598: } else if (y instanceof Scriptable) {
2599: if (y instanceof ScriptableObject) {
2600: Object test = ((ScriptableObject) y)
2601: .equivalentValues(x);
2602: if (test != Scriptable.NOT_FOUND) {
2603: return ((Boolean) test).booleanValue();
2604: }
2605: }
2606: y = toPrimitive(y);
2607: continue;
2608: } else {
2609: warnAboutNonJSObject(y);
2610: return false;
2611: }
2612: }
2613: }
2614:
2615: public static boolean shallowEq(Object x, Object y) {
2616: if (x == y) {
2617: if (!(x instanceof Number)) {
2618: return true;
2619: }
2620: // NaN check
2621: double d = ((Number) x).doubleValue();
2622: return d == d;
2623: }
2624: if (x == null || x == Undefined.instance) {
2625: return false;
2626: } else if (x instanceof Number) {
2627: if (y instanceof Number) {
2628: return ((Number) x).doubleValue() == ((Number) y)
2629: .doubleValue();
2630: }
2631: } else if (x instanceof String) {
2632: if (y instanceof String) {
2633: return x.equals(y);
2634: }
2635: } else if (x instanceof Boolean) {
2636: if (y instanceof Boolean) {
2637: return x.equals(y);
2638: }
2639: } else if (x instanceof Scriptable) {
2640: if (x instanceof Wrapper && y instanceof Wrapper) {
2641: return ((Wrapper) x).unwrap() == ((Wrapper) y).unwrap();
2642: }
2643: } else {
2644: warnAboutNonJSObject(x);
2645: return x == y;
2646: }
2647: return false;
2648: }
2649:
2650: /**
2651: * The instanceof operator.
2652: *
2653: * @return a instanceof b
2654: */
2655: public static boolean instanceOf(Object a, Object b, Context cx) {
2656: // Check RHS is an object
2657: if (!(b instanceof Scriptable)) {
2658: throw typeError0("msg.instanceof.not.object");
2659: }
2660:
2661: // for primitive values on LHS, return false
2662: // XXX we may want to change this so that
2663: // 5 instanceof Number == true
2664: if (!(a instanceof Scriptable))
2665: return false;
2666:
2667: return ((Scriptable) b).hasInstance((Scriptable) a);
2668: }
2669:
2670: /**
2671: * Delegates to
2672: *
2673: * @return true iff rhs appears in lhs' proto chain
2674: */
2675: public static boolean jsDelegatesTo(Scriptable lhs, Scriptable rhs) {
2676: Scriptable proto = lhs.getPrototype();
2677:
2678: while (proto != null) {
2679: if (proto.equals(rhs))
2680: return true;
2681: proto = proto.getPrototype();
2682: }
2683:
2684: return false;
2685: }
2686:
2687: /**
2688: * The in operator.
2689: *
2690: * This is a new JS 1.3 language feature. The in operator mirrors
2691: * the operation of the for .. in construct, and tests whether the
2692: * rhs has the property given by the lhs. It is different from the
2693: * for .. in construct in that:
2694: * <BR> - it doesn't perform ToObject on the right hand side
2695: * <BR> - it returns true for DontEnum properties.
2696: * @param a the left hand operand
2697: * @param b the right hand operand
2698: *
2699: * @return true if property name or element number a is a property of b
2700: */
2701: public static boolean in(Object a, Object b, Context cx) {
2702: if (!(b instanceof Scriptable)) {
2703: throw typeError0("msg.instanceof.not.object");
2704: }
2705:
2706: return hasObjectElem((Scriptable) b, a, cx);
2707: }
2708:
2709: public static boolean cmp_LT(Object val1, Object val2) {
2710: double d1, d2;
2711: if (val1 instanceof Number && val2 instanceof Number) {
2712: d1 = ((Number) val1).doubleValue();
2713: d2 = ((Number) val2).doubleValue();
2714: } else {
2715: if (val1 instanceof Scriptable)
2716: val1 = ((Scriptable) val1).getDefaultValue(NumberClass);
2717: if (val2 instanceof Scriptable)
2718: val2 = ((Scriptable) val2).getDefaultValue(NumberClass);
2719: if (val1 instanceof String && val2 instanceof String) {
2720: return ((String) val1).compareTo((String) val2) < 0;
2721: }
2722: d1 = toNumber(val1);
2723: d2 = toNumber(val2);
2724: }
2725: return d1 < d2;
2726: }
2727:
2728: public static boolean cmp_LE(Object val1, Object val2) {
2729: double d1, d2;
2730: if (val1 instanceof Number && val2 instanceof Number) {
2731: d1 = ((Number) val1).doubleValue();
2732: d2 = ((Number) val2).doubleValue();
2733: } else {
2734: if (val1 instanceof Scriptable)
2735: val1 = ((Scriptable) val1).getDefaultValue(NumberClass);
2736: if (val2 instanceof Scriptable)
2737: val2 = ((Scriptable) val2).getDefaultValue(NumberClass);
2738: if (val1 instanceof String && val2 instanceof String) {
2739: return ((String) val1).compareTo((String) val2) <= 0;
2740: }
2741: d1 = toNumber(val1);
2742: d2 = toNumber(val2);
2743: }
2744: return d1 <= d2;
2745: }
2746:
2747: // ------------------
2748: // Statements
2749: // ------------------
2750:
2751: public static ScriptableObject getGlobal(Context cx) {
2752: final String GLOBAL_CLASS = "org.mozilla.javascript.tools.shell.Global";
2753: Class globalClass = Kit.classOrNull(GLOBAL_CLASS);
2754: if (globalClass != null) {
2755: try {
2756: Class[] parm = { ScriptRuntime.ContextClass };
2757: Constructor globalClassCtor = globalClass
2758: .getConstructor(parm);
2759: Object[] arg = { cx };
2760: return (ScriptableObject) globalClassCtor
2761: .newInstance(arg);
2762: } catch (Exception e) {
2763: // fall through...
2764: }
2765: }
2766: return new ImporterTopLevel(cx);
2767: }
2768:
2769: public static boolean hasTopCall(Context cx) {
2770: return (cx.topCallScope != null);
2771: }
2772:
2773: public static Scriptable getTopCallScope(Context cx) {
2774: Scriptable scope = cx.topCallScope;
2775: if (scope == null) {
2776: throw new IllegalStateException();
2777: }
2778: return scope;
2779: }
2780:
2781: public static Object doTopCall(Callable callable, Context cx,
2782: Scriptable scope, Scriptable this Obj, Object[] args) {
2783: if (scope == null)
2784: throw new IllegalArgumentException();
2785: if (cx.topCallScope != null)
2786: throw new IllegalStateException();
2787:
2788: Object result;
2789: cx.topCallScope = ScriptableObject.getTopLevelScope(scope);
2790: cx.useDynamicScope = cx
2791: .hasFeature(Context.FEATURE_DYNAMIC_SCOPE);
2792: ContextFactory f = cx.getFactory();
2793: try {
2794: result = f.doTopCall(callable, cx, scope, this Obj, args);
2795: } finally {
2796: cx.topCallScope = null;
2797: // Cleanup cached references
2798: cx.cachedXMLLib = null;
2799:
2800: if (cx.currentActivationCall != null) {
2801: // Function should always call exitActivationFunction
2802: // if it creates activation record
2803: throw new IllegalStateException();
2804: }
2805: }
2806: return result;
2807: }
2808:
2809: /**
2810: * Return <tt>possibleDynamicScope</tt> if <tt>staticTopScope</tt>
2811: * is present on its prototype chain and return <tt>staticTopScope</tt>
2812: * otherwise.
2813: * Should only be called when <tt>staticTopScope</tt> is top scope.
2814: */
2815: static Scriptable checkDynamicScope(
2816: Scriptable possibleDynamicScope, Scriptable staticTopScope) {
2817: // Return cx.topCallScope if scope
2818: if (possibleDynamicScope == staticTopScope) {
2819: return possibleDynamicScope;
2820: }
2821: Scriptable proto = possibleDynamicScope;
2822: for (;;) {
2823: proto = proto.getPrototype();
2824: if (proto == staticTopScope) {
2825: return possibleDynamicScope;
2826: }
2827: if (proto == null) {
2828: return staticTopScope;
2829: }
2830: }
2831: }
2832:
2833: public static void initScript(NativeFunction funObj,
2834: Scriptable this Obj, Context cx, Scriptable scope,
2835: boolean evalScript) {
2836: if (cx.topCallScope == null)
2837: throw new IllegalStateException();
2838:
2839: int varCount = funObj.getParamAndVarCount();
2840: if (varCount != 0) {
2841:
2842: Scriptable varScope = scope;
2843: // Never define any variables from var statements inside with
2844: // object. See bug 38590.
2845: while (varScope instanceof NativeWith) {
2846: varScope = varScope.getParentScope();
2847: }
2848:
2849: for (int i = varCount; i-- != 0;) {
2850: String name = funObj.getParamOrVarName(i);
2851: boolean isConst = funObj.getParamOrVarConst(i);
2852: // Don't overwrite existing def if already defined in object
2853: // or prototypes of object.
2854: if (!ScriptableObject.hasProperty(scope, name)) {
2855: if (!evalScript) {
2856: // Global var definitions are supposed to be DONTDELETE
2857: if (isConst)
2858: ScriptableObject.defineConstProperty(
2859: varScope, name);
2860: else
2861: ScriptableObject.defineProperty(varScope,
2862: name, Undefined.instance,
2863: ScriptableObject.PERMANENT);
2864: } else {
2865: varScope
2866: .put(name, varScope, Undefined.instance);
2867: }
2868: } else {
2869: ScriptableObject.redefineProperty(scope, name,
2870: isConst);
2871: }
2872: }
2873: }
2874: }
2875:
2876: public static Scriptable createFunctionActivation(
2877: NativeFunction funObj, Scriptable scope, Object[] args) {
2878: return new NativeCall(funObj, scope, args);
2879: }
2880:
2881: public static void enterActivationFunction(Context cx,
2882: Scriptable scope) {
2883: if (cx.topCallScope == null)
2884: throw new IllegalStateException();
2885: NativeCall call = (NativeCall) scope;
2886: call.parentActivationCall = cx.currentActivationCall;
2887: cx.currentActivationCall = call;
2888: }
2889:
2890: public static void exitActivationFunction(Context cx) {
2891: NativeCall call = cx.currentActivationCall;
2892: cx.currentActivationCall = call.parentActivationCall;
2893: call.parentActivationCall = null;
2894: }
2895:
2896: static NativeCall findFunctionActivation(Context cx, Function f) {
2897: NativeCall call = cx.currentActivationCall;
2898: while (call != null) {
2899: if (call.function == f)
2900: return call;
2901: call = call.parentActivationCall;
2902: }
2903: return null;
2904: }
2905:
2906: public static Scriptable newCatchScope(Throwable t,
2907: Scriptable lastCatchScope, String exceptionName,
2908: Context cx, Scriptable scope) {
2909: Object obj;
2910: boolean cacheObj;
2911:
2912: getObj: if (t instanceof JavaScriptException) {
2913: cacheObj = false;
2914: obj = ((JavaScriptException) t).getValue();
2915: } else {
2916: cacheObj = true;
2917:
2918: // Create wrapper object unless it was associated with
2919: // the previous scope object
2920:
2921: if (lastCatchScope != null) {
2922: NativeObject last = (NativeObject) lastCatchScope;
2923: obj = last.getAssociatedValue(t);
2924: if (obj == null)
2925: Kit.codeBug();
2926: break getObj;
2927: }
2928:
2929: RhinoException re;
2930: String errorName;
2931: String errorMsg;
2932: Throwable javaException = null;
2933:
2934: if (t instanceof EcmaError) {
2935: EcmaError ee = (EcmaError) t;
2936: re = ee;
2937: errorName = ee.getName();
2938: errorMsg = ee.getErrorMessage();
2939: } else if (t instanceof WrappedException) {
2940: WrappedException we = (WrappedException) t;
2941: re = we;
2942: javaException = we.getWrappedException();
2943: errorName = "JavaException";
2944: errorMsg = javaException.getClass().getName() + ": "
2945: + javaException.getMessage();
2946: } else if (t instanceof EvaluatorException) {
2947: // Pure evaluator exception, nor WrappedException instance
2948: EvaluatorException ee = (EvaluatorException) t;
2949: re = ee;
2950: errorName = "InternalError";
2951: errorMsg = ee.getMessage();
2952: } else {
2953: // Script can catch only instances of JavaScriptException,
2954: // EcmaError and EvaluatorException
2955: throw Kit.codeBug();
2956: }
2957:
2958: String sourceUri = re.sourceName();
2959: if (sourceUri == null) {
2960: sourceUri = "";
2961: }
2962: int line = re.lineNumber();
2963: Object args[];
2964: if (line > 0) {
2965: args = new Object[] { errorMsg, sourceUri,
2966: new Integer(line) };
2967: } else {
2968: args = new Object[] { errorMsg, sourceUri };
2969: }
2970:
2971: Scriptable errorObject = cx.newObject(scope, errorName,
2972: args);
2973: ScriptableObject
2974: .putProperty(errorObject, "name", errorName);
2975:
2976: if (javaException != null) {
2977: Object wrap = cx.getWrapFactory().wrap(cx, scope,
2978: javaException, null);
2979: ScriptableObject.defineProperty(errorObject,
2980: "javaException", wrap,
2981: ScriptableObject.PERMANENT
2982: | ScriptableObject.READONLY);
2983: }
2984: Object wrap = cx.getWrapFactory().wrap(cx, scope, re, null);
2985: ScriptableObject.defineProperty(errorObject,
2986: "rhinoException", wrap, ScriptableObject.PERMANENT
2987: | ScriptableObject.READONLY);
2988:
2989: obj = errorObject;
2990: }
2991:
2992: NativeObject catchScopeObject = new NativeObject();
2993: // See ECMA 12.4
2994: catchScopeObject.defineProperty(exceptionName, obj,
2995: ScriptableObject.PERMANENT);
2996:
2997: // Add special Rhino object __exception__ defined in the catch
2998: // scope that can be used to retrieve the Java exception associated
2999: // with the JavaScript exception (to get stack trace info, etc.)
3000: catchScopeObject.defineProperty("__exception__", Context
3001: .javaToJS(t, catchScopeObject),
3002: ScriptableObject.PERMANENT | ScriptableObject.DONTENUM);
3003:
3004: if (cacheObj) {
3005: catchScopeObject.associateValue(t, obj);
3006: }
3007: return catchScopeObject;
3008: }
3009:
3010: public static Scriptable enterWith(Object obj, Context cx,
3011: Scriptable scope) {
3012: Scriptable sobj = toObjectOrNull(cx, obj);
3013: if (sobj == null) {
3014: throw typeError1("msg.undef.with", toString(obj));
3015: }
3016: if (sobj instanceof XMLObject) {
3017: XMLObject xmlObject = (XMLObject) sobj;
3018: return xmlObject.enterWith(scope);
3019: }
3020: return new NativeWith(scope, sobj);
3021: }
3022:
3023: public static Scriptable leaveWith(Scriptable scope) {
3024: NativeWith nw = (NativeWith) scope;
3025: return nw.getParentScope();
3026: }
3027:
3028: public static Scriptable enterDotQuery(Object value,
3029: Scriptable scope) {
3030: if (!(value instanceof XMLObject)) {
3031: throw notXmlError(value);
3032: }
3033: XMLObject object = (XMLObject) value;
3034: return object.enterDotQuery(scope);
3035: }
3036:
3037: public static Object updateDotQuery(boolean value, Scriptable scope) {
3038: // Return null to continue looping
3039: NativeWith nw = (NativeWith) scope;
3040: return nw.updateDotQuery(value);
3041: }
3042:
3043: public static Scriptable leaveDotQuery(Scriptable scope) {
3044: NativeWith nw = (NativeWith) scope;
3045: return nw.getParentScope();
3046: }
3047:
3048: public static void setFunctionProtoAndParent(BaseFunction fn,
3049: Scriptable scope) {
3050: fn.setParentScope(scope);
3051: fn.setPrototype(ScriptableObject.getFunctionPrototype(scope));
3052: }
3053:
3054: public static void setObjectProtoAndParent(ScriptableObject object,
3055: Scriptable scope) {
3056: // Compared with function it always sets the scope to top scope
3057: scope = ScriptableObject.getTopLevelScope(scope);
3058: object.setParentScope(scope);
3059: Scriptable proto = ScriptableObject.getClassPrototype(scope,
3060: object.getClassName());
3061: object.setPrototype(proto);
3062: }
3063:
3064: public static void initFunction(Context cx, Scriptable scope,
3065: NativeFunction function, int type, boolean fromEvalCode) {
3066: if (type == FunctionNode.FUNCTION_STATEMENT) {
3067: String name = function.getFunctionName();
3068: if (name != null && name.length() != 0) {
3069: if (!fromEvalCode) {
3070: // ECMA specifies that functions defined in global and
3071: // function scope outside eval should have DONTDELETE set.
3072: ScriptableObject.defineProperty(scope, name,
3073: function, ScriptableObject.PERMANENT);
3074: } else {
3075: scope.put(name, scope, function);
3076: }
3077: }
3078: } else if (type == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) {
3079: String name = function.getFunctionName();
3080: if (name != null && name.length() != 0) {
3081: // Always put function expression statements into initial
3082: // activation object ignoring the with statement to follow
3083: // SpiderMonkey
3084: while (scope instanceof NativeWith) {
3085: scope = scope.getParentScope();
3086: }
3087: scope.put(name, scope, function);
3088: }
3089: } else {
3090: throw Kit.codeBug();
3091: }
3092: }
3093:
3094: public static Scriptable newArrayLiteral(Object[] objects,
3095: int[] skipIndexces, Context cx, Scriptable scope) {
3096: int count = objects.length;
3097: int skipCount = 0;
3098: if (skipIndexces != null) {
3099: skipCount = skipIndexces.length;
3100: }
3101: int length = count + skipCount;
3102: Integer lengthObj = new Integer(length);
3103: Scriptable arrayObj;
3104: /*
3105: * If the version is 120, then new Array(4) means create a new
3106: * array with 4 as the first element. In this case, we have to
3107: * set length property manually.
3108: */
3109: if (cx.getLanguageVersion() == Context.VERSION_1_2) {
3110: arrayObj = cx.newObject(scope, "Array",
3111: ScriptRuntime.emptyArgs);
3112: ScriptableObject.putProperty(arrayObj, "length", lengthObj);
3113: } else {
3114: arrayObj = cx.newObject(scope, "Array",
3115: new Object[] { lengthObj });
3116: }
3117: int skip = 0;
3118: for (int i = 0, j = 0; i != length; ++i) {
3119: if (skip != skipCount && skipIndexces[skip] == i) {
3120: ++skip;
3121: continue;
3122: }
3123: ScriptableObject.putProperty(arrayObj, i, objects[j]);
3124: ++j;
3125: }
3126: return arrayObj;
3127: }
3128:
3129: /**
3130: * This method is here for backward compat with existing compiled code. It
3131: * is called when an object literal is compiled. The next instance will be
3132: * the version called from new code.
3133: * @deprecated This method only present for compatibility.
3134: */
3135: public static Scriptable newObjectLiteral(Object[] propertyIds,
3136: Object[] propertyValues, Context cx, Scriptable scope) {
3137: // This will initialize to all zeros, exactly what we need for old-style
3138: // getterSetters values (no getters or setters in the list)
3139: int[] getterSetters = new int[propertyIds.length];
3140: return newObjectLiteral(propertyIds, propertyValues,
3141: getterSetters, cx, scope);
3142: }
3143:
3144: public static Scriptable newObjectLiteral(Object[] propertyIds,
3145: Object[] propertyValues, int[] getterSetters, Context cx,
3146: Scriptable scope) {
3147: Scriptable object = cx.newObject(scope);
3148: for (int i = 0, end = propertyIds.length; i != end; ++i) {
3149: Object id = propertyIds[i];
3150: int getterSetter = getterSetters[i];
3151: Object value = propertyValues[i];
3152: if (id instanceof String) {
3153: if (getterSetter == 0)
3154: ScriptableObject.putProperty(object, (String) id,
3155: value);
3156: else {
3157: Callable fun;
3158: String definer;
3159: if (getterSetter < 0) // < 0 means get foo() ...
3160: definer = "__defineGetter__";
3161: else
3162: definer = "__defineSetter__";
3163: fun = getPropFunctionAndThis(object, definer, cx);
3164: // Must consume the last scriptable object in cx
3165: lastStoredScriptable(cx);
3166: Object[] outArgs = new Object[2];
3167: outArgs[0] = id;
3168: outArgs[1] = value;
3169: fun.call(cx, scope, object, outArgs);
3170: }
3171: } else {
3172: int index = ((Integer) id).intValue();
3173: ScriptableObject.putProperty(object, index, value);
3174: }
3175: }
3176: return object;
3177: }
3178:
3179: public static boolean isArrayObject(Object obj) {
3180: return obj instanceof NativeArray || obj instanceof Arguments;
3181: }
3182:
3183: public static Object[] getArrayElements(Scriptable object) {
3184: Context cx = Context.getContext();
3185: long longLen = NativeArray.getLengthProperty(cx, object);
3186: if (longLen > Integer.MAX_VALUE) {
3187: // arrays beyond MAX_INT is not in Java in any case
3188: throw new IllegalArgumentException();
3189: }
3190: int len = (int) longLen;
3191: if (len == 0) {
3192: return ScriptRuntime.emptyArgs;
3193: } else {
3194: Object[] result = new Object[len];
3195: for (int i = 0; i < len; i++) {
3196: Object elem = ScriptableObject.getProperty(object, i);
3197: result[i] = (elem == Scriptable.NOT_FOUND) ? Undefined.instance
3198: : elem;
3199: }
3200: return result;
3201: }
3202: }
3203:
3204: static void checkDeprecated(Context cx, String name) {
3205: int version = cx.getLanguageVersion();
3206: if (version >= Context.VERSION_1_4
3207: || version == Context.VERSION_DEFAULT) {
3208: String msg = getMessage1("msg.deprec.ctor", name);
3209: if (version == Context.VERSION_DEFAULT)
3210: Context.reportWarning(msg
3211: // <netbeans>
3212: , "msg.deprec.ctor", name
3213: // </netbeans>
3214: );
3215: else
3216: throw Context.reportRuntimeError(msg);
3217: }
3218: }
3219:
3220: public static String getMessage0(String messageId) {
3221: return getMessage(messageId, null);
3222: }
3223:
3224: public static String getMessage1(String messageId, Object arg1) {
3225: Object[] arguments = { arg1 };
3226: return getMessage(messageId, arguments);
3227: }
3228:
3229: public static String getMessage2(String messageId, Object arg1,
3230: Object arg2) {
3231: Object[] arguments = { arg1, arg2 };
3232: return getMessage(messageId, arguments);
3233: }
3234:
3235: public static String getMessage3(String messageId, Object arg1,
3236: Object arg2, Object arg3) {
3237: Object[] arguments = { arg1, arg2, arg3 };
3238: return getMessage(messageId, arguments);
3239: }
3240:
3241: public static String getMessage4(String messageId, Object arg1,
3242: Object arg2, Object arg3, Object arg4) {
3243: Object[] arguments = { arg1, arg2, arg3, arg4 };
3244: return getMessage(messageId, arguments);
3245: }
3246:
3247: /* OPT there's a noticable delay for the first error! Maybe it'd
3248: * make sense to use a ListResourceBundle instead of a properties
3249: * file to avoid (synchronized) text parsing.
3250: */
3251: public static String getMessage(String messageId, Object[] arguments) {
3252: final String defaultResource = "org.mozilla.javascript.resources.Messages";
3253:
3254: Context cx = Context.getCurrentContext();
3255: Locale locale = cx != null ? cx.getLocale() : Locale
3256: .getDefault();
3257:
3258: // ResourceBundle does cacheing.
3259: ResourceBundle rb = ResourceBundle.getBundle(defaultResource,
3260: locale);
3261:
3262: String formatString;
3263: try {
3264: formatString = rb.getString(messageId);
3265: } catch (java.util.MissingResourceException mre) {
3266: throw new RuntimeException(
3267: "no message resource found for message property "
3268: + messageId);
3269: }
3270:
3271: /*
3272: * It's OK to format the string, even if 'arguments' is null;
3273: * we need to format it anyway, to make double ''s collapse to
3274: * single 's.
3275: */
3276: // TODO: MessageFormat is not available on pJava
3277: MessageFormat formatter = new MessageFormat(formatString);
3278: return formatter.format(arguments);
3279: }
3280:
3281: public static EcmaError constructError(String error, String message) {
3282: int[] linep = new int[1];
3283: String filename = Context.getSourcePositionFromStack(linep);
3284: return constructError(error, message, filename, linep[0], null,
3285: 0);
3286: }
3287:
3288: public static EcmaError constructError(String error,
3289: String message, String sourceName, int lineNumber,
3290: String lineSource, int columnNumber) {
3291: return new EcmaError(error, message, sourceName, lineNumber,
3292: lineSource, columnNumber);
3293: }
3294:
3295: public static EcmaError typeError(String message) {
3296: return constructError("TypeError", message);
3297: }
3298:
3299: public static EcmaError typeError0(String messageId) {
3300: String msg = getMessage0(messageId);
3301: return typeError(msg);
3302: }
3303:
3304: public static EcmaError typeError1(String messageId, String arg1) {
3305: String msg = getMessage1(messageId, arg1);
3306: return typeError(msg);
3307: }
3308:
3309: public static EcmaError typeError2(String messageId, String arg1,
3310: String arg2) {
3311: String msg = getMessage2(messageId, arg1, arg2);
3312: return typeError(msg);
3313: }
3314:
3315: public static EcmaError typeError3(String messageId, String arg1,
3316: String arg2, String arg3) {
3317: String msg = getMessage3(messageId, arg1, arg2, arg3);
3318: return typeError(msg);
3319: }
3320:
3321: public static RuntimeException undefReadError(Object object,
3322: Object id) {
3323: String idStr = (id == null) ? "null" : id.toString();
3324: return typeError2("msg.undef.prop.read", toString(object),
3325: idStr);
3326: }
3327:
3328: public static RuntimeException undefCallError(Object object,
3329: Object id) {
3330: String idStr = (id == null) ? "null" : id.toString();
3331: return typeError2("msg.undef.method.call", toString(object),
3332: idStr);
3333: }
3334:
3335: public static RuntimeException undefWriteError(Object object,
3336: Object id, Object value) {
3337: String idStr = (id == null) ? "null" : id.toString();
3338: String valueStr = (value instanceof Scriptable) ? value
3339: .toString() : toString(value);
3340: return typeError3("msg.undef.prop.write", toString(object),
3341: idStr, valueStr);
3342: }
3343:
3344: public static RuntimeException notFoundError(Scriptable object,
3345: String property) {
3346: // XXX: use object to improve the error message
3347: String msg = getMessage1("msg.is.not.defined", property);
3348: throw constructError("ReferenceError", msg);
3349: }
3350:
3351: public static RuntimeException notFunctionError(Object value) {
3352: return notFunctionError(value, value);
3353: }
3354:
3355: public static RuntimeException notFunctionError(Object value,
3356: Object messageHelper) {
3357: // XXX Use value for better error reporting
3358: String msg = (messageHelper == null) ? "null" : messageHelper
3359: .toString();
3360: if (value == Scriptable.NOT_FOUND) {
3361: return typeError1("msg.function.not.found", msg);
3362: }
3363: return typeError2("msg.isnt.function", msg,
3364: value == null ? "null" : value.getClass().getName());
3365: }
3366:
3367: private static RuntimeException notXmlError(Object value) {
3368: throw typeError1("msg.isnt.xml.object", ScriptRuntime
3369: .toString(value));
3370: }
3371:
3372: private static void warnAboutNonJSObject(Object nonJSObject) {
3373: String message = "RHINO USAGE WARNING: Missed Context.javaToJS() conversion:\n"
3374: + "Rhino runtime detected object "
3375: + nonJSObject
3376: + " of class "
3377: + nonJSObject.getClass().getName()
3378: + " where it expected String, Number, Boolean or Scriptable instance. Please check your code for missing Context.javaToJS() call.";
3379: Context.reportWarning(message
3380: // <netbeans>
3381: , "warnAboutNonJSObject", nonJSObject
3382: // </netbeans>
3383: );
3384: // Just to be sure that it would be noticed
3385: System.err.println(message);
3386: }
3387:
3388: public static RegExpProxy getRegExpProxy(Context cx) {
3389: return cx.getRegExpProxy();
3390: }
3391:
3392: public static void setRegExpProxy(Context cx, RegExpProxy proxy) {
3393: if (proxy == null)
3394: throw new IllegalArgumentException();
3395: cx.regExpProxy = proxy;
3396: }
3397:
3398: public static RegExpProxy checkRegExpProxy(Context cx) {
3399: RegExpProxy result = getRegExpProxy(cx);
3400: if (result == null) {
3401: throw Context.reportRuntimeError0("msg.no.regexp");
3402: }
3403: return result;
3404: }
3405:
3406: private static XMLLib currentXMLLib(Context cx) {
3407: // Scripts should be running to access this
3408: if (cx.topCallScope == null)
3409: throw new IllegalStateException();
3410:
3411: XMLLib xmlLib = cx.cachedXMLLib;
3412: if (xmlLib == null) {
3413: xmlLib = XMLLib.extractFromScope(cx.topCallScope);
3414: if (xmlLib == null)
3415: throw new IllegalStateException();
3416: cx.cachedXMLLib = xmlLib;
3417: }
3418:
3419: return xmlLib;
3420: }
3421:
3422: /**
3423: * Escapes the reserved characters in a value of an attribute
3424: *
3425: * @param value Unescaped text
3426: * @return The escaped text
3427: */
3428: public static String escapeAttributeValue(Object value, Context cx) {
3429: XMLLib xmlLib = currentXMLLib(cx);
3430: return xmlLib.escapeAttributeValue(value);
3431: }
3432:
3433: /**
3434: * Escapes the reserved characters in a value of a text node
3435: *
3436: * @param value Unescaped text
3437: * @return The escaped text
3438: */
3439: public static String escapeTextValue(Object value, Context cx) {
3440: XMLLib xmlLib = currentXMLLib(cx);
3441: return xmlLib.escapeTextValue(value);
3442: }
3443:
3444: public static Ref memberRef(Object obj, Object elem, Context cx,
3445: int memberTypeFlags) {
3446: if (!(obj instanceof XMLObject)) {
3447: throw notXmlError(obj);
3448: }
3449: XMLObject xmlObject = (XMLObject) obj;
3450: return xmlObject.memberRef(cx, elem, memberTypeFlags);
3451: }
3452:
3453: public static Ref memberRef(Object obj, Object namespace,
3454: Object elem, Context cx, int memberTypeFlags) {
3455: if (!(obj instanceof XMLObject)) {
3456: throw notXmlError(obj);
3457: }
3458: XMLObject xmlObject = (XMLObject) obj;
3459: return xmlObject
3460: .memberRef(cx, namespace, elem, memberTypeFlags);
3461: }
3462:
3463: public static Ref nameRef(Object name, Context cx,
3464: Scriptable scope, int memberTypeFlags) {
3465: XMLLib xmlLib = currentXMLLib(cx);
3466: return xmlLib.nameRef(cx, name, scope, memberTypeFlags);
3467: }
3468:
3469: public static Ref nameRef(Object namespace, Object name,
3470: Context cx, Scriptable scope, int memberTypeFlags) {
3471: XMLLib xmlLib = currentXMLLib(cx);
3472: return xmlLib.nameRef(cx, namespace, name, scope,
3473: memberTypeFlags);
3474: }
3475:
3476: private static void storeIndexResult(Context cx, int index) {
3477: cx.scratchIndex = index;
3478: }
3479:
3480: static int lastIndexResult(Context cx) {
3481: return cx.scratchIndex;
3482: }
3483:
3484: public static void storeUint32Result(Context cx, long value) {
3485: if ((value >>> 32) != 0)
3486: throw new IllegalArgumentException();
3487: cx.scratchUint32 = value;
3488: }
3489:
3490: public static long lastUint32Result(Context cx) {
3491: long value = cx.scratchUint32;
3492: if ((value >>> 32) != 0)
3493: throw new IllegalStateException();
3494: return value;
3495: }
3496:
3497: private static void storeScriptable(Context cx, Scriptable value) {
3498: // The previosly stored scratchScriptable should be consumed
3499: if (cx.scratchScriptable != null)
3500: throw new IllegalStateException();
3501: cx.scratchScriptable = value;
3502: }
3503:
3504: public static Scriptable lastStoredScriptable(Context cx) {
3505: Scriptable result = cx.scratchScriptable;
3506: cx.scratchScriptable = null;
3507: return result;
3508: }
3509:
3510: static String makeUrlForGeneratedScript(boolean isEval,
3511: String masterScriptUrl, int masterScriptLine) {
3512: if (isEval) {
3513: return masterScriptUrl + '#' + masterScriptLine + "(eval)";
3514: } else {
3515: return masterScriptUrl + '#' + masterScriptLine
3516: + "(Function)";
3517: }
3518: }
3519:
3520: static boolean isGeneratedScript(String sourceUrl) {
3521: // ALERT: this may clash with a valid URL containing (eval) or
3522: // (Function)
3523: return sourceUrl.indexOf("(eval)") >= 0
3524: || sourceUrl.indexOf("(Function)") >= 0;
3525: }
3526:
3527: private static RuntimeException errorWithClassName(String msg,
3528: Object val) {
3529: return Context.reportRuntimeError1(msg, val.getClass()
3530: .getName());
3531: }
3532:
3533: public static final Object[] emptyArgs = new Object[0];
3534: public static final String[] emptyStrings = new String[0];
3535:
3536: }
|