/*
 * 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>
#ifdef VX
#include <vxWorks.h>
#include <private/mathP.h>
#endif
#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
#include "js-contextp.h"

/* Class data */
pthread_key_t JSContext::key;
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)
{
	JSContext *current = (JSContext *) pthread_getspecific(key);

	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 = NULL;
	jsString_t filename = getSourcePosition(cx, &linep, &lineSource);

	reportWarning(cx, message, filename, linep, lineSource, 0);
}
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 = NULL;
	jsString_t filename = getSourcePosition(cx, &linep, &lineSource);

	reportError(cx, message, filename, linep, lineSource, 0);
}
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 = NULL;
	jsString_t filename = getSourcePosition(cx, &linep, &lineSource);
	return reportRuntimeError(cx, message, filename, linep, lineSource, 0);
}

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)
{
#ifdef VX
	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 !defined CONF_WPAD
	if (cx->interpreterLine > 0 && cx->interpreterSourceFile != NULL) {
		*linep = cx->interpreterLine;
		return cx->interpreterSourceFile;
	}
#endif
	*linep = 0;
	return NULL;
}

jsString_t JSContext::getSourcePosition(JSContext * cx, int *linep, jsString_t * buf)
{
#if defined CONF_WPAD
	if (buf) {
		*buf = NULL;
	}
	*linep = 0;
	return NULL;
#else
#if 1
	if (cx->source != NULL || cx->interpreterSourceFile != NULL) {
		int no_t;
		jsString_t string = NULL;
		*linep = cx->interpreterLine;
		if (cx->source != NULL) {
			//jsString_t string = NULL;
			jsString_t source_t = cx->source;
			size_t sourceFileLen = 0;

			if (cx->interpreterSourceFile != NULL) {
				sourceFileLen = strlen(cx->interpreterSourceFile);
			}
			no_t = cx->interpreterLine - cx->scriptBaseLineno;  //get present Source LineNo
			for (int i = 0; i < no_t; i++) {
				if (source_t == NULL)
					return cx->interpreterSourceFile;
				source_t = strstr(source_t, "\n");
				if (source_t == NULL)
					return cx->interpreterSourceFile;
				source_t++;
			}

			size_t sourceLen = strcspn(source_t, "\n");

#if 1
			if (sourceLen > 0) {
				string = (jsString_t) JS_NewZoneObj(cx, sourceLen + 1 + sizeof(string));
				memset(string, 0, (sourceLen + 1 + sizeof(string)));
				strncpy(string, source_t, sourceLen);
				// get to line source is complete.

				int charInt;    // get to line source filtering.
				int n = (int)strlen(string);
				int j;
				for (j = 0; j < n; j++) {
					charInt = (int)string[j];
					if (charInt < 0x80) {   //is maybe utf-8 1Byte Code
						if ((0x20 <= charInt && charInt <= 0x7E)) {
							continue;
						}
						else {
							string[j] = '.';
						}
					}
					else if (charInt >= 0xE0) { //is first Byte of utf-8 3Byte Code
						string[j] = '.';
						if (j < n) {
							j++;
							string[j] = '.';
						}
						if (j < n) {
							j++;
							string[j] = '.';    //replace 3Bytes charctor
						}
					}
					else if (charInt >= 0xC0) { //is first Byte utf-8 2Byte Code
						string[j] = '.';
						if (j < n) {
							j++;
							string[j] = '.';    //replace 2Bytes charctor
						}
					}
					else {  //is not first Byte of utf-8
						string[j] = '.';
					}
				}
			}
		}
#endif
		*buf = (jsString_t) string;
	}
	return cx->interpreterSourceFile;
	*linep = 0;
#endif
	return NULL;
#endif
}


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);

		if (functionProto != NULL) {
			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(ex);
	}
	return scope;
}

jsVal_t JSContext::evaluateString(JSScriptable * scope, jsString_t source, jsString_t sourceName, int lineno)
{
	JSStringReader in(source);
	JSException *ex;


	//    if (source != NULL) this->source = source; // 2003.10.4 ichino
	//20040531 ichino
	setSourceInfo(source, sourceName, lineno);

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

		exceptionHandler->exitTry();
		return result;
	}
	return JS_VAL_NULL;
}

#ifdef JSTIME_TRACE
/* #include "ewb-trace.h" */
#include "js-trace.h"
#endif
jsVal_t JSContext::evaluateReader(JSScriptable * scope, JSReader * in, jsString_t sourceName, int lineno)
{
	this->scriptBaseLineno = lineno;    // 2003.11.4 ichino
	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(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(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(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);

#ifdef VX
	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);
	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 sourceStr, jsString_t sourceName, int lineno)
{
	if (sourceStr != NULL) {
		source = sourceStr;
	}
	if (sourceName != NULL) {
		interpreterSourceFile = JS_NewGCString(this, sourceName);
	}
	interpreterLine = lineno;
}

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
}

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();
}

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()
{
	pthread_key_create(&key, NULL);
}
