/*
 * 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 <math.h>
#include <ctype.h>
#include <vxWorks.h>
#include <private/mathP.h>
#include <pthread.h>
#include "js-interpreter.h"
#include "js-parser.h"
#include "js-scriptruntime.h"
#include "js-exceptionhandler.h"
#include "js-defaulterrorreporter.h"
#include "js-nativearray.h"
#include "js-nativeboolean.h"
#include "js-nativecall.h"
#include "js-nativedate.h"
#include "js-nativeerror.h"
#include "js-nativeglobal.h"
#include "js-nativemath.h"
#include "js-nativenumber.h"
#include "js-nativeobject.h"
#include "js-nativescript.h"
#include "js-nativestring.h"
#include "js-nativewith.h"
#include "js-nativedebug.h"
#include "js-stringreader.h"
#include "js-script.h"
#include "js-propertyexception.h"
#include "js-notafunctionexception.h"
#include "js-frame.h"
#include "js-variabletable.h"
#include "js-utils.h"
#ifdef REGEXP
#include "js-regexpimpl.h"
#include "js-nativeregexp.h"
#endif
#ifdef PERSIST
# include "js-binaryrandomreader.h"
#endif
#include "js-stringruntime.h"
#include "js-contextp.h"

/* Class data */
#if 0
#if defined(VX) && !defined(TARGET_OS_LINUX)
int		JSContext::current;
#else
pthread_key_t	JSContext::key;
#endif
#else
pthread_key_t   JSContext::key;
#endif
int		JSContext::debugLevel = 0;

extern int ThrowCalltimes;


/* Procedures */
JSNativeFunction *
JSContext::compile(JSScriptable *scope, JSTokenStream *ts,
		jsBool_t returnFunction)
{
    JSInterpreter *compiler = new (this) JSInterpreter();

    addInterpreter(compiler);

    errorCount = 0;
    //ThrowCalltimes = 0;

    JSIRFactory *irf = compiler->createIRFactory(this, ts, scope);

    JSParser *p = new (this) JSParser(irf);
    JSNode *tree = p->parse(this, ts);

    if (tree == NULL) {
	p->destroy(this);
	irf->destroy(this);
	delInterpreter(compiler);
	compiler->destroy(this);
	return NULL;
    }

    JSNode *treeTop = tree = compiler->transform(this, tree, ts, scope);
    JSNativeFunction *result = NULL;

    if (printTrees) {
	jsString_t s = tree->toStringTree(this);

	printf("%s\n", s);
	JS_DeleteZoneObj(this, s);
    }
     
    if (returnFunction) {
	JSNode *first = tree->getFirstChild();

	if (first == NULL) goto Err;

	if (!first->getProp(JSNode::FUNCTION_PROP, &tree)) {
	    goto Err;
	}
    }	  
 
    result = compiler->compile(this, scope, tree);
    treeTop->release(this);
    p->destroy(this);
    irf->destroy(this);
    delInterpreter(compiler);
    compiler->destroy(this);

    return errorCount == 0 ? result : NULL;

Err:
    treeTop->destroy(this);
    p->destroy(this);
    irf->destroy(this);
    delInterpreter(compiler);
    compiler->destroy(this);
    return NULL;
}
JSNativeFunction *
JSContext::compile(JSScriptable *scope, JSReader *in, jsString_t sourceName,
		int lineno, jsBool_t returnFunction)
{
    JSTokenStream *ts
		= new (this) JSTokenStream(this, in, scope, sourceName, lineno);
    JSNativeFunction *result = compile(scope, ts, returnFunction);

    ts->destroy(this);
    return result;
}
void
JSContext::newArrayHelper(JSScriptable *scope, JSScriptable *array)
{
    array->setParentScope(this, scope);
    jsVal_t ctor;
    jsBool_t result
		= JSScriptRuntime::getTopLevelProp(this, scope, "Array", &ctor);

    if (result) {
	if (ctor != JS_VAL_NULL && JS_VAL_IS_OBJECT(ctor)) {
	    JSScriptable *s = JS_VAL_TO_OBJECT(ctor);
	    jsVal_t	val;

	    s->get(this, "prototype", s, &val);
	    array->setPrototype(this, JS_VAL_TO_OBJECT(val));
	}
    }
}
void
JSContext::startEnter()
{
    zone = new (runtime) JSZone(runtime);
    lstInit(&frames);
    lstInit(&interpreters);
    lstInit(&variableTables);
 
    localVals = new (this) JSValStack(&allocator);

    currentActivation = NULL;
    iterating = NULL;
    ctorScope = NULL;

    interpreterSourceFile = NULL;
    interpreterLine  = 0;

    exceptionHandler = new (this) JSExceptionHandler(this);

    runtime->enter();
}
void
JSContext::finishExit()
{
    runtime->exit();
    zone->destroy(this->runtime);
}

JSContext *
JSContext::enter(JSContext *cx)
{
#if 0
#if defined(VX) && !defined(TARGET_OS_LINUX)
    if (taskVarGet( 0, &JSContext::current) == ERROR) {
	JSContext::current = 0;
	taskVarAdd( 0, &JSContext::current);
    }
    JSContext *current = (JSContext *) JSContext::current;
#else
    JSContext *current = (JSContext *) pthread_getspecific(key);
#endif
#else
    JSContext *current = (JSContext *) pthread_getspecific(key);
#endif

    if (current != NULL) { 
	if (current->enterCount == 0) {
	    current->startEnter();
	}
	current->enterCount++;
	return current;
    }
    if (cx != NULL) {
	pthread_setspecific(key, cx);
	if (cx->currentThread == 0) {
		cx->currentThread = pthread_self();
	    if (cx->enterCount == 0) {
		cx->startEnter();
	    }
	    cx->enterCount++;
	}
	return cx;
    }

    return NULL;
}
void
JSContext::exit()
{
    JSContext *cx = (JSContext *) getCurrentContext();

    if (cx != NULL) {
	if (--cx->enterCount == 0) {
	    cx->finishExit();
	    pthread_setspecific(key, NULL);
	    cx->currentThread = 0;
	}    
    }
}
JSContext *
JSContext::getCurrentContext()
{
    return (JSContext *) pthread_getspecific(key);
}
void
JSContext::reportWarning(JSContext *cx, jsString_t message,
			jsString_t sourceName, int lineno,
			jsString_t lineSource, int lineOffset)
{
    cx->getErrorReporter()->warning(cx, message, sourceName, lineno,
				lineSource, lineOffset);
}
void
JSContext::reportWarning(JSContext *cx, jsString_t message)
{
    int linep;
    jsString_t lineSource;
    jsString_t filename = getSourcePosition(cx, &linep, &lineSource);

    reportWarning(cx, message, filename, linep, lineSource, 0);
    if ( lineSource != NULL ) JS_DeleteZoneObj( cx, lineSource );
}
void
JSContext::reportError(JSContext *cx, jsString_t message,
		jsString_t sourceName, int lineno, jsString_t lineSource,
		int lineOffset)
{
    cx->errorCount++;
    cx->getErrorReporter()->error(cx, message, sourceName, lineno, lineSource,
				lineOffset);
}
void
JSContext::reportError(JSContext *cx, jsString_t message)
{
    int linep;
    jsString_t lineSource;
    jsString_t filename = getSourcePosition(cx, &linep, &lineSource);

    reportError(cx, message, filename, linep, lineSource, 0);
    if ( lineSource != NULL ) JS_DeleteZoneObj( cx, lineSource );
}
JSEvaluatorException *
JSContext::reportRuntimeError(JSContext *cx, jsString_t message,
			jsString_t sourceName, int lineno,
			jsString_t lineSource, int lineOffset)
{
    cx->errorCount++;
    return cx->getErrorReporter()->runtimeError(cx, message, sourceName,
					lineno, lineSource, lineOffset);
}
JSEvaluatorException *
JSContext::reportRuntimeError(JSContext *cx, jsString_t message)
{
    int linep;
    jsString_t lineSource;
    jsString_t filename = getSourcePosition(cx, &linep, &lineSource);
    JSEvaluatorException* ex = reportRuntimeError(cx, message, filename,
                                        linep, lineSource, 0);

    if ( lineSource != NULL ) JS_DeleteZoneObj( cx, lineSource );
    return ex;
}
jsVal_t
JSContext::getUndefinedValue()
{
    return JS_VAL_VOID;
}
jsString_t
JSContext::getMessage(JSContext *cx, jsString_t messageId, ...)
{
    va_list ap;

    va_start(ap, messageId);
    jsString_t message = getMessageV(cx, messageId, ap);
    va_end(ap);
    return message;
}
jsString_t
JSContext::getMessageV(JSContext *cx, jsString_t messageId, va_list ap)
{
#if defined(VX) && !defined(TARGET_OS_LINUX)
    jsChar_t buf[128];

    vsprintf(buf, messageId, ap);
    return JS_NewZoneString(cx, buf);
#else
    jsString_t buf;
    int n = 128;
    int result;
    
    while (TRUE) {
	buf = (jsString_t) JS_NewZoneObj(cx, sizeof (*buf) * (n + 1));

	result = vsnprintf(buf, n, messageId, ap);
	if (result <= n) break;

	JS_DeleteZoneObj(cx, buf);
	n = result;
    }
    return buf;
#endif
}

/* JSContext::getSourcePosition is changed by ichino 2003.11.19
 * if we want to getSourcePosition with a line Source, we must argment "char *buf"
 * if we want to getSourcePosition without a line Source, we must argment "char *buf"
 */
jsString_t
JSContext::getSourcePosition(JSContext *cx, int *linep)
{
// get source Position whithout Line Source.
	if (cx->interpreterLine > 0 && cx->interpreterSourceFile != NULL) {
	    *linep = cx->interpreterLine;
	    return cx->interpreterSourceFile;
	}
	*linep = 0;
	return NULL;
}

jsString_t
JSContext::getSourcePosition(JSContext *cx, int *linep, jsString_t* buf)
{
   if (
        cx->sourceName != NULL && cx->interpreterSourceFile != NULL
        &&
        strcmp( cx->sourceName, cx->interpreterSourceFile ) == 0
        &&
        cx->interpreterLine > 0
    ) {
        *linep = cx->interpreterLine;

        if (cx->source != NULL) {
            // get present Source and lineNo
            jsString_t source = cx->source;
            int lineno = cx->interpreterLine - cx->lineno;

            for ( int i = 0; i < lineno; i++ ) {
                jsString_t s = strstr( source, "\n" );

                if( s == NULL ) {
                    *buf = NULL;
                    return cx->interpreterSourceFile;
                }
                source = s + 1;
            }
            size_t length = strcspn( source, "\n" );

            if( length > 0 ){
                jsString_t string
                        = (jsString_t) JS_NewZoneObj(cx,
                                        ( length + 1 ) * sizeof (*string) );

                strncpy(string, source, length);
                string[ length ] = '\0';

                // get to line source is complete.
                for ( int i = 0; i < ( int ) length; ) {
                    int n = JSStringRuntime::mblen( string + i, length - i );

                    if ( n == 1 ) {
                        int c = string[ i ] & 0xff;

                        if ( c < 0x20 || c > 0x7f ) {
                            string[ i ] = '.';
                        }
                    } else {
                        for ( int j = 0; j < n; ++j ) {
                            string[ i + j ] = '.';
                        }
                    }
                    i += n;
                }
                *buf = (jsString_t) string;
                return cx->interpreterSourceFile;
            }
        }
    }
    *buf = NULL;
    return getSourcePosition( cx, linep );
}

int
JSContext::getLanguageVersion()
{
    return version;
}
void
JSContext::setLanguageVersion(int version)
{
    this->version = version;
}
JSErrorReporter *
JSContext::getErrorReporter()
{
    if (errorReporter == NULL) {
	errorReporter = new (runtime) JSDefaultErrorReporter();
    }
    return errorReporter;
}
JSErrorReporter *
JSContext::setErrorReporter(JSErrorReporter *reporter)
{
    JSErrorReporter *result = errorReporter;

    this->errorReporter = reporter;
    return result;
}
/**
 * Initialize the standard objects.
 *   
 * Creates instances of the standard objects and their constructors
 * (Object, String, Number, Date, etc.), setting up 'scope' to act
 * as a global object as in ECMA 15.1.
 *   
 * This method must be called to initialize a scope before scripts
 * can be evaluated in that scope.
 *   
 */  
JSScriptable *
JSContext::initStandardObjects(JSScriptableObject *scope)
{
    JSException *ex;

    if ((ex = enterTry(this)) == NULL) {
	if (scope == NULL) {
	    scope = new (this) JSNativeObject();
 	}
	JSNativeFunction::defineClass(this, scope);
	JSNativeObject::defineClass(this, scope);

	JSScriptable *objectProto
		= JSScriptableObject::getObjectPrototype(this, scope);

	// Function.prototype.__proto__ should be Object.prototype
	JSScriptable *functionProto
		= JSScriptableObject:: getFunctionPrototype(this, scope);

	functionProto->setPrototype(this, objectProto);
 
	// Set the prototype of the object passed in if need be
	if (scope->getPrototype(this) == NULL) {
	    scope->setPrototype(this, objectProto);
	}
	
	// must precede NativeGlobal since it's needed therein
	JSNativeError::defineClass(this, scope);
	JSNativeGlobal::defineClass(this, scope);

	static  void (*classes[])(JSContext *cx, JSScriptable *scope) = {
		&JSNativeArray::defineClass,
		&JSNativeString::defineClass,
		&JSNativeBoolean::defineClass,
		&JSNativeNumber::defineClass,
		&JSNativeDate::defineClass,
		&JSNativeMath::defineClass,
		&JSNativeCall::defineClass,
		&JSNativeWith::defineClass,
#ifdef REGEXP
		&JSNativeRegExp::defineClass,
#endif
		&JSNativeScript::defineClass,
	} ;

	for (int i = 0; i < (int) TableSize(classes); ++i) {
	    (*classes[i])(this, scope);
	}    
#ifdef NATIVE_DEBUG
	JSNativeDebug::defineClass(this, scope);
#endif
	exceptionHandler->exitTry();
    } else {
	exceptionHandler->throwEx(this, ex);
    }
    return scope;
}
jsVal_t
JSContext::evaluateString(JSScriptable *scope, jsString_t source,
			jsString_t sourceName, int lineno)
{
    JSStringReader in(source);
    JSException *ex;

    setSourceInfo( source, sourceName, lineno);

    if ((ex = enterTry(this)) == NULL) {
	jsVal_t result = evaluateReader(scope, &in, sourceName, lineno);

	exceptionHandler->exitTry();
	clearSourceInfo();
	return result;
    }
    clearSourceInfo();
    return JS_VAL_NULL;
}
#ifdef JSTIME_TRACE
#include "ewb-trace.h"
#endif
jsVal_t
JSContext::evaluateReader(JSScriptable *scope, JSReader *in,
			jsString_t sourceName, int lineno)
{
    JSScript *script = compileReader(scope, in, sourceName, lineno);

#ifndef SHELL
#ifdef JSTIME_TRACE
    EWBTIME("evaluateReader", NULL);
#endif
#endif
    if (script != NULL) {
	pushLocalVal(JS_OBJECT_TO_VAL((JSNativeScript *) script));

	jsVal_t result = script->exec(this, scope);

	popLocalVals(1); // if cause exeception, we can't pop without try catch
	return result;
    } else {
	return JS_VAL_NULL;
    }
}
jsBool_t
JSContext::stringIsCompilableUnit(jsString_t source)
{
    JSStringReader in(source);
    // no source name or source text manager, because we're just
    // going to throw away the result.
    JSTokenStream *ts = new (this) JSTokenStream(this, &in, NULL, NULL, 1);

    // Temporarily set error reporter to always be the exception-throwing
    // DefaultErrorReporter.  (This is why the method is synchronized...)
    JSErrorReporter *reporter = new (runtime) JSDefaultErrorReporter();
    JSErrorReporter *currentReporter = setErrorReporter(reporter);

    jsBool_t errorseen = FALSE;
    JSException *ex;

    if ((ex = enterTry(this)) == NULL) {
        JSIRFactory *irf = new (this) JSIRFactory(ts, NULL);
        JSParser *p = new (this) JSParser(irf);
        JSNode *tree = p->parse(this, ts);

	if (tree != NULL) {
            tree->release(this);
	}
	p->destroy(this);
	irf->destroy(this);
	exceptionHandler->exitTry();
    } else {
	errorseen = TRUE;
    }
    // Restore the old error reporter.
    setErrorReporter(currentReporter);
    reporter->destroy(runtime);

    // Return false only if an error occurred as a result of reading past
    // the end of the file, i.e. if the source could be fixed by
    // appending more source.
    if (errorseen && ts->eof()) {
	ts->destroy(this);
        return FALSE;
    } else {
	ts->destroy(this);
        return TRUE;
    }
}
JSScript *
JSContext::compileReader(JSScriptable *scope, JSReader *in,
			jsString_t sourceName, int lineno)
{
    return (JSNativeScript *) compile(scope, in, sourceName, lineno, FALSE);
}
JSFunction *
JSContext::compileFunction(JSScriptable *scope, jsString_t source,
			jsString_t sourceName, int lineno)
{
    JSStringReader in(source);
    JSException *ex;

    if ((ex = enterTry(this)) == NULL) {
	JSFunction *result
		= (JSFunction *) compile(scope, &in, sourceName, lineno, TRUE);
	exceptionHandler->exitTry();
	return result;
    } else {
	exceptionHandler->throwEx(this, new (this) JSRuntimeException());
	/* NOT REACHED */
    }
    return NULL;
}
jsString_t
JSContext::decompileScript(JSScript *script, JSScriptable *scope, int indent)
{
    JSNativeScript *ns = (JSNativeScript *) script;

    ns->initScript(this, scope);
    return ns->decompile(this, indent, TRUE, FALSE);
}
jsString_t
JSContext::decompileFunction(JSFunction *fun, int indent)
{
    return ((JSNativeFunction *) fun)->decompile(this, indent, TRUE, FALSE);
}
jsString_t
JSContext::decompileFunctionBody(JSFunction *fun, int indent)
{
    return ((JSNativeFunction *) fun)->decompile(this, indent, TRUE, TRUE);
}
JSScriptable *
JSContext::newObject(JSScriptable *scope)
{
     return newObject(scope, "Object", 0, NULL);
}
JSScriptable *
JSContext::newObject(JSScriptable *scope, jsString_t constructorName)
{
    return newObject(scope, constructorName, 0, NULL);
}
JSScriptable *
JSContext::newObject(JSScriptable *scope, jsString_t constructorName,
		int numArgs, jsVal_t *args)
{
    jsVal_t ctorVal;
    jsBool_t result
		= JSScriptRuntime::getTopLevelProp(this, scope,
						constructorName, &ctorVal);

    if (!result) {
	jsString_t message
		= getMessage(this, "msg.ctor.not.found %s", constructorName);

	exceptionHandler->throwEx(this, new (this) JSPropertyException(message));
    }
    if (!JS_VAL_IS_OBJECT(ctorVal)
	|| !JS_VAL_TO_OBJECT(ctorVal)
			->isInstanceOf(JSNativeFunction::className)) {
	jsString_t message = getMessage(this, "msg.not.ctor", constructorName);

	exceptionHandler->throwEx(this, new (this) JSNotAFunctionException(message));
    }
    JSFunction *ctor = (JSNativeFunction *) JS_VAL_TO_OBJECT(ctorVal);

    return ctor->construct(this, JS_VAL_TO_OBJECT(ctorVal)
					->getParentScope(this), numArgs, args);
}
JSScriptable *
JSContext::newArray(JSScriptable *scope, int length)
{
    JSScriptable *result = new (this) JSNativeArray(this, length);

    newArrayHelper(scope, result);
    return result;
}
JSScriptable *
JSContext::newArray(JSScriptable *scope, jsVal_t *elements, int elementsLength)
{
    JSScriptable *result
		= new (this) JSNativeArray(this, elements, elementsLength);

    newArrayHelper(scope, result);
    return result;
}
jsVal_t *
JSContext::getElements(JSScriptable *object, int *elementsLength)
{
    double doubleLen = JSNativeArray::getLengthProperty(this, object);

#if defined(VX) && !defined(TARGET_OS_LINUX)
    if (isNan(doubleLen)) {
#else
    if (isnan(doubleLen)) {
#endif
	return NULL;
    }
    int len = (int) doubleLen;

    jsVal_t *result = (jsVal_t *) JS_NewZoneObj(this, sizeof (*result) * len);
    for (int i = 0; i < len; i++) {
	jsVal_t elem;
	jsBool_t ok = object->get(this, i, object, &elem);

	result[i] = (!ok) ? JS_VAL_VOID : elem;
    }
    return result;
}
/**
  * Convert the value to a JavaScript boolean value.
  * See ECMA 9.2.
  *   
  */  
jsBool_t
JSContext::toBoolean(jsVal_t value)
{
    return JSScriptRuntime::toBoolean(this, value);
}
/**
 * Convert the value to a JavaScript Number value.
 * Returns a Java double for the JavaScript Number.
 * See ECMA 9.3.
 */
double
JSContext::toNumber(jsVal_t value)
{
    return JSScriptRuntime::toNumber(this, value);
}
/**
 * Convert the value to a JavaScript String value.
 * See ECMA 9.8.
 */
jsString_t
JSContext::toString(jsVal_t value)
{
    return JSScriptRuntime::toString(this, value);
}
/**
 * Convert the value to an JavaScript object value.
 * Note that a scope must be provided to look up the constructors
 * for Number, Boolean, and String.
 * See ECMA 9.9.
 */
JSScriptable *
JSContext::toObject(jsVal_t value, JSScriptable *scope)
{
    return JSScriptRuntime::toObject(this, scope, value);
}
jsVal_t
JSContext::toValue(jsBool_t b)
{
    return JS_BOOLEAN_TO_VAL(b);
}
jsVal_t
JSContext::toValue(int i)
{
    return JSScriptRuntime::toValue(this, i);
}
jsVal_t
JSContext::toValue(double d)
{
    return JSScriptRuntime::toValue(this, d);
}
jsVal_t
JSContext::toValue(jsString_t s)
{
    return JS_STRING_TO_VAL(JS_NewGCString(this, s));
}
int
JSContext::getOptimizationLevel()
{
    return optimizationLevel;
}
void
JSContext::setOptimizationLevel(int optimizationLevel)
{
    if (optimizationLevel < 0) {
	optimizationLevel = -1;
    } else if (optimizationLevel > 9) {
	optimizationLevel = 9;
    }
    this->optimizationLevel = optimizationLevel;
}
jsBool_t
JSContext::hasCompileFunctionsWithDynamicScope()
{
    return compileFunctionsWithDynamicScopeFlag;
}
void
JSContext::setCompileFunctionsWithDynamicScope(jsBool_t flag)
{
    compileFunctionsWithDynamicScopeFlag = flag;
}
int
JSContext::getDebugLevel()
{
    return debugLevel;
}
int
JSContext::setDebugLevel(int debugLevel)
{
    int result = this->debugLevel;

    if (debugLevel < 0) {
	debugLevel = 0;
    } else if (debugLevel > 9) {
	debugLevel = 9;
    }
    if (debugLevel > 0) {
	setOptimizationLevel(0);
    }
    return result;
}
JSRegExpProxy *
JSContext::getRegExpProxy()
{
#ifdef REGEXP
    if (regExpProxy == NULL) {
	regExpProxy = new (this) JSRegExpImpl(this);
    }
#endif
    return regExpProxy; // always NULL without REGEXP
}
jsBool_t
JSContext::isVersionECMA1()
{
    return version == VERSION_DEFAULT || version >= VERSION_1_3;
}
#ifdef BRANCH
void
JSContext::setBranchCallback(jsBranchProc_t branch)
{
    this->branch = branch;
}
#endif
void *
JSContext::getClosure()
{
     return closure;
}
void
JSContext::setClosure(void *closure)
{
    this->closure = closure;
}
JSFrame *
JSContext::pushFrame(JSScriptable *scope, JSScriptable *thisObj,
		int numArgs, jsVal_t *args, JSInterpreterData *theData)
{
    JSFrame *frame = new (this) JSFrame(this, scope, thisObj, numArgs, args,
				theData);

    lstAdd(&frames, frame);
#if 1 /* YTanaka */
    if ( frames.count /* lstCount( &frames ) */ > 127 ) {
        printf( "[EWB]JSContext::pushFrame stack will be overflow soon\n");
    }
#endif
    return frame;
}
void
JSContext::popFrame()
{
    JSFrame *frame;

    frame = (JSFrame *) lstLast(&frames);
    lstDelete(&frames, frame);
    frame->destroy(this);
}
void
JSContext::pushLocalVals(int n)
{
    localVals->pushN(n);
}
void
JSContext::pushLocalVal(jsVal_t val)
{
    localVals->push(val);
}
jsVal_t *
JSContext::getLocalVals(int n)
{
    return localVals->top() - n;
}
void
JSContext::popLocalVals(int n)
{
    localVals->popN(n);
}
void
JSContext::addInterpreter(JSInterpreter *interpreter)
{
    ThrowCalltimes = 0;
    JSTagNode *tag = new (this) JSTagNode(interpreter);

    lstAdd(&interpreters, tag);
}
void
JSContext::delInterpreter(JSInterpreter *interpreter)
{
    for (NODE *node = lstFirst(&interpreters);
	node != NULL; node = lstNext(node)) {
	JSTagNode *tag = (JSTagNode *) node;

	if (tag->data == interpreter) {
	    lstDelete(&interpreters, tag);
	    tag->destroy(this);
	    break;
	}
    }
}
#if 0
/* 20040416 ichino */
void
JSContext::showIcodeSize()
{
#ifndef VX

  int size;

    JSTagNode *tag;
    JSInterpreter *interpreter;
    tag = (JSTagNode*) lstFirst(&interpreters);
    printf("lstCount(&interpreters):%d\n",lstCount(&interpreters));
    printf("-------\n");
    for(;tag != NULL;){
        interpreter =(JSInterpreter*)tag->data;
	interpreter->getIcodeMemSize();
        //printf("interpreter->getIcodeMemSize():%d\n",interpreter->getIcodeMemSize());
	tag = (JSTagNode*) lstNext((NODE*)tag); 
	size += interpreter->getIcodeMemSize();
        printf("interpreter->getIcodeMemSize():%d\n",size);
    }


#endif
}
#endif

void
JSContext::addVariableTable(JSVariableTable *variableTable)
{
    JSTagNode *tag = new (this) JSTagNode(variableTable);

    lstAdd(&variableTables, tag);
}
void
JSContext::delVariableTable(JSVariableTable *variableTable)
{
    for (NODE *node = lstFirst(&variableTables);
	node != NULL; node = lstNext(node)) {
	JSTagNode *tag = (JSTagNode *) node;

	if (tag->data == variableTable) {
	    lstDelete(&variableTables, tag);
	    tag->destroy(this);
	    break;
	}
    }
}
void
JSContext::addNewBorn(void *newBorn)
{
    newBorns[newBornsIndex] = newBorn;
    newBornsIndex += 1;
    if (newBornsIndex >= (int) TableSize(newBorns)) {
	newBornsIndex = 0;
    }
}
void    
JSContext::setSourceInfo(jsString_t source, jsString_t sourceName, int lineno)
{
    this->source = source;
    if ( sourceName != NULL ) {
        this->sourceName = sourceName;
    }
    this->lineno = lineno;
}
void
JSContext::clearSourceInfo()
{
    source = NULL;
    sourceName = NULL;
}

void
JSContext::mark()
{
    if (enterCount > 0) {
	NODE *node; 	// avoid compile error on vxWorks 

	for (node = lstFirst(&frames);
	    node != NULL; node = lstNext(node)) {
	    JSFrame *frame = (JSFrame *) node;

	    frame->mark(this);
	}
	for (node = lstFirst(&interpreters);
	    node != NULL; node = lstNext(node)) {
	    JSTagNode *tag = (JSTagNode *) node;
	    JSInterpreter *interpreter = (JSInterpreter *) tag->data;

	    interpreter->mark(this);
	}
	for (node = lstFirst(&variableTables);
	    node != NULL; node = lstNext(node)) {
	    JSTagNode *tag = (JSTagNode *) node;
	    JSVariableTable *variableTable = (JSVariableTable *) tag->data;

	    variableTable->mark(this);
	}
  	int size = localVals->size();
	int i; // avoid compile error on vxWorks

 	for (i = 0; i < size; ++i) {
	    JS_MarkVal(this, localVals->elementAt(i));
	}
 	for (i = 0; i < (int) TableSize(newBorns); ++i) {
	    void *obj = newBorns[i];

	    if (obj != NULL) {
		JSGCHeap *gcHeap = &runtime->gcHeap;
		int kind = gcHeap->getObjKind(obj);

		if (kind == JSGCMemory::PTRFREE) {
		    gcHeap->markObj(obj);
		} else {
		    JSGCNew *gcObj = (JSGCNew *) obj;

		    gcObj->mark(this);
		}
	    }
	}
	if (currentActivation != NULL) {
	    currentActivation->mark(this);
	}
	if (interpreterSourceFile != NULL) {
	    JSGCHeap *gcHeap = &runtime->gcHeap;

	    gcHeap->markObj(interpreterSourceFile);
	}
    }
    if (outOfMemory != NULL) {
	outOfMemory->mark(this);
    }
#ifdef REGEXP
    if (regExpProxy != NULL) {
	((JSRegExpImpl *) regExpProxy)->mark(this);
    }
#endif
}
#ifdef PERSIST
int
JSContext::saveWriter( JSScriptable *scope, JSScript* script,
		JSRandomWriter* out )
{
    JSInterpretedScript *is = ( JSInterpretedScript* ) script;
    JSException *ex;
    
    pushLocalVal(JS_OBJECT_TO_VAL( is ) );

    if ((ex = enterTry(this)) == NULL) {
	JSPersist::StringConstants stringConstants( this );
	JSPersist::NumberConstants numberConstants( this );

	is->presave( this, scope, stringConstants, numberConstants );

	jsBFHeader_t header;
	int n;

	memset( &header, 0, sizeof ( header ) );
#if 1	/*	AR164852	/	Mantis:0643675	*/
	memcpy(( char* ) header.j_ident,JBFMAG,SJBFMAG);
#else
	strcpy( ( char* ) header.j_ident, JBFMAG );
#endif	/*	AR164852	/	Mantis:0643675	*/
	header.j_version = JSPersist::normalize( ( uint32_t ) 0x00000000 );
	n = out->write( &header, 0, sizeof ( header ) );
	if ( n < ( int ) sizeof ( header ) ) {
	    printf( "can't write header\n" );
	    popLocalVals(1);
	    return -1;
	}
	size_t size = stringConstants.hashtable.size();
	uint16_t count16 = JSPersist::normalize( ( uint16_t ) size );

	n = out->write( &count16, 0, sizeof ( count16 ) );
	if ( n < ( int ) sizeof ( count16 ) ) {
	    printf( "can't write string constants count\n" );
	    popLocalVals(1);
	    return -1;
	}
	uint32_t size32 = 0;

	if ( size != 0 ) {
	    JSPersist::StringHashtable::HashtableEnumerator enumerator
		    = stringConstants.hashtable.entries();
	    jsString_t *strings = ( jsString_t* ) JS_NewZoneObj( this,
			    sizeof ( *strings ) * size );

	    while ( enumerator.hasMoreElements() ) {
		JSPersist::StringHashtable::HashtableEntry* tabEntry
			= enumerator.nextElement();

		strings[ tabEntry->value - 1 ] = tabEntry->key;
		size32 += sizeof ( *tabEntry->key )
			* ( strlen( tabEntry->key ) + 1 );
	    }
	    size32 = JSPersist::normalize( size32 );
	    n = out->write( &size32, 0, sizeof ( size32 ) );
	    if ( n < ( int ) sizeof ( size32 ) ) {
		printf( "can't write string constants size\n" );
		JS_DeleteZoneObj( this, strings );
		popLocalVals(1);
		return -1;
	    }
	    for ( int i = 0; i < ( int ) size; ++i ) {
		jsString_t string = strings[ i ];
		int len = strlen( string );
	
		n = out->write( string, 0, sizeof ( *string ) * ( len + 1 ) );
		if ( n < ( int ) ( sizeof ( *string ) * ( len + 1 ) ) ) {
	 	    printf( "can't write string\n" );
		    JS_DeleteZoneObj( this, strings );
		    popLocalVals(1);
	 	    return -1;
		}
	    }
	    JS_DeleteZoneObj( this, strings );
	}
	size = numberConstants.hashtable.size();
	count16 = JSPersist::normalize( ( uint16_t ) size );
	n = out->write( &count16, 0, sizeof ( count16 ) );
	if ( n < ( int ) sizeof ( count16 ) ) {
	    printf( "can't write number constants count\n" );
	    popLocalVals(1);
	    return -1;
	}
	if ( size != 0 ) {
	    JSPersist::NumberHashtable::HashtableEnumerator enumerator
		    = numberConstants.hashtable.entries();
	    jsString_t *numbers = ( jsString_t* )
		    JS_NewZoneObj( this, sizeof ( *numbers ) * size );

	    size32 = 0;
	    while ( enumerator.hasMoreElements() ) {
		JSPersist::NumberHashtable::HashtableEntry* tabEntry
			= enumerator.nextElement();
		jsString_t number = JSScriptRuntime::numberToString( this,
					tabEntry->key, 10 );

		numbers[ tabEntry->value - 1 ] = number;
		size32 += sizeof ( *number ) * ( strlen( number ) + 1 );
	    }
	    size32 = JSPersist::normalize( size32 );
	    n = out->write( &size32, 0, sizeof ( size32 ) );
	    if ( n < ( int ) sizeof ( size32 ) ) {
		printf( "can't write string constants size\n" );
		JS_DeleteZoneObj( this, numbers );
		popLocalVals(1);
		return -1;
	    }
	    for ( int i = 0; i < ( int ) size; ++i ) {
		jsString_t number = numbers[ i ];
		int len = strlen( number );
	
		n = out->write( number, 0, sizeof ( *number ) * ( len + 1 ) );
		if ( n < ( int ) ( sizeof ( *number ) * ( len + 1 ) ) ) {
	 	    printf( "can't write numbers\n" );
		    JS_DeleteZoneObj( this, numbers );
		    popLocalVals(1);
	 	    return -1;
		}
		JS_DeleteZoneObj( this, number );
	    }
	    JS_DeleteZoneObj( this, numbers );
	}
	count16 = JSPersist::normalize( ( uint16_t ) 1 );
	n = out->write( &count16, 0, sizeof ( count16 ) );
	if ( n < ( int ) sizeof ( count16 ) ) {
	    printf( "can't write scripts count\n" );
	    popLocalVals(1);
	    return -1;
	}
	is->save( this, scope, out, stringConstants, numberConstants );
        exceptionHandler->exitTry();
    	popLocalVals(1);
	return 0;
    }
    popLocalVals(1);
    return -1;
}
JSScript*
JSContext::loadReader( JSScriptable *scope, JSRandomReader* in )
{
    JSScript* script = NULL;
    JSException *ex;
    int count = 0;

    if ((ex = enterTry(this)) == NULL) {
        jsBFHeader_t header;
        int n = in->read( &header, 0, sizeof ( header ) );

        if ( n < ( int ) sizeof ( header ) ) {
	    printf( "can't read header\n" );
	    return NULL;
        }
	header.j_version = JSPersist::localize( header.j_version );
        if ( strncmp( ( char* ) header.j_ident, JBFMAG, SJBFMAG ) != 0 ) {
	    printf( "header ident is different\n" );
	    return NULL;
        }
        jsString_t* strings = NULL;
        uint16_t count16;

        n = in->read( &count16, 0, sizeof ( count16 ) );
        if ( n < ( int ) sizeof ( count16 ) ) {
	    printf( "can't read string constants count\n" );
	    return NULL;
        }
        count16 = JSPersist::localize( count16 );

        if ( count16 != 0 ) {
            uint32_t size32;

            n = in->read( &size32, 0, sizeof ( size32 ) );
            if ( n < ( int ) sizeof ( size32 ) ) {
	        printf( "can't read string constants size\n" );
	        return NULL;
            }
            size32 = JSPersist::localize( size32 );

	    jsString_t string = ( jsString_t ) JS_NewZoneObj( this, size32 );

    	    n = in->read( string, 0, size32 );
	    if ( n < ( int ) size32 ) {
	        printf( "can't read string constants\n" );
	        JS_DeleteZoneObj( this, string );
	        return NULL;
	    }
	    jsString_t s = string;

	    strings = ( jsString_t* ) JS_NewZoneObj( this,
			 sizeof ( *strings ) * count16 );
	    for ( int i = 0; i < count16; ++i ) {
	        strings[ i ] = JS_NewGCString( this, s );

	        pushLocalVal( JS_STRING_TO_VAL( strings[ i ] ) );
	        count += 1;
	        s += strlen( s ) + 1;
	    }
	    JS_DeleteZoneObj( this, string );
        }
        double* numbers = NULL;

        n = in->read( &count16, 0, sizeof ( count16 ) );
        if ( n < ( int ) sizeof ( count16 ) ) {
	    printf( "can't read number constants count\n" );
	    popLocalVals( count );
	    return NULL;
        }
        count16 = JSPersist::localize( count16 );
        if ( count16 != 0 ) {
            uint32_t size32;

            n = in->read( &size32, 0, sizeof ( size32 ) );
            if ( n < ( int ) sizeof ( size32 ) ) {
	        printf( "can't read number constants size\n" );
	        popLocalVals( count );
	        return NULL;
            }
            size32 = JSPersist::localize( size32 );

	    jsString_t number = ( jsString_t ) JS_NewZoneObj( this, size32 );

    	    n = in->read( number, 0, size32 );
	    if ( n < ( int ) size32 ) {
	        printf( "can't read number constants\n" );
	        JS_DeleteZoneObj( this, number );
	        popLocalVals( count );
	        return NULL;
	    }
	    jsString_t s = number;

	    numbers = ( double* ) JS_NewZoneObj( this,
			    sizeof ( *numbers ) * count16 );
	    for ( int i = 0; i < count16; ++i ) {
	        numbers[ i ] = JSScriptRuntime::toNumber( this, s );
	        s += strlen( s ) + 1;
	    }
	    JS_DeleteZoneObj( this, number );
        }
        n = in->read( &count16, 0, sizeof ( count16 ) );
        if ( n < ( int ) sizeof ( count16 ) ) {
	    printf( "can't read scripts constants count\n" );
	    if ( strings != NULL ) JS_DeleteZoneObj( this, strings );
	    if ( numbers != NULL ) JS_DeleteZoneObj( this, numbers );
    	    popLocalVals( count );
	    return NULL;
        }
        count16 = JSPersist::localize( count16 );
        for ( int i = 0; i < count16; ++i ) {
	    JSInterpreterData *theData
		    = new ( this ) JSInterpreterData( this, 0, 0, 0,
				    hasCompileFunctionsWithDynamicScope() );
	    JSVariableTable* varTable = new ( this ) JSVariableTable( this );

	    theData->itsVariableTable = varTable;

	    JSInterpretedScript* is
		    = new ( this ) JSInterpretedScript( theData, this );

	    pushLocalVal( JS_OBJECT_TO_VAL( is ) );
	    count += 1;
	    is->load( this, scope, in, strings, numbers );
	    script = is;
        }
	if ( strings != NULL ) JS_DeleteZoneObj( this, strings );
	if ( numbers != NULL ) JS_DeleteZoneObj( this, numbers );
        exceptionHandler->exitTry();
    }
    popLocalVals( count );
    return script;
}
jsVal_t
JSContext::evaluateBinary( JSScriptable *scope, void* data, size_t size )
{
    JSBinaryRandomReader in( data, size );
    JSScript* script = loadReader( scope, &in );

    if (script != NULL) {
	pushLocalVal(JS_OBJECT_TO_VAL((JSNativeScript *) script));

	jsVal_t result = script->exec(this, scope);

	popLocalVals(1); // if cause exeception, we can't pop without try catch
	return result;
    } else {
	return JS_VAL_NULL;
    }
}
#endif /* PERSIST */
void
JSContext::cleanup()
{
    lstInit(&frames);
    lstInit(&interpreters);
    lstInit(&variableTables);

    localVals->popN( localVals->size() );
    memset(newBorns, 0, sizeof (*newBorns) * TableSize(newBorns));
    currentActivation = NULL;
    interpreterSourceFile = NULL;
    outOfMemory = NULL;
    regExpProxy = NULL;
    runtime->cleanup();
    runtime->gcHeap.gc(this);
}

void
JSContext::destroy(JSRuntime *runtime)
{
    runtime->delContext(this);
    if (errorReporter != NULL) {
	errorReporter->destroy(runtime);
    }
    JSRuntimeNew::destroy(runtime);
}

JSContext::JSContext(JSRuntime *runtime)
		: allocator(this)
{
    this->runtime = runtime;
    setLanguageVersion(VERSION_DEFAULT);
    optimizationLevel = 0;

    errorReporter = NULL;
    currentThread = 0;
    regExpProxy = NULL;
    compileFunctionsWithDynamicScopeFlag = FALSE;
    enterCount = 0; 

#ifdef BRANCH
    branch = NULL;
#endif

    newBornsIndex = 0;
    memset(newBorns, 0, sizeof (*newBorns) * TableSize(newBorns));
 
    runtime->addContext(this);

    outOfMemory = NULL;	// avoid error in GC in new outOfMemory
    outOfMemory = new (this) JSRuntimeException("OutOfMemory");
}
void
JSContext::classInitialize()
{
	/** 9999:20130520 Modified Start(toi) */
#if !defined(VX) || !defined(TARGET_OS_VW)
    pthread_key_create(&key, NULL);
#endif
}

