/* vim: set ts=4 sw=4 tw=99 et:
 *
 * Copyright (C) 2012 David Anderson
 *
 * This file is part of JITCraft.
 *
 * JITCraft is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 * 
 * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * JITCraft. If not, see http://www.gnu.org/licenses/.
 */
#ifndef _include_jc_stack_h_
#define _include_jc_stack_h_

#include <stddef.h>
#include "Utility.h"
#include "Code.h"
#include "Functions.h"
#include "VirtualObjectVisitor.h"

namespace ke {

class Object;
class Array;
class Reference;
class Frame;
class Stack;
class Zone;
class Module;
    
union LocalSlot
{
    int i32;
    float f;
    Address address;
    Object *obj;
    Array *array;
    Reference *ref;
    Function *fun;
    Module *module;
};

struct Top
{
    LocalSlot *sp;
    bytecode *pc;
    Frame *fp;
};

class Frame
{
    friend class Stack;

    Frame *prev_;
    bytecode *prevpc_;
    union {
        Function *fun_;
        Native *native_;
        Object *callee_;
    };
    Code *code_;

    // Position of the LIFO stack at function entry.
    Address lifoPosition_;
    
    // Loop nesting level, for LIFO stack balancing. LIFO stack balancing
    // is not strictly required, but it helps to avoid unnecessary waste,
    // since once the LIFO stack is depleted, we must use the heap instead.
    unsigned loopDepth_;

  public:
    Frame(Frame *prev, bytecode *prevpc, Function *fun, Code *code, Address lifoPosition)
      : prev_(prev),
        prevpc_(prevpc),
        fun_(fun),
        code_(code),
        lifoPosition_(lifoPosition),
        loopDepth_(0)
    {
    }

    Frame(Frame *prev, bytecode *prevpc, Native *native, Address lifoPosition)
      : prev_(prev),
        prevpc_(prevpc),
        native_(native),
        code_(NULL),
        lifoPosition_(lifoPosition)
    {
    }

    Address *loopStack() {
        return reinterpret_cast<Address *>(this + 1);
    }
    LocalSlot *locals() {
        return reinterpret_cast<LocalSlot *>(loopStack() + code_->maxLoopDepth());
    }
    LocalSlot *stackBase() {
        return locals() + code_->nlocals();
    }
    Function *fun() const {
        assert(fun_ && callee_->is(MapKind_Function));
        return fun_;
    }
    Function *maybeFun() const {
        return (fun_ && callee_->is(MapKind_Function)) ? fun_ : NULL;
    }
    bool isNative() const {
        return callee_ && callee_->is(MapKind_Native);
    }
    Native *native() const {
        assert(isNative());
        return native_;
    }
    Code *code() const {
        assert(code_);
        return code_;
    }
    Frame *prev() const {
        return prev_;
    }
    bytecode *prevpc() const {
        return prevpc_;
    }
    Address lifoPosition() const {
        return lifoPosition_;
    }
    LocalSlot &arg(unsigned slot) {
        assert(slot < fun()->nargs());
        return *(reinterpret_cast<LocalSlot *>(this) - slot - 1);
    }
    void enterLoop(Zone *zone);
    void leaveLoop(Zone *zone);
    void accept(VirtualObjectVisitor *visitor);
};

// RAII object for calling a function from C++.
class InvokeArgs
{
    friend class Stack;

    Stack *stack_;
    LocalSlot *args_;
    unsigned nargs_;

  public:
    InvokeArgs()
      : stack_(NULL),
        args_(NULL),
        nargs_(0)
    {
    }

    ~InvokeArgs();

    LocalSlot &arg(unsigned index) {
        assert(index < nargs_);
        return args_[nargs_ - index - 1];
    }
};

// RAII object for calling a native from C++.
class NativeArgs
{
    friend class Stack;

    Stack *stack_;
    LocalSlot *argsBase_;

  public:
    NativeArgs()
      : stack_(NULL),
        argsBase_(NULL)
    {
    }

    ~NativeArgs();

    intptr_t *args() {
        return reinterpret_cast<intptr_t *>(argsBase_);
    }
};

class Stack
{
    friend class InvokeArgs;
    friend class NativeArgs;

  private:
    static const size_t STACK_SIZE = 1024 * 1024;

  public:
    Stack(Zone *zone);
    ~Stack();

    bool initialize();

  public:
    Frame *pushInlineFrame(Handle<Function> fun);
    Frame *popInlineFrame();

    bool pushInvokeFrame(Handle<Function> fun, InvokeArgs &args);
    bool pushModuleFrame(Handle<Module> module, InvokeArgs &args);
    bool pushNativeFrame(Handle<Native> native, NativeArgs &args, unsigned argc);

    Top *top() {
        return &top_;
    }
    bool isInCallStack(Handle<Module> module);
    void accept(VirtualObjectVisitor *visitor);

  private:
    void popInvokeFrame(InvokeArgs &args);
    void popNativeFrame(NativeArgs &args);

  private:
    Zone *zone_;
    char *buffer_;
    char *end_;
    Top top_;
};

class StackIter
{
    bytecode *pc_;
    Frame *fp_;

  public:
    StackIter(Zone *zone);

    bool done() const;
    const char *function() const;
    unsigned lineNumber() const;
    const char *source() const;
    void prev();
};

}

#endif // _include_jc_stack_h_
