/*
 * 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 "js-objectadapter.h"
#include "js-stringreader.h"
#include "js-scriptruntime.h"
#include "js-evaluatorexception.h"
#include "js-exceptionhandler.h"
#include "js-runtimeexception.h"
#include "js-functionobject.h"
#include "js-nativescript.h"

/* Internal data */
const jsString_t	JSNativeScript::className = "Script";

/* Procedures */
void
JSNativeScript::finishInit(JSContext *cx, JSScriptable *scope,
			JSFunctionObject *ctor, JSScriptable *proto)
{
    class ScriptAdapter: public JSObjectAdapter {
	public:
	    static  jsVal_t compile(JSContext *cx, JSScriptable *thisObj,
				int numArgs, jsVal_t *args,
				JSFunction *funObj) {
                JSScriptable *scriptable;

                scriptable = getScriptable(cx, thisObj,
					JSNativeScript::className, funObj);

                JSNativeScript *_thisObj
                                = (JSNativeScript *) scriptable;
		jsString_t source = JSScriptRuntime::toString(cx, args[0]);
		jsVal_t result = _thisObj->jsFunction_compile(cx, source);

	 	JS_DeleteZoneObj(cx, source);
		return result;
	    }
	    static  jsVal_t exec(JSContext *cx, JSScriptable *thisObj,
				int numArgs, jsVal_t *args,
				JSFunction *funObj) {
                JSScriptable *scriptable;

                scriptable = getScriptable(cx, thisObj,
					JSNativeScript::className, funObj);

                JSNativeScript *_thisObj
                                = (JSNativeScript *) scriptable;

		return _thisObj->jsFunction_exec(cx);
	    }

	    JS_OA_DefineGetter(name, JSNativeScript)
    } ;
    static jsFunction_t	functions[] = {
#define	ItemAdapter(name, clazz, length)	\
			{ #name,  clazz::name, length }
#define	Item(name, length)	{ #name,  jsFunction_##name, length }
	ItemAdapter(compile, ScriptAdapter, 1),
	ItemAdapter(exec, ScriptAdapter, 0),	
	Item(toString, -1),
#undef	Item
#undef	ItemAdapter
    } ;
    static jsProperty_t properties[] = {
#define ItemG(name, clazz)					      \
	{							       \
	    #name, JS_PA_ReadOnly | JS_PA_Permanent | JS_PA_DontEnum , \
	    &clazz::name##_getter, NULL				 \
	}
	ItemG(name, ScriptAdapter)
#undef ItemG
    };

    ((JSScriptableObject *) proto)->defineFunctionProperties(cx, className,
				TableSize(functions), functions,
				JS_PA_DontEnum);
    ((JSScriptableObject *) proto)->defineProperties(cx,
				TableSize(properties), properties); 
}
jsVal_t
JSNativeScript::jsConstructor(JSContext *cx, int numArgs,
			jsVal_t *args, JSFunction *ctorObj, jsBool_t inNewExpr)
{
    jsString_t source = numArgs == 0
			? (jsString_t) ""
			: JSScriptRuntime::toString(cx, args[0]);
    JSScriptable *scope = cx->ctorScope;
    if (scope == NULL) {
	scope = (JSNativeFunction *) ctorObj;
    }
    JSScript *script = compile(cx, scope, source);

    if (numArgs != 0) JS_DeleteZoneObj(cx, source);
    return JS_OBJECT_TO_VAL((JSNativeScript *) script);
}
jsVal_t
JSNativeScript::jsFunction_toString(JSContext *cx, JSScriptable *thisObj,
				int numArgs, jsVal_t *args, JSFunction *funObj)
{
    JSScript *thisScript = ((JSNativeScript *) thisObj)->script;

    if (thisScript == NULL) {
	thisScript = (JSNativeScript *) thisObj;
    }
    JSScriptable *scope = getTopLevelScope(cx, thisObj);

    jsString_t s = cx->decompileScript(thisScript, scope, 0);
    jsVal_t result = JS_STRING_TO_VAL(JS_NewGCString(cx, s));

    JS_DeleteZoneObj(cx, s);
    return result;
}
JSScript *
JSNativeScript::compile(JSContext *cx, JSScriptable *scope, jsString_t source)
{
    JSStringReader reader(source);
    JSException *ex;

    if ((ex = enterTry(cx)) == NULL) {
	int linep;
	jsString_t filename = JSContext::getSourcePosition(cx, &linep);

        if (filename == NULL) {
            filename = "<Script object>";
            linep = 1;
        }
	JSScript *result
		= cx->compileReader(scope, &reader, filename, linep);

	cx->exceptionHandler->exitTry();
	return result;
    } else {
	cx->exceptionHandler->throwEx(
                cx,
                new (cx) JSRuntimeException("Unexpected IOException"));
    }
    /* NOT REACHED */
    return NULL;
}

void
JSNativeScript::defineClass(JSContext *cx, JSScriptable *scope)
{
    JSScriptable *proto = new (cx) JSNativeScript();

    JSScriptableObject::defineClass(cx, scope, proto, jsConstructor,
				finishInit);
}
jsVal_t
JSNativeScript::jsFunction_compile(JSContext *cx, jsString_t source)
{
    script = compile(cx, NULL, source);
    return JS_OBJECT_TO_VAL(this);
}
jsVal_t
JSNativeScript::jsFunction_exec(JSContext *cx)
{
    cx->exceptionHandler->throwEx(
            cx,
            JSContext::reportRuntimeError(cx,
			JSContext::getMessage(cx, "msg.cant.call.indirect %s",
					"exec")));
    /* NOT REACHED */
    return JS_VAL_VOID;
}
jsBool_t
JSNativeScript::jsGet_name(JSContext *cx, jsVal_t *value)
{
    *value = JS_STRING_TO_VAL(JS_NewGCString(cx, ""));
    return TRUE;
}
/**
  * Initialize script.
  *   
  * Does nothing here, but scripts will override with code
  * to initialize contained functions, regexp literals, etc.
  */ 
void
JSNativeScript::initScript(JSContext *cx, JSScriptable *scope)
{
}
jsBool_t
JSNativeScript::isInstanceOf(const jsString_t className)
{
    if (className == JSNativeScript::className) return TRUE;
    return JSNativeFunction::isInstanceOf(className);
}
jsString_t
JSNativeScript::getClassName(JSContext *cx)
{
    return JSNativeScript::className;
}
jsVal_t
JSNativeScript::exec(JSContext *cx, JSScriptable *scope)
{
    return script == NULL ? JS_VAL_VOID : script->exec(cx, scope);
}
jsVal_t
JSNativeScript::call(JSContext *cx, JSScriptable *scope, JSScriptable *thisObj,
			int numArgs, jsVal_t *args)
{
    return exec(cx, scope);
}
JSScriptable *
JSNativeScript::construct(JSContext *cx, JSScriptable *scope, int numArgs,
			jsVal_t *args)
{
    jsString_t message
		= JSContext::getMessage(cx, "msg.script.is.not.constructor");
    cx->exceptionHandler->throwEx(cx, JSContext::reportRuntimeError(cx, message));
    /* NOT REACHED */
    return NULL;
}
void
JSNativeScript::mark(JSContext *cx)
{
    JSGCHeap *gcHeap = &cx->runtime->gcHeap;

    if (gcHeap->isMarkObj(this)) return;

    JSScriptableObject::mark(cx);
    if (script != NULL) {
	((JSNativeScript *) script)->mark(cx);
    }
}
JSNativeScript::JSNativeScript()
{
    script = NULL;
}

