/*
 * 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.
 */

#ifndef _JS_VARIABLE_TABLE_H_
#define _JS_VARIABLE_TABLE_H_

#include "js-runtime.h"
#include "js-gcnew.h"
#include "js-context.h"
#include "js-utils.h"
#include "js-vector.h"
#include "js-hashtable.h"
#include "js-localvariable.h"

/* Classes */
class JSVariableTable: public JSGCNew { /* GC collectable */
    protected:
        typedef JSVector<JSLocalVariable *, JSRuntime::GCAllocator> _Variables;
        class Variables: public _Variables {
            public:
                Variables(JSContext *cx, int initialiCapacity)
                        : _Variables(&cx->runtime->allocator,
                                initialiCapacity) {
                }
                void    mark(JSContext *cx) {
                    JSGCHeap *gcHeap = &cx->runtime->gcHeap;

                    if (elementData != NULL) {
                        gcHeap->markObj(elementData);
                    }
                }
        } ;
        typedef JSHashtable<int, jsString_t, JSRuntime::GCAllocator>
                _VariableNames;
        class VariableNames: public _VariableNames {
            protected:
                static size_t   hashCode(jsString_t key) {
                    const int M = 31;   // MULTIPLIER
                    const int maxLength = 8;
                    int value;

                    value = 0;
                    for (int i = 0; i < maxLength && key[i]; i++) {
                        value = value * M + key[i] & 0xff;
                    }
                    return value;
                }
                static int      cmpKey(jsString_t key1, jsString_t key2) {
                    return strcmp(key1, key2);
                }
                static void     dupEntry(
                                _VariableNames::HashtableEntry *dst,
                                _VariableNames::HashtableEntry *src,
                                JSRuntime::GCAllocator *allocator) {
                    *dst = *src;
#ifdef COMPACT_MEM
                    JSContext *cx = JSContext::getCurrentContext();
                    jsString_t string = cx->runtime->hasGCString(src->key);

                    if (string != NULL) {
                        cx->addNewBorn(string);
                        dst->key = string;
                        return;
                    }
#endif
                    dst->key
                        = (jsString_t) allocator->allocate(
                                                sizeof (dst->key)
                                                * (strlen(src->key) + 1));
                    if (dst->key) {
                        strcpy(dst->key, src->key);
                    }
                }
                static void     destroyEntry(
                                _VariableNames::HashtableEntry *tabEntry,
                                JSRuntime::GCAllocator *allocator) {
                }

            public:
                VariableNames(JSContext *cx, int initialCapacity)
                        :  _VariableNames(hashCode, cmpKey, dupEntry,
                                        destroyEntry, &cx->runtime->allocator,
                                        initialCapacity) {
                }

                /* GC methods */
                void    mark(JSContext *cx) {
                    JSGCHeap *gcHeap = &cx->runtime->gcHeap;

                    if (table != NULL) {
                        gcHeap->markObj(table);
                        for (int i = tableLength ; i-- > 0 ;) {
                            for (HashtableEntry *tabEntry = table[i] ;
                                tabEntry != NULL ; tabEntry = tabEntry->next) {
                                gcHeap->markObj(tabEntry);
                                gcHeap->markObj(tabEntry->key);
                            }
                        }
                    }
                }
        } ;

    protected:
        Variables               itsVariables;
        VariableNames           itsVariableNames;
        int                     varStart;

    public:
        static  void    classInitialize();

        JSVariableTable(JSContext *cx);
        virtual ~JSVariableTable() {
        }

        /* operators */
        void * operator new(size_t size, void *placement);

        int     size();
        int     getParameterCount();
        JSLocalVariable *
                createLocalVariable(JSContext *cx, jsString_t name,
                        jsBool_t isParameter);
        JSLocalVariable *
                get(int index);
        JSLocalVariable *
                get(jsString_t name);
        int
                getOrdinal(jsString_t name);
        jsString_t
                getName(int index);
        void    establishIndices();
        void    addParameter(JSContext *cx, jsString_t pName);
        void    addLocal(JSContext *cx, jsString_t vName);
        void    removeLocal(JSContext *cx, jsString_t name);

        /* gc methods */
        void    mark(JSContext *cx);
};

/* Inlines */
#ifdef FASTER
inline int
JSVariableTable::size()
{
    return itsVariables.size();
}
#endif

#endif /* _JS_VARIABLE_TABLE_H_ */
