/*
 * The contents of this file are subject to the Netscape Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/NPL/

 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.

 * The Original Code is Rhino code, released
 * May 6, 1999.

 * The Initial Developer of the Original Code is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1997-1999 Netscape Communications Corporation. All
 * Rights Reserved.

 * Contributor(s): 

 * Alternatively, the contents of this file may be used under the
 * terms of the GNU Public License (the "GPL"), in which case the
 * provisions of the GPL are applicable instead of those above.
 * If you wish to allow use of your version of this file only
 * under the terms of the GPL and not to allow others to use your
 * version of this file under the NPL, indicate your decision by
 * deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL.  If you do not delete
 * the provisions above, a recipient may use your version of this
 * file under either the NPL or the GPL.
 */
/* Header file */
#include <stdio.h>
#include <ctype.h>
#include <limits.h>
#include <math.h>
#ifdef VX
#include <private/mathP.h>
#endif
#ifdef SUNOS5
#include <strings.h>
#include <ieeefp.h>
#endif
#include "js-utils.h"
#include "js-binarydigitreader.h"
#include "js-stringbuffer.h"
#include "js-idenumeration.h"
#include "js-flattenedobject.h"
#include "js-regexpproxy.h"
#include "js-scriptableobject.h"
#include "js-nativeobject.h"
#include "js-nativewith.h"
#include "js-nativecall.h"
#include "js-nativescript.h"
#include "js-nativeglobal.h"
#include "js-nativehost.h"
#include "js-functionobject.h"
#include "js-evaluatorexception.h"
#include "js-runtimeexception.h"
#include "js-exceptionhandler.h"
#ifdef REGEXP
#include "js-nativeregexp.h"
#endif
#include "js-fpclass.h"
#include "js-dtoa.h"
#ifdef VX /* Turbot */
# include "js-nan.h"
#endif
#include "js-scriptruntime.h"

/* Class data */
#ifdef VX /* Turbot */
double	JSScriptRuntime::NaN;
#else
const	double	JSScriptRuntime::NaN = 0.0 / 0.0;
#endif

/* Procedures */
/**
 * Convert the value to a boolean.
 *
 * See ECMA 9.2.
 */
jsBool_t
JSScriptRuntime::toBoolean(JSContext *cx, jsVal_t val)
{
    if (val == JS_VAL_NULL) {
        return FALSE;
    }
    if (JS_VAL_IS_OBJECT(val)) {
        if (cx->isVersionECMA1()) {
            // pure ECMA
            return TRUE;
        }
        // ECMA extension
        val = JS_VAL_TO_OBJECT(val)->getDefaultValue(cx, JS_T_Boolean);
	if (JS_VAL_IS_OBJECT(val)) {
            cx->exceptionHandler->throwEx(
                    cx,
                    errorWithClassName(cx, "msg.primitive.expected", val));
	}
        // fall through
    }
    if (JS_VAL_IS_STRING(val)) {
#ifdef FASTER
        return JS_VAL_TO_STRING(val)[0] != '\0';
#else
        return strlen(JS_VAL_TO_STRING(val)) != 0;
#endif
    }
    if (JS_VAL_IS_INT(val)) {
	return JS_VAL_TO_INT(val) != 0;
    }
    if (JS_VAL_IS_DOUBLE(val)) {
        double *dp = JS_VAL_TO_DOUBLE(val);

#ifdef VX
        return (!isNan(*dp) && *dp != 0.0);
#else
        return (!isnan(*dp) && *dp != 0.0);
#endif
    }
    if (JS_VAL_IS_BOOLEAN(val)) {
        return JS_VAL_TO_BOOLEAN(val);
    }
    if (JS_VAL_IS_VOID(val)) {
	return FALSE;
    }
    cx->exceptionHandler->throwEx(
            cx,
            errorWithClassName(cx, "msg.invalid.type", val));
    /* NOT REACHED */
    return FALSE;
}
/**
 * Convert the value to a number.
 *
 * See ECMA 9.3.
 */
double
JSScriptRuntime::toNumber(JSContext *cx, jsVal_t val)
{
    if (val != JS_VAL_NULL && JS_VAL_IS_OBJECT(val)) {
        val = JS_VAL_TO_OBJECT(val)->getDefaultValue(cx, JS_T_Number);
        if (val != JS_VAL_NULL && JS_VAL_IS_OBJECT(val)) {
             cx->exceptionHandler->throwEx(
                     cx,
                     errorWithClassName(cx, "msg.primitive.expected", val));
	}
        // fall through
    }
    if (val == JS_VAL_NULL) {
        return +0.0;
    }
    if (JS_VAL_IS_STRING(val)) {
        return toNumber(cx, JS_VAL_TO_STRING(val));
    }
    if (JS_VAL_IS_INT(val)) {
        return (double) JS_VAL_TO_INT(val);
    }
    if (JS_VAL_IS_DOUBLE(val)) {
        return *JS_VAL_TO_DOUBLE(val);
    }
    if (JS_VAL_IS_BOOLEAN(val)) {
        return (double) JS_VAL_TO_BOOLEAN(val);
    }
    if (JS_VAL_IS_VOID(val)) {
	return JSScriptRuntime::NaN;
    }
    cx->exceptionHandler->throwEx(
            cx,
            errorWithClassName(cx, "msg.invalid.type", val));
    /* NOT REACHED */
    return 0.0;
}
/*
 * Helper function for toNumber, parseInt, and TokenStream.getToken.
 */
double
JSScriptRuntime::stringToNumber(jsString_t s, int start, int radix)
{
    jsChar_t digitMax = '9';
    jsChar_t lowerCaseBound = 'a';
    jsChar_t upperCaseBound = 'A';
    int len = strlen(s);

    if (radix < 10) {
        digitMax = (jsChar_t) ('0' + radix - 1);
    }
    if (radix > 10) {
        lowerCaseBound = (jsChar_t) ('a' + radix - 10);
        upperCaseBound = (jsChar_t) ('A' + radix - 10);
    }
    int end;
    double sum = 0.0;

    for (end = start; end < len; end++) {
        jsChar_t c = s[end];
        int newDigit;

        if ('0' <= c && c <= digitMax) {
            newDigit = c - '0';
        } else if ('a' <= c && c < lowerCaseBound) {
            newDigit = c - 'a' + 10;
        } else if ('A' <= c && c < upperCaseBound) {
            newDigit = c - 'A' + 10;
	} else {
            break;
	}
        sum = sum*radix + newDigit;
    }
    if (start == end) {
        return NaN;
    }
    if (sum >= 9007199254740992.0) {
        if (radix == 10) {
            /* If we're accumulating a decimal number and the number
             * is >= 2^53, then the result from the repeated multiply-add
             * above may be inaccurate.  Call Java to get the correct
             * resultwer.
             */
	    double d;

	    if (sscanf(s, "%lf", &d) == 1)  {
		return d;
	    } else {
                return NaN;
	    }
        } else if (radix == 2 || radix == 4 || radix == 8 ||
                   radix == 16 || radix == 32)
        {
            /* The number may also be inaccurate for one of these bases.
             * This happens if the addition in value*radix + digit causes
             * a round-down to an even least significant mantissa bit
             * when the first dropped bit is a one.  If any of the
             * following digits in the number (which haven't been added
             * in yet) are nonzero then the correct action would have
             * been to round up instead of down.  An example of this
             * occurs when reading the number 0x1000000000000081, which
             * rounds to 0x1000000000000000 instead of 0x1000000000000100.
             */
            JSBinaryDigitReader bdr(radix, s, start, end);
            int bit;
            sum = 0.0;

            /* Skip leading zeros. */
            do {
                bit = bdr.getNextBinaryDigit();
            } while (bit == 0);

            if (bit == 1) {
                /* Gather the 53 significant bits (including the leading 1) */
                sum = 1.0;
                for (int j = 52; j != 0; j--) {
                    bit = bdr.getNextBinaryDigit();
                    if (bit < 0)
                        return sum;
                    sum = sum*2 + bit;
                }
                /* bit54 is the 54th bit (the first dropped from the
		   mantissa) */
                int bit54 = bdr.getNextBinaryDigit();

                if (bit54 >= 0) {
                    double factor = 2.0;
                    int sticky = 0;  /* sticky is 1 if any bit beyond the 54th
					is 1 */
                    int bit3;

                    while ((bit3 = bdr.getNextBinaryDigit()) >= 0) {
                        sticky |= bit3;
                        factor *= 2;
                    }
                    sum += bit54 & (bit | sticky);
                    sum *= factor;
                }
            }
        }
        /* We don't worry about inaccurate numbers for any other base. */
    }
    return sum;
}
/**
 * ToNumber applied to the String type
 *
 * See ECMA 9.3.1
 */
double
JSScriptRuntime::toNumber(JSContext *cx, jsString_t s)
{
    int len = strlen(s);
    int start = 0;
    jsChar_t startChar;

    for (;;) {
        if (start == len) {
            // Empty or contains only whitespace
            return +0.0;
        }
        startChar = s[start];
        if (!isspace(startChar)) {
            break;
	}
        start++;
    }

    if (startChar == '0' && start + 2 < len && tolower(s[start + 1]) == 'x') {
        // A hexadecimal number
        return stringToNumber(s, start + 2, 16);
    }

    if ((startChar == '+' || startChar == '-') && start + 3 < len
	&& s[start + 1] == '0' && tolower(s[start + 2]) == 'x') {
        // A hexadecimal number
        double val = stringToNumber(s, start + 3, 16);

        return startChar == '-' ? -val : val;
    }

    int end = len - 1;
    jsChar_t endChar;

    while (isspace(endChar = s[end])) {
        end--;
    }
    if (endChar == 'y') {
        // check for "Infinity"
        if (startChar == '+' || startChar == '-') {
            start++;
	}
        jsString_t sub = JS_Substring(cx, s, start, end + 1);

        if (strcmp(sub, "Infinity") == 0) {
	    JS_DeleteZoneObj(cx, sub);
            return startChar == '-' ? NEGATIVE_INFINITY : POSITIVE_INFINITY;
	}
	JS_DeleteZoneObj(cx, sub);
        return NaN;
    }
    // A non-hexadecimal, non-infinity number:
    // just try a normal floating point conversion

    jsString_t sub = JS_Substring(cx, s, start, end + 1);
    double d;

    if (sscanf(sub, "%lf", &d) == 1) {
	JS_DeleteZoneObj(cx, sub);
        return d;
    } else {
	JS_DeleteZoneObj(cx, sub);
        return NaN;
    }
}
/**
 * Helper function for builtin objects that use the varargs form.
 * ECMA function formal arguments are undefined if not supplied;
 * this function pads the argument array out to the expected
 * length, if necessary.
 */
jsVal_t *
JSScriptRuntime::padArguments(JSContext *cx, int numArgs, jsVal_t *args,
			int count)
{
    int i;
    jsVal_t *result = (jsVal_t *) JS_NewZoneObj(cx, sizeof (*result) * count);

    for (i = 0; i < numArgs; i++) {
        result[i] = args[i];
    }

    for (; i < count; i++) {
        result[i] = JS_VAL_VOID;
    }

    return result;
}
/**
 * For escaping strings printed by object and array literals; not quite
 * the same as 'escape.'
 */
jsString_t
JSScriptRuntime::escapeString(JSContext *cx, jsString_t s)
{
    // ack!  Java lacks \v.
    // was escapeMap = "\bb\ff\nn\rr\tt\u000bv\"\"''";
    static const jsString_t escapeMap = "\bb\ff\nn\rr\tt\vv\"\"''";
    JSStringBuffer result(cx);

    int length = strlen(s);
    for (int i = 0; i < length; i++) {
        jsChar_t c = s[i];

        // an ordinary print character
        if (c >= ' ' && c <= '~'     // string.h isprint()
            && c != '"') {
            result.append(c);
            continue;
        }

        // an \escaped sort of character
        jsString_t s;

        if ((s = index(escapeMap, c)) != NULL) {
            result.append("\\");
            result.append(escapeMap[(s - escapeMap) + 1]);
            continue;
        }

        // 2-digit hex?
        if ((c & 0xff) < 256) {
	    jsChar_t hex[4];

            result.append("\\x");
	    sprintf(hex, "%02x", (int) c);
            result.append(hex);
            continue;
        }

/* 
        // nope.  Unicode.
	jsChar_t hex[4];

        result.append("\\u");
	sprintf(hex, "%02x", (int) c);
        result.append(hex);
*/
    }

    return result.offString();
}
/**
 * Convert the value to a string.
 *
 * See ECMA 9.8.
 */
jsString_t
JSScriptRuntime::toString(JSContext *cx, jsVal_t val)
{
    for (;;) {
        if (val == JS_VAL_NULL) {
            return JS_NewZoneString(cx, "null");
	}
        if (val == JS_VAL_VOID) {
            return JS_NewZoneString(cx, "undefined");
	}
        if (JS_VAL_IS_OBJECT(val)) {
            val = JS_VAL_TO_OBJECT(val)->getDefaultValue(cx, JS_T_String);
            if (val != JS_VAL_VOID && JS_VAL_IS_OBJECT(val)) {
                cx->exceptionHandler->throwEx(
                        cx,
			errorWithClassName(cx, "msg.primitive.expected", val));
            }
            continue;
        }
        if (JS_VAL_IS_INT(val)) {
            return numberToString(cx, JS_VAL_TO_INT(val), 10);
	}
	if (JS_VAL_IS_DOUBLE(val)) {
            return numberToString(cx, *JS_VAL_TO_DOUBLE(val), 10);
        }
	if (JS_VAL_IS_BOOLEAN(val)) {
            return JS_NewZoneString(cx, JS_VAL_TO_BOOLEAN(val) ?
				(jsString_t) "true" : (jsString_t) "false");
	}
	return JS_NewZoneString(cx, JS_VAL_TO_STRING(val));
    }
}

jsString_t
JSScriptRuntime::numberToString(JSContext *cx, double d, int base)
{
#ifdef VX
    if (isNan(d)) {
#else
    if (isnan(d)) {
#endif
        return JS_NewZoneString(cx, "NaN");
    }
    if (d == POSITIVE_INFINITY) {
        return JS_NewZoneString(cx, "Infinity");
    }
    if (d == NEGATIVE_INFINITY) {
        return JS_NewZoneString(cx, "-Infinity");
    }
    if (d == 0.0) {
        return JS_NewZoneString(cx, "0");
    }

    if ((base < 2) || (base > 36)) {
        cx->exceptionHandler->throwEx(
                cx,
		JSContext::reportRuntimeError(cx,
				getMessage(cx, "msg.bad.radix %d", base)));
    }

#ifndef DTOA
    jsChar_t buf[32];

    if (base == 16 && floor(d) == d && ((int32_t) d) == d) {
	sprintf(buf, "%#x", (int32_t) floor(d));
        return JS_NewZoneString(cx, buf);
    }
    if (base == 10) {
	if (floor(d) == d && ((int64_t) d) == d) {
	    sprintf(buf, "%.0f", d);
            return JS_NewZoneString(cx, buf);
	}
	sprintf(buf, "%g", d);
        return JS_NewZoneString(cx, buf);
    }
    cx->exceptionHandler->throwEx(
            cx,
            JSContext::reportRuntimeError(cx,
			getMessage(cx, "msg.bad.radix %d", base)));
    /* NOT REACHED */
    return NULL;
#else
    if (base != 10) {
        return JSDToA::JS_dtobasestr(cx, base, d);
    } else {
        JSStringBuffer result(cx);

        JSDToA::JS_dtostr(cx, &result, JSDToA::DTOSTR_STANDARD, 0, d);
        return result.offString();
    }
#endif
}
/**
 * Convert the value to an object.
 *
 * See ECMA 9.9.
 */
JSScriptable *
JSScriptRuntime::toObject(JSContext *cx, JSScriptable *scope,
			jsVal_t val)
{
    if (val == JS_VAL_NULL) {
        cx->exceptionHandler->throwEx(
                cx,
		JSNativeGlobal::constructError(cx, "TypeError",
			JSScriptRuntime::getMessage(cx, "msg.null.to.object"),
			scope));
    }
    if (val == JS_VAL_VOID) {
        cx->exceptionHandler->throwEx(
                cx,
		JSNativeGlobal::constructError(cx, "TypeError",
			JSScriptRuntime::getMessage(cx, "msg.undef.to.object",
						NULL),
			scope));
    }
    if (JS_VAL_IS_OBJECT(val)) {
        return JS_VAL_TO_OBJECT(val);
    }
    jsString_t className;

    if (JS_VAL_IS_BOOLEAN(val)) {
        className = (jsString_t) "Boolean";
    } else if (JS_VAL_IS_NUMBER(val)) {
        className = (jsString_t) "Number";
    } else {
        className = (jsString_t) "String";
    }

    jsVal_t args[1] = { val };

    scope = JSScriptableObject::getTopLevelScope(cx, scope);
    JSScriptable *result = newObject(cx, scope, className, 1, args);

    return result;
}
JSScriptable *
JSScriptRuntime::newObject(JSContext *cx, JSScriptable *scope,
			jsString_t constructorName, int numArgs, jsVal_t *args)
{
    JSException *ex;

    if ((ex = enterTry(cx)) == NULL) {
	JSScriptable *result = cx->newObject(scope, constructorName, numArgs,
					args);

	cx->exceptionHandler->exitTry();
	return result;
    }
    cx->exceptionHandler->throwEx(cx, cx->reportRuntimeError(cx, ex->getMessage()));
    /* NOT REACHED */
    return NULL;
}
/**
 *
 * See ECMA 9.4.
 */
double
JSScriptRuntime::toInteger(JSContext *cx, jsVal_t val)
{
    double d = toNumber(cx, val);

    // if it's NaN
#ifdef VX
    if (isNan(d)) {
#else
    if (isnan(d)) {
#endif
        return +0.0;
    }

#ifdef VX
    if (d == 0.0 || isInf(d)) {
#else
    if (d == 0.0 || !finite(d)) {
#endif
        return d;
    }

    if (d > 0.0) {
        return floor(d);
    } else {
        return ceil(d);
    }
}
// convenience method
double
JSScriptRuntime::toInteger(double d)
{
    // if it's NaN
#ifdef VX
    if (isNan(d)) {
#else
    if (isnan(d)) {
#endif
        return +0.0;
    }

#ifdef VX
    if (d == 0.0 || isInf(d)) {
#else
    if (d == 0.0 || !finite(d)) {
#endif
        return d;
    }

    if (d > 0.0) {
        return floor(d);
    } else {
        return ceil(d);
    }
}
/**
 *
 * See ECMA 9.5.
 */
int
JSScriptRuntime::toInt32(JSContext *cx, jsVal_t val)
{
    // short circuit for common small values; TokenStream
    // returns them as Bytes.
    if (JS_VAL_IS_INT(val)) {
        return JS_VAL_TO_INT(val);
    }

    return toInt32(toNumber(cx, val));
}
int
JSScriptRuntime::toInt32(double d)
{
    // 0x100000000 gives me a numeric overflow...
    const double two32 = 4294967296.0;
    const double two31 = 2147483648.0;

#ifdef VX
    if (isInf(d) || d == 0.0 || isInf(d)) {
#else
    if (d == 0.0 || !finite(d)) {
#endif
        return 0;
    }

    d = fmod(d, two32); // d = Math.IEEEremainder(d, two32);

    d = d >= 0
        ? d
        : d + two32;

    if (d >= two31) {
        return (int)(d - two32);
    } else {
        return (int)d;
    }
}
/**
 *
 * See ECMA 9.6.
 */
uint64_t
JSScriptRuntime::toUint32(double d)
{
    // 0x100000000 gives me a numeric overflow...
    const double two32 = 4294967296.0;

#ifdef VX
    if (isNan(d) || d == 0.0 || isInf(d)) {
#else
    if (d == 0.0 || !finite(d)) {
#endif
        return 0;
    }

    if (d > 0.0) {
        d = floor(d);
    } else {
        d = ceil(d);
    }

    d = fmod(d, two32); // d = Math.IEEEremainder(d, two32);

    d = d >= 0
	? d
	: d + two32;

    return (uint64_t) floor(d);
}
uint64_t
JSScriptRuntime::toUint32(JSContext *cx, jsVal_t val)
{
    return toUint32(toNumber(cx, val));
}
/**
 *
 * See ECMA 9.7.
 */
uint16_t
JSScriptRuntime::toUint16(JSContext *cx, jsVal_t val)
{
    const int int16 = 0x10000;

    double d = toNumber(cx, val);

#ifdef VX
    if (isNan(d) || d == 0.0 || isInf(d)) {
#else
    if (d == 0.0 || !finite(d)) {
#endif
	return 0;
    }

    d = fmod(d, int16);  // d = Math.IEEEremainder(d, int16);

    d = d >= 0
        ? d
	: d + int16;

    return (uint16_t) floor(d);
}
/**
 * Unwrap a JavaScriptException.  Sleight of hand so that we don't
 * javadoc JavaScriptException.getRuntimeValue().
 */
jsVal_t
JSScriptRuntime::unwrapJavaScriptException(JSJavaScriptException *jse)
{
    return jse->value;
}

jsVal_t
JSScriptRuntime::getProp(JSContext *cx, jsVal_t obj, jsString_t id,
			JSScriptable *scope)
{
    JSScriptable *start;
    /*
    if(obj == NULL){
        cx->exceptionHandler->throwEx(
	     JSNativeGlobal::constructError(cx, "TypeError",
	     JSScriptRuntime::getMessage(cx, "msg.undef.obj.parent.of %s",
					 id, scope)));
    } // 20040608.ichino
    */
    if (JS_VAL_IS_OBJECT(obj)) {
        start = JS_VAL_TO_OBJECT(obj);
    } else {
        start = toObject(cx, scope, obj);
    }
    if (start == NULL) {
        jsString_t msg = start == NULL ? (jsString_t) "msg.null.to.object"
				       : (jsString_t) "msg.undefined";

        cx->exceptionHandler->throwEx(
                cx,
		JSNativeGlobal::constructError(cx, "ConversionError",
					JSScriptRuntime::getMessage(cx, msg),
					scope));
    }
    JSScriptable *m = start;

    do {
	jsVal_t result;
	jsBool_t ok = m->get(cx, id, start, &result);

        if (ok) {
            return result;
	}
        m = m->getPrototype(cx);
    } while (m != NULL);
    return JS_VAL_VOID;
}

jsBool_t
JSScriptRuntime::getTopLevelProp(JSContext *cx, JSScriptable *scope,
				jsString_t id, jsVal_t *v)
{
    JSScriptable *s = JSScriptableObject::getTopLevelScope(cx, scope);

    do {
	jsBool_t ok = s->get(cx, id, s, v);

        if (ok) {
            return TRUE;
	}
        s = s->getPrototype(cx);
    } while (s != NULL);
    return FALSE;
}
/***********************************************************************/
JSScriptable *
JSScriptRuntime::getProto(JSContext *cx, jsVal_t obj, JSScriptable *scope)
{
    JSScriptable *s;

    if (JS_VAL_IS_OBJECT(obj)) {
        s = JS_VAL_TO_OBJECT(obj);
    } else {
        s = toObject(cx, scope, obj);
    }
    if (s == NULL) {
        cx->exceptionHandler->throwEx(
                cx,
		JSNativeGlobal::constructError(cx, "TypeError",
			JSScriptRuntime::getMessage(cx, "msg.null.to.object"),
			scope));
    }
    return s->getPrototype(cx);
}
JSScriptable *
JSScriptRuntime::getParent(JSContext *cx, jsVal_t obj)
{
    JSScriptable *s;
 
    if (JS_VAL_IS_OBJECT(obj)) {
	s = JS_VAL_TO_OBJECT(obj);
    } else {
	return NULL;
    }
    if (s == NULL) {
        return NULL;
    }
    return getThis(cx, s->getParentScope(cx));
}
JSScriptable *
JSScriptRuntime::getParent(JSContext *cx, jsVal_t obj, JSScriptable *scope)
{
    JSScriptable *s;

    if (JS_VAL_IS_OBJECT(obj)) {
        s = JS_VAL_TO_OBJECT(obj);
    } else {
        s = toObject(cx, scope, obj);
    }
    if (s == NULL) {
        cx->exceptionHandler->throwEx(
                cx,
		JSNativeGlobal::constructError(cx, "TypeError",
			JSScriptRuntime::getMessage(cx, "msg.null.to.object"),
			scope));
    }
    return s->getParentScope(cx);
}
jsVal_t
JSScriptRuntime::setProto(JSContext *cx, jsVal_t obj, jsVal_t value,
			JSScriptable *scope)
{
    JSScriptable *start;

    if (JS_VAL_IS_OBJECT(obj)) {
        start = JS_VAL_TO_OBJECT(obj);
    } else {
        start = toObject(cx, scope, obj);
    }
    JSScriptable *result
		= value == JS_VAL_NULL ? NULL : toObject(cx, scope, value);
    JSScriptable *s = result;

    while (s != NULL) {
        if (s == start) {
            cx->exceptionHandler->throwEx(
                    cx,
			JSContext::reportRuntimeError(cx, 
				getMessage(cx, "msg.cyclic.value %s",
					"__proto__")));
        }
        s = s->getPrototype(cx);
    }
    if (start == NULL) {
        cx->exceptionHandler->throwEx(
                cx,
		JSNativeGlobal::constructError(cx, "TypeError",
			JSScriptRuntime::getMessage(cx, "msg.null.to.object"),
			scope));
    }
    start->setPrototype(cx, result);

    return JS_OBJECT_TO_VAL(result);
}
jsVal_t
JSScriptRuntime::setParent(JSContext *cx, jsVal_t obj, jsVal_t value,
			JSScriptable *scope)
{
    JSScriptable *start;

    if (JS_VAL_IS_OBJECT(obj)) {
        start = JS_VAL_TO_OBJECT(obj);
    } else {
        start = toObject(cx, scope, obj);
    }
    JSScriptable *result
		= value == JS_VAL_NULL ? NULL : toObject(cx, scope, value);
    JSScriptable *s = result;

    while (s != NULL) {
        if (s == start) {
            cx->exceptionHandler->throwEx(
                    cx,
                    JSContext::reportRuntimeError(cx, 
					getMessage(cx, "msg.cyclic.value")));
        }
        s = s->getParentScope(cx);
    }
    if (start == NULL) {
        cx->exceptionHandler->throwEx(
                cx,
        	JSNativeGlobal::constructError(cx, "TypeError",
			JSScriptRuntime::getMessage(cx, "msg.null.to.object"),
			scope));
    }
    start->setParentScope(cx, result);
    return JS_OBJECT_TO_VAL(result);
}
jsVal_t
JSScriptRuntime::setProp(JSContext *cx, jsVal_t obj, jsString_t id,
			jsVal_t value, JSScriptable *scope)
{
    JSScriptable *start;
    /*
    if(obj == NULL){
        cx->exceptionHandler->throwEx(
             cx,
	     JSNativeGlobal::constructError(cx, "TypeError",
	     JSScriptRuntime::getMessage(cx, "msg.undef.obj.parent.of %s",
					 id, scope)));
    } // 20040608.ichino
    */
    if (JS_VAL_IS_OBJECT(obj)) {
        start = JS_VAL_TO_OBJECT(obj);
    } else {
        start = toObject(cx, scope, obj);
    }
    if (start == NULL) {
        cx->exceptionHandler->throwEx(
                cx,
        	JSNativeGlobal::constructError(cx, "TypeError",
			JSScriptRuntime::getMessage(cx, "msg.null.to.object"),
			scope));
    }
    JSScriptable *m = start;

    do {
        if (m->has(cx, id, start)) {
            m->put(cx, id, start, value);
            return value;
        }
        m = m->getPrototype(cx);
    } while (m != NULL);

    start->put(cx, id, start, value);
    return value;
}
int
JSScriptRuntime::indexFromString(jsString_t str)
{
    /* It must be a string. */
    int len = strlen(str);
    jsChar_t c;

    if (len > 0 && '0' <= (c = str[0]) && c <= '9' && len <= MAX_VALUE_LENGTH) {
	int index = c - '0';
        int oldIndex = 0;
        int i = 1;

        if (index != 0) {
            while (i < len && '0' <= (c = str[i]) && c <= '9') {
                oldIndex = index;
                index = 10 * index + (c - '0');
	        i++;
	    }
        }
        /* Make sure all characters were consumed and that it couldn't
         * have overflowed.
         */
        if (i == len
	   && (oldIndex < (INT_MAX / 10)
		|| (oldIndex == (INT_MAX / 10)
			&& c < (INT_MAX % 10)))) {
            return index;
        }
    }
    return NO_INDEX;
}
jsString_t
JSScriptRuntime::getStringId(JSContext *cx, jsVal_t id)
{
    if (JS_VAL_IS_INT(id)) {
	return NULL;
    }
    if (JS_VAL_IS_DOUBLE(id)) {
	double d = *JS_VAL_TO_DOUBLE(id);

	if (floor(d) == d) {
            return NULL;
	}
        return toString(cx, id);
    }
    jsString_t s = toString(cx, id);
    int index = indexFromString(s);

    if (index != NO_INDEX || (strlen(s) == 1 && s[0] == NO_INDEX_CHAR)) {
	JS_DeleteZoneObj(cx, s);
	return NULL;
    }
    return s;
}
int
JSScriptRuntime::getIntId(JSContext *cx, jsVal_t id)
{
    if (JS_VAL_IS_INT(id)) {
	return JS_VAL_TO_INT(id);
    }
    if (JS_VAL_IS_DOUBLE(id)) {
        double d = *JS_VAL_TO_DOUBLE(id);
	int index = (int) d;
        if (((double) index) == d) {
            return (int) d;
	}
        return 0;
    }
    jsString_t s = toString(cx, id);
    int index = indexFromString(s);

    if (index != NO_INDEX || (strlen(s) == 1 && s[0] == NO_INDEX_CHAR)) {
	JS_DeleteZoneObj(cx, s);
        return index;
    }
    JS_DeleteZoneObj(cx, s);
    return 0;
}
jsVal_t
JSScriptRuntime::getElem(JSContext *cx, jsVal_t obj, jsVal_t id,
			JSScriptable *scope)
{
    int index;
    jsString_t s;

    if(obj == JS_VAL_NULL){
        cx->exceptionHandler->throwEx(
                cx,
                JSNativeGlobal::constructError(cx, "TypeError",
                    JSScriptRuntime::getMessage(cx, "msg.undef.obj.parent.of %s",
					 toString(cx, id)), scope));
    } // 20040608.ichino
    if (JS_VAL_IS_INT(id)) {
	s = NULL;
	index = JS_VAL_TO_INT(id);
    }
    if (JS_VAL_IS_DOUBLE(id)) {
        double d = *JS_VAL_TO_DOUBLE(id);

        index = (int) d;
        s = ((double) index) == d ? NULL : toString(cx, id);
    } else {
        s = toString(cx, id);
        index = indexFromString(s);

        if (index != NO_INDEX) {
	    JS_DeleteZoneObj(cx, s);
            s = NULL;
        } else if (strlen(s) == 1 && s[0] == NO_INDEX_CHAR) {
            // disambiguate return value: was actually a number
	    JS_DeleteZoneObj(cx, s);
            s = NULL;
        }
    }
    JSScriptable *start = JS_VAL_IS_OBJECT(obj)
				?  JS_VAL_TO_OBJECT(obj)
				: toObject(cx, scope, obj);
    JSScriptable *m = start;

    if (s != NULL) {
        if (strcmp(s, "__proto__") == 0) {
	    JS_DeleteZoneObj(cx, s);
            return JS_OBJECT_TO_VAL(start->getPrototype(cx));
	}
        if (strcmp(s, "__parent__") == 0) {
	    JS_DeleteZoneObj(cx, s);
            return JS_OBJECT_TO_VAL(start->getParentScope(cx));
	}
        while (m != NULL) {
	    jsVal_t result;
            jsBool_t ok = m->get(cx, s, start, &result);

            if (ok) {
		JS_DeleteZoneObj(cx, s);
                return result;
	    }
            m = m->getPrototype(cx);
        }
	JS_DeleteZoneObj(cx, s);
        return JS_VAL_VOID;
    }
    while (m != NULL) {
	jsVal_t result;
        jsBool_t ok = m->get(cx, index, start, &result);

        if (ok) {
            return result;
	}
        m = m->getPrototype(cx);
    }
    return JS_VAL_VOID;
}
/*
 * A cheaper and less general version of the above for well-known argument
 * types.
 */
jsVal_t
JSScriptRuntime::getElem(JSContext *cx, JSScriptable *obj, int index)
{
    JSScriptable *m = obj;
    while (m != NULL) {
	jsVal_t result;
        jsBool_t ok = m->get(cx, index, obj, &result);

        if (ok) {
            return result;
	}
        m = m->getPrototype(cx);
    }
    return JS_VAL_VOID;
}
jsVal_t
JSScriptRuntime::setElem(JSContext *cx, jsVal_t obj, jsVal_t id, jsVal_t value,
			JSScriptable *scope)
{
    int index;
    jsString_t s;

    if(obj == JS_VAL_NULL){
        cx->exceptionHandler->throwEx(
                cx,
                JSNativeGlobal::constructError(cx, "TypeError",
                    JSScriptRuntime::getMessage(cx, "msg.undef.obj.parent.of %s",
					 toString(cx, id)), scope));
    }
    if (JS_VAL_IS_INT(id)) {
	index = JS_VAL_TO_INT(id);
	s = NULL;
    }
    if (JS_VAL_IS_NUMBER(id)) {
        double d = JS_VAL_TO_NUMBER(id);

        index = (int) d;
        s = ((double) index) == d ? NULL : toString(cx, id);
    } else {
        s = toString(cx, id);

        index = indexFromString(s);
        if (index != NO_INDEX) {
	    JS_DeleteZoneObj(cx, s);
            s = NULL;
	} else if (strlen(s) == 1 && s[0] == NO_INDEX_CHAR) {
            // disambiguate return value: was actually a number
	    JS_DeleteZoneObj(cx, s);
            s = NULL;
        }
    }
    JSScriptable *start = JS_VAL_IS_OBJECT(obj)
				? JS_VAL_TO_OBJECT(obj)
				: toObject(cx, scope, obj);
    JSScriptable *m = start;

    if (s != NULL) {
        if (strcmp(s, "__proto__") == 0) {
	    JS_DeleteZoneObj(cx, s);
            return setProto(cx, obj, value, scope);
	}
        if (strcmp(s, "__parent__") == 0) {
	    JS_DeleteZoneObj(cx, s);
            return setParent(cx, obj, value, scope);
	}
	if(m != NULL){
            do {
                if (m->has(cx, s, start)) {
                    m->put(cx, s, start, value);
                    JS_DeleteZoneObj(cx, s);
                    return value;
                }
                m = m->getPrototype(cx);
            } while (m != NULL);
	}
        start->put(cx, s, start, value);
	JS_DeleteZoneObj(cx, s);
        return value;
    }

    if(m != NULL){
        do {
            if (m->has(cx, index, start)) {
                m->put(cx, index, start, value);
                return value;
            }
            m = m->getPrototype(cx);
        } while (m != NULL);
    }
    start->put(cx, index, start, value);
    return value;
}
/*
 * A cheaper and less general version of the above for well-known argument
 * types.
 */
jsVal_t
JSScriptRuntime::setElem(JSContext *cx, JSScriptable *obj, int index,
			jsVal_t value)
{
    JSScriptable *m = obj;

    do {
        if (m->has(cx, index, obj)) {
            m->put(cx, index, obj, value);
            return value;
        }
        m = m->getPrototype(cx);
    } while (m != NULL);
    obj->put(cx, index, obj, value);
    return value;
}
/**
 * The delete operator
 *
 * See ECMA 11.4.1
 *
 * In ECMA 0.19, the description of the delete operator (11.4.1)
 * assumes that the [[Delete]] method returns a value. However,
 * the definition of the [[Delete]] operator (8.6.2.5) does not
 * define a return value. Here we assume that the [[Delete]]
 * method doesn't return a value.
 */
jsVal_t
JSScriptRuntime::del(JSContext *cx, jsVal_t obj, jsVal_t id)
{
    if (!JS_VAL_IS_OBJECT(obj)) {
        return JS_VAL_TRUE;
    }
    JSFlattenedObject f(JS_VAL_TO_OBJECT(obj));
    return f.deleteProperty(cx, id) ? JS_VAL_TRUE : JS_VAL_FALSE;
}
/**
 * Looks up a name in the scope chain and returns its value.
 */
jsVal_t
JSScriptRuntime::name(JSContext *cx, JSScriptable *scopeChain, jsString_t id)
{
    JSScriptable *obj = scopeChain;

    while (obj != NULL) {
        JSScriptable *m = obj;

        do {
	    jsVal_t result;
            jsBool_t ok = m->get(cx, id, obj, &result);

            if (ok) {
                return result;
	    }
            m = m->getPrototype(cx);
        } while (m != NULL);
        obj = obj->getParentScope(cx);
    }
    cx->exceptionHandler->throwEx(
            cx,
            JSNativeGlobal::constructError(cx, "ReferenceError",
			JSScriptRuntime::getMessage(cx,
						"msg.is.not.defined %s", id),
			scopeChain));
    /* NOT REACHED */
    return JS_VAL_VOID;
}
/**
 * Returns the object in the scope chain that has a given property.
 *
 * The order of evaluation of an assignment expression involves
 * evaluating the lhs to a reference, evaluating the rhs, and then
 * modifying the reference with the rhs value. This method is used
 * to 'bind' the given name to an object containing that property
 * so that the side effects of evaluating the rhs do not affect
 * which property is modified.
 * Typically used in conjunction with setName.
 *
 * See ECMA 10.1.4
 */
JSScriptable *
JSScriptRuntime::bind(JSContext *cx, JSScriptable *scope, jsString_t id)
{
    JSScriptable *obj = scope;

    while (obj != NULL) {
        JSScriptable *m = obj;

        do {
            if (m->has(cx, id, obj)) {
                return obj;
	    }
            m = m->getPrototype(cx);
        } while (m != NULL);
        obj = obj->getParentScope(cx);
    }
    return NULL;
}
JSScriptable *
JSScriptRuntime::getBase(JSContext *cx, JSScriptable *scope, jsString_t id)
{
    JSScriptable *obj = scope;

    while (obj != NULL) {
        JSScriptable *m = obj;

        do {
	    jsVal_t val;

            if (m->get(cx, id, obj, &val)) {
                return obj;
	    }
            m = m->getPrototype(cx);
        } while (m != NULL);
        obj = obj->getParentScope(cx);
    }
    cx->exceptionHandler->throwEx(
            cx,
            JSNativeGlobal::constructError(cx, "ReferenceError",
			JSScriptRuntime::getMessage(cx,
						"msg.is.not.defined %s", id),
			scope));
    /* NOT REACHED */
    return NULL;
}
JSScriptable *
JSScriptRuntime::getThis(JSContext *cx, JSScriptable *base)
{
    while (base != NULL && base->isInstanceOf(JSNativeWith::className)) {
        base = base->getPrototype(cx);
    }
    if (base != NULL && base->isInstanceOf(JSNativeCall::className)) {
        base = JSScriptableObject::getTopLevelScope(cx, base);
    }
    return base;
}
jsVal_t
JSScriptRuntime::setName(JSContext *cx, JSScriptable *bound, jsVal_t value,
			JSScriptable *scope, jsString_t id)
{
    if (bound == NULL) {
        // "newname = 7;", where 'newname' has not yet
        // been defined, creates a new property in the
        // global object. Find the global object by
        // walking up the scope chain.
        JSScriptable *next = scope;

        do {
            bound = next;
            next = bound->getParentScope(cx);
        } while (next != NULL);

        bound->put(cx, id, bound, value);

        jsString_t message = getMessage(cx, "msg.assn.create %s", id);

        JSContext::reportWarning(cx, message);
        return value;
    }
    return setProp(cx, JS_OBJECT_TO_VAL(bound), id, value, scope);
}
JSIdEnumeration *
JSScriptRuntime::initEnum(JSContext *cx, jsVal_t value, JSScriptable *scope)
{
    JSScriptable *m = toObject(cx, scope, value);

    return new (cx) JSIdEnumeration(cx, m);
}
jsVal_t
JSScriptRuntime::nextEnum(JSIdEnumeration *enumeration)
{
    // OPT this could be more efficient; should junk the Enumeration
    // interface
    if (!enumeration->hasMoreElements()) {
        return JS_VAL_NULL;
    }
    return enumeration->nextElement();
}
// Form used by class files generated by 1.4R3 and earlier.
jsVal_t
JSScriptRuntime::call(JSContext *cx, jsVal_t fun, jsVal_t thisArg,
			int numArgs, jsVal_t *args)
{
    JSScriptable *scope = NULL;

    if (JS_VAL_IS_OBJECT(fun)) {
        scope = JS_VAL_TO_OBJECT(fun)->getParentScope(cx);
    }
    return call(cx, fun, thisArg, numArgs, args, scope);
}
jsVal_t
JSScriptRuntime::call(JSContext *cx, jsVal_t fun, jsVal_t thisArg,
			int numArgs, jsVal_t *args, JSScriptable *scope)
{
    JSFunction *function;

    if (JS_VAL_IS_OBJECT(fun) && (fun != JS_VAL_NULL) ) {
	JSScriptable *scriptable = JS_VAL_TO_OBJECT(fun);

	if (scriptable->isInstanceOf(JSNativeFunction::className)) {
	    function = (JSNativeFunction *) scriptable;
	} else if (scriptable->isInstanceOf(JSNativeHost::className)) {
	    function = (JSNativeHost *) scriptable;
#ifdef REGEXP
	} else if (scriptable->isInstanceOf(JSNativeRegExp::className)) {
	    function = (JSNativeRegExp *) scriptable;
#endif
	} else {
	    function = NULL;
	}
	if (function != NULL) {
	    JSScriptable *thisObj;

	    if (JS_VAL_IS_OBJECT(thisArg) || thisArg == JS_VAL_NULL) {
		thisObj = (JSScriptable *) thisArg;
	    } else {
		thisObj = JSScriptRuntime::toObject(cx, scope, thisArg);
	    }
	    return function->call(cx, scope, thisObj, numArgs, args);
	}
    }
    cx->exceptionHandler->throwEx(
            cx,
            JSNativeGlobal::constructError(cx, "TypeError",
			JSScriptRuntime::getMessage(cx, "msg.isnt.function %s",
                                             toString(cx, fun)),
			scope));
    /* NOT REACHED */
    return JS_VAL_VOID;
}
jsVal_t
JSScriptRuntime::callOrNewSpecial(JSContext *cx, JSScriptable *scope,
				jsVal_t fun, jsVal_t jsThis, jsVal_t thisArg,
				int numArgs, jsVal_t *args, jsBool_t isCall,
                                jsString_t filename, int lineNumber)
{
    if (JS_VAL_IS_OBJECT(fun)
	&& JS_VAL_TO_OBJECT(fun)->isInstanceOf(JSFunctionObject::className)) {
        JSFunctionObject *fo = (JSFunctionObject *) JS_VAL_TO_OBJECT(fun);
	
	jsString_t *names = fo->names;
	jsString_t clazz = fo->clazz;

        if (strcmp(names[0], "eval") == 0
	    && clazz == JSNativeGlobal::className) {
            return JSNativeGlobal::evalSpecial(cx, scope,
					JS_VAL_TO_OBJECT(thisArg), numArgs,
					args, filename, lineNumber);
	}
        if (strcmp(names[0], "With") == 0
	    && clazz == JSNativeWith::className) {
            return JSNativeWith::newWithSpecial(cx, numArgs, args, fo, !isCall);
	}
        if (strcmp(names[0], "exec") == 0
	    && clazz == JSNativeScript::className) {
            return (
		((JSNativeScript *) JS_VAL_TO_OBJECT(jsThis))
			->exec(cx, JSScriptableObject::getTopLevelScope(cx,
									scope))
	    ) ;
	}
        if (strcmp(names[0], "exec") == 0 && (cx->getRegExpProxy() != NULL)
	    && (cx->getRegExpProxy()->isRegExp(jsThis))) {
            return call(cx, fun, jsThis, numArgs, args, scope);
	}
    }

    if (isCall) {
        return call(cx, fun, thisArg, numArgs, args, scope);
    }
    return JS_OBJECT_TO_VAL(newObject(cx, fun, numArgs, args, scope));
}
jsVal_t
JSScriptRuntime::callSpecial(JSContext *cx, jsVal_t fun,
			jsVal_t thisArg, int numArgs, jsVal_t *args,
			JSScriptable *enclosingThisArg,
			JSScriptable *scope, jsString_t filename,
			int lineNumber)
{
    return callOrNewSpecial(cx, scope, fun, thisArg,
                        JS_OBJECT_TO_VAL(enclosingThisArg), numArgs, args,
			TRUE, filename, lineNumber);
}
/**
 * Operator new.
 *
 * See ECMA 11.2.2
 */
JSScriptable *
JSScriptRuntime::newObject(JSContext *cx, jsVal_t fun,
			int numArgs, jsVal_t *args, JSScriptable *scope)
{
    JSFunction *f;

    if (JS_VAL_IS_OBJECT(fun) && fun != JS_VAL_NULL) {
	if (JS_VAL_TO_OBJECT(fun)->isInstanceOf(JSNativeFunction::className)) {
            f = (JSNativeFunction *) JS_VAL_TO_OBJECT(fun);
#ifdef REGEXP
	} else if (JS_VAL_TO_OBJECT(fun)
			->isInstanceOf(JSNativeRegExp::className)) {
            f = (JSNativeRegExp *) JS_VAL_TO_OBJECT(fun);
#endif
	} else {
	    f = NULL;
	}

        if (f != NULL) {
            return f->construct(cx, scope, numArgs, args);
        }
    }
    cx->exceptionHandler->throwEx(
            cx,
            JSNativeGlobal::constructError(cx, "TypeError",
			JSScriptRuntime::getMessage(cx, "msg.isnt.function %s",
					toString(cx, fun)),
			scope));
    /* NOT REACHED */
    return NULL;
}
JSScriptable *
JSScriptRuntime::newObjectSpecial(JSContext *cx, jsVal_t fun, int numArgs,
				jsVal_t *args, JSScriptable *scope)
{
    return JS_VAL_TO_OBJECT(callOrNewSpecial(cx, scope, fun, 
					JS_VAL_NULL, JS_VAL_NULL,
					numArgs, args, FALSE, NULL, -1));
}
/**
 * The typeof operator
 */
jsString_t
JSScriptRuntime::doTypeof(JSContext *cx, jsVal_t value) 
{
    if (value == JS_VAL_VOID) {
        return "undefined";
    }
    if (value == JS_VAL_NULL) {
        return "object";
    }
    if (JS_VAL_IS_OBJECT(value)) {
        return (
#ifdef REGEXP
	    (JS_VAL_TO_OBJECT(value)->isInstanceOf(JSNativeFunction::className)
		|| JS_VAL_TO_OBJECT(value)->isInstanceOf(
						JSNativeRegExp::className)) ?
		(jsString_t) "function" : (jsString_t) "object"
#else
	    JS_VAL_TO_OBJECT(value)->isInstanceOf(JSNativeFunction::className) ?
		(jsString_t) "function" : (jsString_t) "object"
#endif
	) ;
    }
    if (JS_VAL_IS_STRING(value)) {
        return "string";
    }
    if (JS_VAL_IS_INT(value) || JS_VAL_IS_DOUBLE(value)) {
        return "number";
    }
    if (JS_VAL_IS_BOOLEAN(value)) {
        return "boolean";
    }
    cx->exceptionHandler->throwEx(
            cx,
            errorWithClassName(cx, "msg.invalid.type", value));
    /* NOT REACHED */
    return "";
}

/**
 * The typeof operator that correctly handles the undefined case
 */
jsString_t
JSScriptRuntime::typeofName(JSContext *cx, JSScriptable *scope, jsString_t id)
{
    jsVal_t val = JS_OBJECT_TO_VAL(bind(cx, scope, id));

    if (val == JS_VAL_NULL) {
        return "undefined";
    }
    return doTypeof(cx, getProp(cx, val, id, scope));
}
// neg:
// implement the '-' operator inline in the caller
// as "-toNumber(val)"

// not:
// implement the '!' operator inline in the caller
// as "!toBoolean(val)"

// bitnot:
// implement the '~' operator inline in the caller
// as "~toInt32(val)"
jsVal_t
JSScriptRuntime::add(JSContext *cx, jsVal_t val1, jsVal_t val2)
{
    if (JS_VAL_IS_OBJECT(val1) && val1 != JS_VAL_NULL) {
        val1 = JS_VAL_TO_OBJECT(val1)->getDefaultValue(cx, JS_T_Null);
    }
    if (JS_VAL_IS_OBJECT(val2) && val2 != JS_VAL_NULL) {
        val2 = JS_VAL_TO_OBJECT(val2)->getDefaultValue(cx, JS_T_Null);
    }
    if (!JS_VAL_IS_STRING(val1) && !JS_VAL_IS_STRING(val2)) {
	double d1, d2;

	if (JS_VAL_IS_NUMBER(val1) && JS_VAL_IS_NUMBER(val2)) {
	    d1 = JS_VAL_TO_NUMBER(val1);
	    d2 = JS_VAL_TO_NUMBER(val2);
	} else {
	    d1 = toNumber(cx, val1);
	    d2 = toNumber(cx, val2);
	}
        return toValue(cx, d1 + d2);
	// was JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, d1 + d2));
    }
    jsString_t s1, s2;

    s1 = JS_VAL_IS_STRING(val1) ? JS_VAL_TO_STRING(val1) : toString(cx, val1);
    s2 = JS_VAL_IS_STRING(val2) ? JS_VAL_TO_STRING(val2) : toString(cx, val2);

    int length = strlen(s1) + strlen(s2) + 1;
    jsString_t s = (jsString_t) JS_NewGCObj(cx, sizeof (*s) * length);

    sprintf(s, "%s%s", s1, s2);
    if (!JS_VAL_TO_STRING(val1)) JS_DeleteZoneObj(cx, s1);
    if (!JS_VAL_TO_STRING(val2)) JS_DeleteZoneObj(cx, s2);
    return JS_STRING_TO_VAL(s);
}
jsVal_t
JSScriptRuntime::postIncrement(JSContext *cx, jsVal_t value)
{
    if (JS_VAL_IS_NUMBER(value)) {
	double d = JS_VAL_TO_NUMBER(value);

	value = toValue(cx, d + 1.0);
	// was JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, d + 1.0));
    } else {
	value =  toValue(cx, toNumber(cx, value) + 1.0);
	// was JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, toNumber(cx, value) + 1.0));
    }
    return value;
}
jsVal_t
JSScriptRuntime::postIncrement(JSContext *cx, JSScriptable *scopeChain,
			jsString_t id)
{
    JSScriptable *obj = scopeChain;

    while (obj != NULL) {
        JSScriptable *m = obj;

        do {
	    jsVal_t result;
            jsBool_t ok = m->get(cx, id, obj, &result);

            if (ok) {
                jsVal_t newValue = result;

                if (JS_VAL_IS_NUMBER(newValue)) {
		    double d = JS_VAL_TO_NUMBER(newValue);

		    newValue = toValue(cx, d + 1.0);
		    // was JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, d + 1.0));
                    m->put(cx, id, obj, newValue);
                    return result;
                } else {
		    newValue = toValue(cx, toNumber(cx, newValue) + 1.0);
		    // was JS_DOUBLE_TO_VAL(
		    //			JS_NewGCDouble(cx,
		    //				toNumber(cx, newValue) + 1.0));
                    m->put(cx, id, obj, newValue);
                    return  toValue(cx, toNumber(cx, result));
		    // JS_DOUBLE_TO_VAL(
		    //		JS_NewGCDouble(cx, toNumber(cx, result)));
                }
            }
            m = m->getPrototype(cx);
        } while (m != NULL);
        obj = obj->getParentScope(cx);
    }
    cx->exceptionHandler->throwEx(
            cx,
            JSNativeGlobal::constructError(cx, "ReferenceError",
                	JSScriptRuntime::getMessage(cx,
						"msg.is.not.defined %s", id),
			scopeChain));
    /* NOT REACHED */
    return JS_VAL_VOID;
}
jsVal_t
JSScriptRuntime::postIncrement(JSContext *cx, jsVal_t obj, jsString_t id,
			JSScriptable *scope)
{
    JSScriptable *start;

    if (JS_VAL_IS_OBJECT(obj)) {
        start = JS_VAL_TO_OBJECT(obj);
    } else {
        start = toObject(cx, scope, obj);
    }
    if (start == NULL) {
	cx->exceptionHandler->throwEx(
                cx,
		JSNativeGlobal::constructError(cx, "TypeError",
			JSScriptRuntime::getMessage(cx, "msg.null.to.object"),
			scope));
    }
    JSScriptable *m = start;

    do {
	jsVal_t result;
	jsBool_t ok = m->get(cx, id, start, &result);

        if (ok) {
            jsVal_t newValue = result;

            if (JS_VAL_IS_NUMBER(newValue)) {
		double d = JS_VAL_TO_NUMBER(newValue);

		newValue = toValue(cx, d + 1.0);
		// was JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, d + 1.0));
                m->put(cx, id, start, newValue);
                return result;
            }
            else {
		newValue = toValue(cx, toNumber(cx, newValue) + 1.0);
		// was JS_DOUBLE_TO_VAL(
		//		JS_NewGCDouble(cx,
		//			toNumber(cx, newValue) + 1.0));
                m->put(cx, id, start, newValue);
                return toValue(cx, toNumber(cx, result));
		// was JS_DOUBLE_TO_VAL(
		//		JS_NewGCDouble(cx, toNumber(cx, result)));
            }
        }
        m = m->getPrototype(cx);
    } while (m != NULL);
    return JS_VAL_VOID;
}
jsVal_t
JSScriptRuntime::postIncrementElem(JSContext *cx, jsVal_t obj, jsVal_t index,
				JSScriptable *scope)
{
    jsVal_t oldValue = getElem(cx, obj, index, scope);

    if (oldValue == JS_VAL_VOID) {
        return JS_VAL_VOID;
    }
    double resultValue = toNumber(cx, oldValue);
    jsVal_t newValue = toValue(cx, resultValue + 1.0);
    // was JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, resultValue + 1.0));

    setElem(cx, obj, index, newValue, scope);
    return toValue(cx, resultValue);
    // was JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, resultValue));
}
jsVal_t
JSScriptRuntime::postDecrementElem(JSContext *cx, jsVal_t obj, jsVal_t index,
				JSScriptable *scope)
{
    jsVal_t oldValue = getElem(cx, obj, index, scope);

    if (oldValue == JS_VAL_VOID) {
        return JS_VAL_VOID;
    }
    double resultValue = toNumber(cx, oldValue);
    jsVal_t newValue = toValue(cx, resultValue - 1.0);
    // was JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, resultValue - 1.0));

    setElem(cx, obj, index, newValue, scope);
    return toValue(cx, resultValue);
    // was JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, resultValue));
}
jsVal_t
JSScriptRuntime::postDecrement(JSContext *cx, jsVal_t value)
{
    if (JS_VAL_IS_NUMBER(value)) {
	double d = JS_VAL_TO_NUMBER(value);

        value = toValue(cx, d - 1.0);
	// was JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, d - 1.0));
    } else {
        value = toValue(cx, toNumber(cx, value) - 1.0);
	// JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, toNumber(cx, value) - 1.0));
    }
    return value;
}
jsVal_t
JSScriptRuntime::postDecrement(JSContext *cx, JSScriptable *scopeChain,
			jsString_t id)
{
    JSScriptable *obj = scopeChain;

    while (obj != NULL) {
        JSScriptable *m = obj;

        do {
	    jsVal_t result;
            jsBool_t ok = m->get(cx, id, obj, &result);

            if (ok) {
                jsVal_t newValue = result;

		if (JS_VAL_IS_NUMBER(newValue)) {
		    double d = JS_VAL_TO_NUMBER(newValue);

                    newValue = toValue(cx, d - 1.0);
		    // JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, d - 1.0));
                    m->put(cx, id, obj, newValue);
                    return result;
                } else {
                    newValue = toValue(cx, toNumber(cx, newValue) - 1.0);
		    // JS_DOUBLE_TO_VAL(
		    //			JS_NewGCDouble(cx,
		    //				toNumber(cx, newValue) - 1.0));
                    m->put(cx, id, obj, newValue);
                    return toValue(cx, toNumber(cx, result));
		    // JS_DOUBLE_TO_VAL(
		    //		JS_NewGCDouble(cx, toNumber(cx, result)));
                }
            }
            m = m->getPrototype(cx);
        } while (m != NULL);
        obj = obj->getParentScope(cx);
    }
    cx->exceptionHandler->throwEx(
            cx,
            JSNativeGlobal::constructError(cx, "ReferenceError",
			JSScriptRuntime::getMessage(cx,
						"msg.is.not.defined %s", id),
			scopeChain));
    /* NOT REACHED */
    return JS_VAL_VOID;
}
jsVal_t
JSScriptRuntime::postDecrement(JSContext *cx, jsVal_t obj, jsString_t id,
			JSScriptable *scope)
{
    JSScriptable *start;

    if (JS_VAL_IS_OBJECT(obj)) {
        start = JS_VAL_TO_OBJECT(obj);
    } else {
        start = toObject(cx, scope, obj);
    }
    if (start == NULL) {
	cx->exceptionHandler->throwEx(
                cx,
		JSNativeGlobal::constructError(cx, "TypeError",
			JSScriptRuntime::getMessage(cx, "msg.null.to.object"),
			scope));
    }
    JSScriptable *m = start;

    do {
	jsVal_t result;
        jsBool_t ok = m->get(cx, id, start, &result);

        if (ok) {
            jsVal_t newValue = result;

            if (JS_VAL_IS_NUMBER(newValue)) {
		double d = JS_VAL_TO_NUMBER(newValue);

                newValue = toValue(cx, d - 1.0);
		// was JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, d - 1.0));
                m->put(cx, id, start, newValue);
                return result;
            } else {
                newValue = toValue(cx, toNumber(cx, newValue) - 1.0);
		// was JS_DOUBLE_TO_VAL(
		//		JS_NewGCDouble(cx,
		//			toNumber(cx, newValue) - 1.0));
                m->put(cx, id, start, newValue);
                return toValue(cx, toNumber(cx, result));
		// was JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx,
		//			toNumber(cx, result)));
            }
        }
        m = m->getPrototype(cx);
    } while (m != NULL);

    return JS_VAL_VOID;
}
jsVal_t
JSScriptRuntime::toPrimitive(JSContext *cx, jsVal_t val)
{
    if (val == JS_VAL_NULL || !JS_VAL_IS_OBJECT(val)) {
        return val;
    }
    jsVal_t result = JS_VAL_TO_OBJECT(val)->getDefaultValue(cx, JS_T_Null);

    if (result != JS_VAL_NULL && JS_VAL_IS_OBJECT(result)) {
        cx->exceptionHandler->throwEx(
                cx,
		JSNativeGlobal::constructError(cx, "TypeError",
			JSScriptRuntime::getMessage(cx, 
					"msg.bad.default.value"),
			JS_VAL_TO_OBJECT(val)));
    }
    return result;
}
/**
 * Equality
 *
 * See ECMA 11.9
 */
jsBool_t
JSScriptRuntime::eq(JSContext *cx, jsVal_t x, jsVal_t y)
{
    if (x == y) return TRUE;

    for (;;) {
        int tagX = JS_VAL_IS_INT(x) ? JS_VAL_INT : JS_VAL_TAG(x);
        int tagY = JS_VAL_IS_INT(y) ? JS_VAL_INT : JS_VAL_TAG(y);

        if (tagX == tagY) {
	    if (x == JS_VAL_VOID && y == JS_VAL_VOID) {
                return TRUE;
	    }
	    if (tagX == JS_VAL_INT) {
		return JS_VAL_TO_INT(x) == JS_VAL_TO_INT(y);
	    }
	    if (tagX == JS_VAL_DOUBLE) {
		return *JS_VAL_TO_DOUBLE(x) == *JS_VAL_TO_DOUBLE(y);
	    }
	    if (tagX == JS_VAL_STRING) {
		return strcmp(JS_VAL_TO_STRING(x), JS_VAL_TO_STRING(y)) == 0;
	    }
	    if (tagX == JS_VAL_BOOLEAN) {
		return JS_VAL_TO_BOOLEAN(x) == JS_VAL_TO_BOOLEAN(y);
	    }
	    if (tagX == JS_VAL_OBJECT) {
                if (x == y) {
                    return TRUE;
		}
		return FALSE;
	    }
	    // shouldn't get here
            cx->exceptionHandler->throwEx(cx, new (cx) JSRuntimeException());
        }
	if (JS_VAL_IS_NUMBER(x) && JS_VAL_IS_NUMBER(y)) {
	    double dx = JS_VAL_TO_NUMBER(x);
	    double dy = JS_VAL_TO_NUMBER(y);

	    return dx == dy;
	}
        if (x == JS_VAL_NULL && y == JS_VAL_VOID) {
            return TRUE;
	}
        if (x == JS_VAL_VOID && y == JS_VAL_NULL) {
            return TRUE;
	}
	if (x == JS_VAL_VOID || y == JS_VAL_VOID) {
            return FALSE;
	}
        if (JS_VAL_IS_NUMBER(tagX) && (tagY == JS_VAL_STRING)) {
	    double d = JS_VAL_TO_NUMBER(x);

            return d == toNumber(cx, y);
        }
        if (tagX == JS_VAL_STRING && JS_VAL_IS_NUMBER(y)) {
	    double d = JS_VAL_TO_NUMBER(y);

            return toNumber(cx, x) == d;
        }
        if (tagX == JS_VAL_BOOLEAN) {
            x = toValue(cx, toNumber(cx, x));
	    // was JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, toNumber(cx, x)));
            continue;
        }
        if (tagY == JS_VAL_BOOLEAN) {
            y = toValue(cx, toNumber(cx, y));
	    // was JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, toNumber(cx, y)));
            continue;
        }
        if ((tagX == JS_VAL_STRING || JS_VAL_IS_NUMBER(x))
	    && tagY == JS_VAL_OBJECT && y != JS_VAL_NULL) {
            y = toPrimitive(cx, y);
            continue;
        }
        if (tagX == JS_VAL_OBJECT && x != JS_VAL_NULL
	    && (tagY == JS_VAL_STRING || JS_VAL_IS_NUMBER(y))) {
            x = toPrimitive(cx, x);
            continue;
        }
        return FALSE;
    }
}
jsVal_t
JSScriptRuntime::eqB(JSContext *cx, jsVal_t x, jsVal_t y)
{
    if (eq(cx, x, y)) {
        return JS_VAL_TRUE;
    } else {
        return JS_VAL_FALSE;
    }
}

jsVal_t
JSScriptRuntime::neB(JSContext *cx, jsVal_t x, jsVal_t y)
{
    if (eq(cx, x,y)) {
        return JS_VAL_FALSE;
    } else {
        return JS_VAL_TRUE;
    }
}
jsBool_t
JSScriptRuntime::shallowEq(jsVal_t x, jsVal_t y)
{
    if (JS_VAL_IS_NUMBER(x) && JS_VAL_IS_NUMBER(y)) {
	return JS_VAL_TO_NUMBER(x) == JS_VAL_TO_NUMBER(y);
    }

    if (x == y) return TRUE;		// cache

    int tagX = x != JS_VAL_VOID ? JS_VAL_TAG(x) : -1;
    int tagY = y != JS_VAL_VOID ? JS_VAL_TAG(y) : -1;

    if (tagX != tagY) {
	return FALSE;
    }

    if (tagX == JS_VAL_STRING) {
	return strcmp(JS_VAL_TO_STRING(x), JS_VAL_TO_STRING(y)) == 0;
    }
    if (tagX == JS_VAL_BOOLEAN) {
	return JS_VAL_TO_BOOLEAN(x) == JS_VAL_TO_BOOLEAN(y);
    }
    if (tagX == JS_VAL_OBJECT) {
        if (x == y) {
            return TRUE;
	}
        return FALSE;
    }
    if (x == JS_VAL_VOID) {
        return TRUE;
    }
    return FALSE;
}
jsVal_t
JSScriptRuntime::seqB(jsVal_t x, jsVal_t y)
{
    if (shallowEq(x, y)) {
        return JS_VAL_TRUE;
    } else {
        return JS_VAL_FALSE;
    }
}
jsVal_t
JSScriptRuntime::sneB(jsVal_t x, jsVal_t y)
{
    if (shallowEq(x,y)) {
        return JS_VAL_FALSE;
    } else {
        return JS_VAL_TRUE;
    }
}
/**
 * The instanceof operator.
 *
 * @return a instanceof b
 */
jsBool_t
JSScriptRuntime::instanceOf(JSContext *cx, JSScriptable *scope, jsVal_t a,
			jsVal_t b)
{
    // Check RHS is an object
    if (!JS_VAL_IS_OBJECT(b)) {
        cx->exceptionHandler->throwEx(
                cx,
		JSNativeGlobal::constructError(cx, "TypeError",
			JSScriptRuntime::getMessage(cx,
						"msg.instanceof.not.object"),
			scope));
    }

    // for primitive values on LHS, return false
    // XXX we may want to change this so that
    // 5 instanceof Number == true
    if (!JS_VAL_IS_OBJECT(a)) {
        return FALSE;
    }

    return JS_VAL_TO_OBJECT(b)->hasInstance(cx, JS_VAL_TO_OBJECT(a));
}
/**
 * Delegates to
 *
 * @return true iff rhs appears in lhs' proto chain
 */
jsBool_t
JSScriptRuntime::jsDelegatesTo(JSContext *cx, JSScriptable *lhs,
			JSScriptable *rhs)
{
    JSScriptable *proto = lhs->getPrototype(cx);

    while (proto != NULL) {
        if (proto == rhs) return TRUE;
        proto = proto->getPrototype(cx);
    }

    return FALSE;
}
/**
 * The in operator.
 *
 * This is a new JS 1.3 language feature.  The in operator mirrors
 * the operation of the for .. in construct, and tests whether the
 * rhs has the property given by the lhs.  It is different from the for ..
 * in construct in that:
 * <BR> - it doesn't perform ToObject on the right hand side
 * <BR> - it returns true for DontEnum properties.
 * @param a the left hand operand
 * @param b the right hand operand
 *
 * @return true if property name or element number a is a property of b
 */
jsBool_t
JSScriptRuntime::in(JSContext *cx, jsVal_t a, jsVal_t b, JSScriptable *scope)
{
    if (!JS_VAL_IS_OBJECT(b)) {
        cx->exceptionHandler->throwEx(
                cx,
		JSNativeGlobal::constructError(cx, "TypeError",
			JSScriptRuntime::getMessage(cx,
						"msg.instanceof.not.object"),
			scope));
    }
    // OPT do it here rather than making a new FlattenedObject.
    JSFlattenedObject rhs(JS_VAL_TO_OBJECT(b));

    return rhs.hasProperty(cx, a);
}
jsVal_t
JSScriptRuntime::cmp_LTB(JSContext *cx, jsVal_t val1, jsVal_t val2)
{
    if (cmp_LT(cx, val1, val2) == 1) {
        return JS_VAL_TRUE;
    } else {
        return JS_VAL_FALSE;
    }
}
int
JSScriptRuntime::cmp_LT(JSContext *cx, jsVal_t val1, jsVal_t val2)
{
    if (JS_VAL_IS_OBJECT(val1) && val1 != JS_VAL_NULL) {	// 20030922 ichino
        val1 = JS_VAL_TO_OBJECT(val1)->getDefaultValue(cx, JS_T_Number);
    }
    if (JS_VAL_IS_OBJECT(val2) && val2 != JS_VAL_NULL) {
        val2 = JS_VAL_TO_OBJECT(val2)->getDefaultValue(cx, JS_T_Number);
    }
    if (!JS_VAL_IS_STRING(val1) || !JS_VAL_IS_STRING(val2)) {
        double d1 = toNumber(cx, val1);
#ifdef VX /* Turbot */
        if (isNan( d1 )) {
#else
        if (d1 != d1) {
#endif
            return 0;
	}
        double d2 = toNumber(cx, val2);//JSScriptRuntime::cmp_LT;
#ifdef VX /* Turbot */
        if (isNan( d2 )) {
#else
        if (d2 != d2) {
#endif
            return 0;
	}
        return d1 < d2 ? 1 : 0;
    }
    jsString_t s1 = JS_VAL_TO_STRING(val1);
    jsString_t s2 = JS_VAL_TO_STRING(val2);

    return strcmp(s1, s2) < 0 ? 1 : 0;
}
jsVal_t
JSScriptRuntime::cmp_LEB(JSContext *cx, jsVal_t val1, jsVal_t val2)
{
    if (cmp_LE(cx, val1, val2) == 1) {
        return JS_VAL_TRUE;
    } else {
        return JS_VAL_FALSE;
    }
}
int
JSScriptRuntime::cmp_LE(JSContext *cx, jsVal_t val1, jsVal_t val2)
{
    if (JS_VAL_IS_OBJECT(val1) && val1 != JS_VAL_NULL) {	// 20030922 ichino
        val1 = JS_VAL_TO_OBJECT(val1)->getDefaultValue(cx, JS_T_Number);
    }

    if (JS_VAL_IS_OBJECT(val2) && val2 != JS_VAL_NULL) {
	val2 = JS_VAL_TO_OBJECT(val2)->getDefaultValue(cx, JS_T_Number);

    }

    if (!JS_VAL_IS_STRING(val1) || !JS_VAL_IS_STRING(val2)) {
        double d1 = toNumber(cx, val1);
#ifdef VX /* Turbot */
        if (isNan( d1 )) {
#else
        if (d1 != d1) {
#endif
            return 0;
	}
        double d2 = toNumber(cx, val2);
#ifdef VX /* Turbot */
        if (isNan( d2 )) {
#else
        if (d2 != d2) {
#endif
            return 0;
	}
        return d1 <= d2 ? 1 : 0;
    }
    jsString_t s1 = JS_VAL_TO_STRING(val1);
    jsString_t s2 = JS_VAL_TO_STRING(val2);

    return strcmp(s1, s2) <= 0 ? 1 : 0;
}

// lt:
// implement the '<' operator inline in the caller
// as "compare(val1, val2) == 1"

// le:
// implement the '<=' operator inline in the caller
// as "compare(val2, val1) == 0"

// gt:
// implement the '>' operator inline in the caller
// as "compare(val2, val1) == 1"

// ge:
// implement the '>=' operator inline in the caller
// as "compare(val1, val2) == 0"

// ------------------
// Statements
// ------------------
JSScriptable *
JSScriptRuntime::initScript(JSContext *cx, JSScriptable *scope,
			JSNativeFunction *funObj, JSScriptable *thisObj,
			jsBool_t fromEvalCode)
{
    jsString_t *names = funObj->names;

    if (names != NULL) {
        JSScriptableObject *so;

  	if (scope->isInstanceOf(JSScriptableObject::className)) {
	    so = (JSScriptableObject *) scope;
	} else {
	    so = NULL;
 	}

        JSScriptable *varScope = scope;

        if (fromEvalCode) {
            // When executing an eval() inside a with statement,
            // define any variables resulting from var statements
            // in the first non-with scope. See bug 38590.
            varScope = scope;
            while (varScope->isInstanceOf(JSNativeWith::className)) {
                varScope = varScope->getParentScope(cx);
	    }
        }
        for (int i = funObj->namesLength - 1; i > 0; i--) {
            jsString_t name = funObj->names[i];

            // Don't overwrite existing def if already defined in object
            // or prototypes of object.
            if (!hasProp(cx, scope, name)) {
                if (so != NULL && !fromEvalCode) {
                    so->defineProperty(cx, name, JS_VAL_VOID, JS_PA_Permanent);
                } else  {
                    varScope->put(cx, name, varScope, JS_VAL_VOID);
		}
            }
        }
    }

    if (cx->getDebugLevel() > 0) {
        // need an activation to store debug information.
        new (cx) JSNativeCall(cx, scope, funObj, thisObj);
    }

    return scope;
}
JSScriptable *
JSScriptRuntime::initVarObj(JSContext *cx, JSScriptable *scope,
			JSNativeFunction *funObj, JSScriptable *thisObj,
			int numArgs, jsVal_t *args)
{
    JSNativeCall *result = new (cx) JSNativeCall(cx, scope, funObj, thisObj,
						numArgs, args);
    jsString_t *names = funObj->names;

    cx->pushLocalVal(JS_OBJECT_TO_VAL(result)); // gc safe 20061124

    if (names != NULL) {
        for (int i = funObj->argCount + 1; i < funObj->namesLength; i++) {
            jsString_t name = funObj->names[i];

            result->put(cx, name, result, JS_VAL_VOID);
        }
    }
    cx->popLocalVals(1); // gc safe 20061124

    return result;
}
void
JSScriptRuntime::popActivation(JSContext *cx)
{
    JSNativeCall *current = cx->currentActivation;

    if (current != NULL) {
        cx->currentActivation = current->caller;
        current->caller = NULL;
    }
}
JSScriptable *
JSScriptRuntime::newScope(JSContext *cx)
{
    return new (cx) JSNativeObject();
}
JSScriptable *
JSScriptRuntime::enterWith(JSContext *cx, jsVal_t value, JSScriptable *scope)
{
    return new (cx) JSNativeWith(scope, toObject(cx, scope, value));
}
JSScriptable *
JSScriptRuntime::leaveWith(JSContext *cx, JSScriptable *scope)
{
    return scope->getParentScope(cx);
}
JSNativeFunction *
JSScriptRuntime::initFunction(JSNativeFunction *fn, JSScriptable *scope,
			jsString_t fnName, JSContext *cx)
{
    fn->setPrototype(cx,
		JSScriptableObject::getClassPrototype(cx, scope, "Function"));
    fn->setParentScope(cx, scope);

    if (fnName != NULL && ( strlen(fnName) ) != 0) {
        setName(cx, scope, JS_OBJECT_TO_VAL(fn), scope, fnName);
    }
    return fn;
}
void
JSScriptRuntime::checkDeprecated(JSContext *cx, jsString_t name)
{
    int version = cx->getLanguageVersion();

    if (version >= JSContext::VERSION_1_4
	|| version == JSContext::VERSION_DEFAULT) {
        jsString_t msg = getMessage(cx, "msg.deprec.ctor %s", name);
        if (version == JSContext::VERSION_DEFAULT) {
            JSContext::reportWarning(cx, msg);
        } else {
            cx->exceptionHandler->throwEx(
                    cx,
                    JSContext::reportRuntimeError(cx, msg));
	}
    }
}
jsString_t
JSScriptRuntime::getMessage(JSContext *cx, jsString_t messageId, ...)
{
    va_list ap;

    va_start(ap, messageId);

    jsString_t result = JSContext::getMessageV(cx, messageId, ap);

    va_end(ap);
    return result;
}
JSRegExpProxy *
JSScriptRuntime::getRegExpProxy(JSContext *cx)
{
    return cx->getRegExpProxy();
}
JSNativeCall *
JSScriptRuntime::getCurrentActivation(JSContext *cx)
{
    return cx->currentActivation;
}
void
JSScriptRuntime::setCurrentActivation(JSContext *cx, JSNativeCall *activation)
{
    cx->currentActivation = activation;
}
jsBool_t
JSScriptRuntime::hasProp(JSContext *cx, JSScriptable *start, jsString_t name)
{
    JSScriptable *m = start;

    do {
        if (m->has(cx, name, start)) {
            return TRUE;
	}
        m = m->getPrototype(cx);
    } while (m != NULL);
    return FALSE;
}
JSRuntimeException *
JSScriptRuntime::errorWithClassName(JSContext *cx, jsString_t msg, jsVal_t val)
{
    jsChar_t buf[128];

    sprintf(buf, "%s %%s", msg);
    return JSContext::reportRuntimeError(cx,
				getMessage(cx, buf, toString(cx, val)));
}
jsVal_t
JSScriptRuntime::toValue(JSContext *cx, int i)
{
    if (JS_INT_FITS_IN_VAL(i)) {
	return JS_INT_TO_VAL(i);
    }
    return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, (double) i));

}
jsVal_t
JSScriptRuntime::toValue(JSContext *cx, double d)
{
    if (finite(d) && !JSFpClass::isnegzero(d) && d == (double) ((int) d)) {
	return toValue(cx, (int) d);
    }
    return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, (double) d));
}
jsVal_t
JSScriptRuntime::NaNobj(JSContext *cx)
{
    return cx->runtime->NaNobj;
}


#ifdef VX /* Turbot */
void
JSScriptRuntime::classInitialize()
{
    NaN = JS_NaN;
}
#endif
