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