/*
 * 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-scriptruntime.h"
#include "js-arguments.h"
#include "js-evaluatorexception.h"
#include "js-exceptionhandler.h"
#include "js-nativecall.h"

/* Internal data */
const jsString_t JSNativeCall::className = "Call";

/* Procedures */
jsVal_t JSNativeCall::jsConstructor(JSContext * cx, int numArgs, jsVal_t * args, JSFunction * ctorObj, jsBool_t inNewExpr)
{
    if (!inNewExpr) {
        cx->exceptionHandler->throwEx(JSContext::reportRuntimeError(cx, JSContext::getMessage(cx, "msg.only.from.new %s", JSNativeCall::className)));
        /* NOT REACHED */
    }
    JSScriptRuntime::checkDeprecated(cx, "Call");

    JSNativeCall *result = new(cx) JSNativeCall();

    result->setPrototype(cx, getObjectPrototype(cx, (JSNativeFunction *) ctorObj));
    return JS_OBJECT_TO_VAL(result);
}

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

    JSScriptableObject::defineClass(cx, scope, proto, jsConstructor, NULL);
}

JSNativeCall *JSNativeCall::getActivation(JSNativeFunction * f)
{
    JSNativeCall *x = this;

    do {
        if (x->funObj == f) {
            return x;
        }
        x = x->caller;
    } while (x != NULL);
    return NULL;
}

JSNativeFunction *JSNativeCall::getFunctionObject()
{
    return funObj;
}

jsVal_t *JSNativeCall::getOriginalArguments(int *numOriginalArgs)
{
    *numOriginalArgs = this->numOriginalArgs;
    return originalArgs;
}

JSNativeCall *JSNativeCall::getCaller()
{
    return caller;
}

JSScriptable *JSNativeCall::getThisObj()
{
    return thisObj;
}

jsBool_t JSNativeCall::isInstanceOf(jsString_t className)
{
    if (className == JSNativeCall::className)
        return TRUE;
    return JSScriptableObject::isInstanceOf(className);
}

jsString_t JSNativeCall::getClassName(JSContext * cx)
{
    return JSNativeCall::className;
}

void JSNativeCall::mark(JSContext * cx)
{
    JSGCHeap *gcHeap = &cx->runtime->gcHeap;

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

    JSScriptableObject::mark(cx);

    if (funObj != NULL)
        funObj->mark(cx);
    if (thisObj != NULL)
        thisObj->mark(cx);
    if (caller != NULL)
        caller->mark(cx);
    if (originalArgs != NULL)
        gcHeap->markObj(originalArgs);  /* '04.05.06 Y.Tanaka */
}

void JSNativeCall::initialize(JSContext * cx, JSScriptable * scope, JSNativeFunction * funObj, JSScriptable * thisObj)
{
    this->funObj = funObj;
    this->thisObj = thisObj;

    setParentScope(cx, scope);
    // leave prototype null

    // save current activation
    this->caller = cx->currentActivation;
    cx->currentActivation = this;

    numOriginalArgs = 0;
    originalArgs = NULL;
}

JSNativeCall::JSNativeCall(JSContext * cx, JSScriptable * scope, JSNativeFunction * funObj, JSScriptable * thisObj, int numArgs, jsVal_t * args)
{
    initialize(cx, scope, funObj, thisObj);

    cx->pushLocalVal(JS_OBJECT_TO_VAL(this));   // avoid gc in initialize args
    numOriginalArgs = numArgs;
    if (numArgs > 0) {
        originalArgs = (jsVal_t *) JS_NewGCObj(cx, sizeof(*originalArgs)
                * numArgs);
        memcpy(originalArgs, args, sizeof(*originalArgs) * numArgs);
    }

    // initialize values of arguments
    jsString_t *names = funObj->names;

    if (names != NULL) {
        for (int i = 0; i < funObj->argCount; i++) {
            jsVal_t val = i < numArgs ? args[i] : JS_VAL_VOID;

            JSScriptableObject::put(cx, names[i + 1], this, val);
        }
    }

    // initialize "arguments" property
    JSScriptableObject::put(cx, "arguments", this, JS_OBJECT_TO_VAL(new(cx) JSArguments(cx, this)));
    cx->popLocalVals(1);
}

JSNativeCall::JSNativeCall(JSContext * cx, JSScriptable * scope, JSNativeFunction * funObj, JSScriptable * thisObj)
{
    initialize(cx, scope, funObj, thisObj);
}

JSNativeCall::JSNativeCall()
{
    caller = NULL;

    funObj = NULL;
    thisObj = NULL;

    numOriginalArgs = 0;
    originalArgs = NULL;
}
