Milán Major

netscape-revival

1 branch
Code

mocha/src/mo_atom.c

/*
** Mocha atom table.
**
** Brendan Eich, 6/16/95
*/
#include <ctype.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "prhash.h"
#include "prlog.h"
#include "prmem.h"
#include "mo_atom.h"
#include "mo_bcode.h"
#include "mo_emit.h"
#include "mo_cntxt.h"
#include "mocha.h"
#include "mochaapi.h"

MochaAtom *mocha_typeAtoms[MOCHA_NTYPES];
MochaAtom *mocha_booleanAtoms[2];
MochaAtom *mocha_nullAtom;

MochaAtom *mocha_anonymousAtom;
MochaAtom *mocha_assignAtom;
MochaAtom *mocha_constructorAtom;
MochaAtom *mocha_finalizeAtom;
MochaAtom *mocha_getPropertyAtom;
MochaAtom *mocha_listPropertiesAtom;
MochaAtom *mocha_prototypeAtom;
MochaAtom *mocha_resolveNameAtom;
MochaAtom *mocha_setPropertyAtom;
MochaAtom *mocha_toStringAtom;
MochaAtom *mocha_valueOfAtom;

/* Keep this in sync with mochaapi.h -- an assertion below will insist. */
char *mocha_typeStr[] = {
    "undefined",
    "internal",
    "atom",
    "symbol",
    "function",
    "object",
    "number",
    "boolean",
    "string",
};

char mocha_anonymousStr[]       = "anonymous";
char mocha_assignStr[]          = "assign";
char mocha_constructorStr[]     = "constructor";
char mocha_finalizeStr[]        = "finalize";
char mocha_getPropertyStr[]     = "getProperty";
char mocha_listPropertiesStr[]  = "listProperties";
char mocha_prototypeStr[]       = "prototype";
char mocha_resolveNameStr[]     = "resolveName";
char mocha_setPropertyStr[]     = "setProperty";
char mocha_toStringStr[]        = "toString";
char mocha_valueOfStr[]         = "valueOf";

MochaAtomState mocha_AtomState;

PR_STATIC_CALLBACK(void *)
AllocAtomSpace(void *pool, size_t size)
{
    return malloc(size);
}

PR_STATIC_CALLBACK(void)
FreeAtomStub(void *pool, void *item)
{
    free(item);
}

PR_STATIC_CALLBACK(PRHashEntry *)
AllocAtom(void *pool)
{
    MochaAtom *atom;
    
    atom = PR_NEW(MochaAtom);
    if (!atom)
	return 0;
    return &atom->entry;
}

PR_STATIC_CALLBACK(void)
FreeAtom(void *pool, PRHashEntry *he, int flag)
{
    MochaAtom *atom = (MochaAtom *)he;

    PR_ASSERT(flag == HT_FREE_ENTRY);
    if (flag == HT_FREE_ENTRY) {
	free((char *)atom->entry.key);
	PR_DELETE(atom);
    }
}

static PRHashAllocOps atomAllocOps = {
    AllocAtomSpace, FreeAtomStub,
    AllocAtom, FreeAtom
};

#define MOCHA_ATOM_HASH_SIZE	1024

MochaBoolean
mocha_InitAtomState(MochaContext *mc)
{
    unsigned i;
    MochaAtom *atom;

    if (mocha_AtomState.valid) {
	mocha_AtomState.nrefs++;
	return MOCHA_TRUE;
    }

    mocha_AtomState.table = PR_NewHashTable(MOCHA_ATOM_HASH_SIZE,
					    PR_HashString,
					    PR_CompareStrings,
					    PR_CompareValues,
					    &atomAllocOps, 0);
    if (!mocha_AtomState.table) {
	MOCHA_ReportOutOfMemory(mc);
	return MOCHA_FALSE;
    }

#define FROB(lval,str,type) {                                                 \
    if (lval) mocha_DropAtom(mc, lval);                                       \
    lval = atom = mocha_Atomize(mc, str, ATOM_HELD | type);                   \
    if (!atom) return MOCHA_FALSE;                                            \
}

    PR_ASSERT(sizeof mocha_typeStr / sizeof mocha_typeStr[0] == MOCHA_NTYPES);
    for (i = 0; i < MOCHA_NTYPES; i++)
	FROB(mocha_typeAtoms[i],    mocha_typeStr[i],         ATOM_NAME);

    /* XXX redundant w.r.t. mo_scan.c */
    FROB(mocha_booleanAtoms[0],     mocha_false,              ATOM_KEYWORD);
    FROB(mocha_booleanAtoms[1],     mocha_true,               ATOM_KEYWORD);
    atom->fval = 1;
    FROB(mocha_nullAtom,            mocha_null,               ATOM_KEYWORD);

    FROB(mocha_anonymousAtom,       mocha_anonymousStr,       ATOM_NAME);
    FROB(mocha_assignAtom,          mocha_assignStr,          ATOM_NAME);
    FROB(mocha_constructorAtom,     mocha_constructorStr,     ATOM_NAME);
    FROB(mocha_finalizeAtom,        mocha_finalizeStr,        ATOM_NAME);
    FROB(mocha_getPropertyAtom,     mocha_getPropertyStr,     ATOM_NAME);
    FROB(mocha_listPropertiesAtom,  mocha_listPropertiesStr,  ATOM_NAME);
    FROB(mocha_prototypeAtom,       mocha_prototypeStr,       ATOM_NAME);
    FROB(mocha_resolveNameAtom,     mocha_resolveNameStr,     ATOM_NAME);
    FROB(mocha_setPropertyAtom,     mocha_setPropertyStr,     ATOM_NAME);
    FROB(mocha_toStringAtom,        mocha_toStringStr,        ATOM_NAME);
    FROB(mocha_valueOfAtom,         mocha_valueOfStr,         ATOM_NAME);

#undef FROB

    mocha_AtomState.valid = MOCHA_TRUE;
    mocha_AtomState.nrefs = 1;
    return MOCHA_TRUE;
}

void
mocha_FreeAtomState(MochaContext *mc)
{
    PR_ASSERT(mocha_AtomState.nrefs > 0);
    if (mocha_AtomState.nrefs <= 0) return;
    if (--mocha_AtomState.nrefs == 0) {
	PR_HashTableDestroy(mocha_AtomState.table);
	memset(&mocha_AtomState, 0, sizeof mocha_AtomState);
    }
}

MochaAtom *
mocha_Atomize(MochaContext *mc, const char *string, MochaAtomFlags flags)
{
    MochaBoolean doHold;
    unsigned length;
    PRHashNumber keyHash;
    PRHashEntry *he, **hep;
    char *newString;
    MochaAtom *atom;

    doHold  = (flags & ATOM_HELD) ? MOCHA_TRUE : MOCHA_FALSE;
    flags &= ATOM_TYPEMASK;
    length = strlen(string);

    keyHash = PR_HashString(string);
    hep = PR_HashTableRawLookup(mocha_AtomState.table, keyHash, string);
    if ((he = *hep) != 0) {
        atom = (MochaAtom *)he;
	atom->flags |= flags;
    } else {
	newString = MOCHA_malloc(mc, length + 1);
	if (!newString)
	    return 0;
	strcpy(newString, string);
	he = PR_HashTableRawAdd(mocha_AtomState.table, hep, keyHash,
				newString, 0);
	if (!he) {
	    MOCHA_ReportOutOfMemory(mc);
	    return 0;
	}
	atom = (MochaAtom *)he;
	atom->entry.value = 0;
	atom->nrefs = 0;
	atom->length = length;
	atom->flags = flags;
	atom->keyIndex = -1;
	atom->index = 0;
	atom->number = mocha_AtomState.number++;
	atom->fval = 0;
    }
#ifdef DEBUG_brendan
    hep = PR_HashTableRawLookup(mocha_AtomState.table, keyHash, string);
    he = *hep;
    PR_ASSERT(atom == (MochaAtom *)he);
#endif

    if (doHold)
	mocha_HoldAtom(mc, atom);
    return atom;
}

MochaAtomNumber
mocha_IndexAtom(MochaContext *mc, MochaAtom *atom, CodeGenerator *cg)
{
    if (!(atom->flags & ATOM_INDEXED)) {
	atom->flags |= ATOM_INDEXED;
	atom->index = (uint16) cg->atomCount++;
	atom->entry.value = cg->atomList;
	cg->atomList = mocha_HoldAtom(mc, atom);
    }
    return atom->index;
}

MochaAtom *
mocha_HoldAtom(MochaContext *mc, MochaAtom *atom)
{
#ifdef DEBUG_brendan
    const char *string;
    PRHashNumber keyHash;
    PRHashEntry *he, **hep;

    string = atom->entry.key;
    keyHash = PR_HashString(string);
    hep = PR_HashTableRawLookup(mocha_AtomState.table, keyHash, string);
    he = *hep;
    PR_ASSERT(atom == (MochaAtom *)he);
#endif
    atom->nrefs++;
    PR_ASSERT(atom->nrefs > 0);
    return atom;
}

MochaAtom *
mocha_DropAtom(MochaContext *mc, MochaAtom *atom)
{
#ifdef DEBUG_brendan
    const char *string;
    PRHashNumber keyHash;
    PRHashEntry *he, **hep;

    string = strdup(atom->entry.key);
    if (string) {
	keyHash = PR_HashString(string);
	hep = PR_HashTableRawLookup(mocha_AtomState.table, keyHash, string);
	he = *hep;
	PR_ASSERT(atom == (MochaAtom *)he);
    }
#endif
    PR_ASSERT(atom->nrefs > 0);
    if (atom->nrefs <= 0) return 0;
    if (--atom->nrefs == 0) {
	PR_HashTableRemove(mocha_AtomState.table, atom->entry.key);
#ifdef DEBUG_brendan
	if (string) {
	    hep = PR_HashTableRawLookup(mocha_AtomState.table, keyHash, string);
	    he = *hep;
	    PR_ASSERT(he == 0);
	}
#endif
	atom = 0;
    }
#ifdef DEBUG_brendan
    PR_FREEIF((char *)string);
#endif
    return atom;
}

MochaAtom *
mocha_GetAtom(MochaContext *mc, MochaAtomMap *map, MochaAtomNumber n)
{
    MochaAtom *atom;

    PR_ASSERT(map->vector && n < map->length);
    if (!map->vector || n >= map->length) {
	MOCHA_ReportError(mc, "internal error: no index for atom %ld", (long)n);
	return 0;
    }
    atom = map->vector[n];
    PR_ASSERT(atom);
    return atom;
}

MochaBoolean
mocha_InitAtomMap(MochaContext *mc, MochaAtomMap *map, CodeGenerator *cg)
{
    MochaAtom *atom, *next, **vector;
    MochaAtomNumber length;

    atom = cg->atomList;
    if (!atom) {
	map->vector = 0, map->length = 0;
	return MOCHA_TRUE;
    }

    length = cg->atomCount;
    if (length >= MOCHA_ATOM_INDEX_MAX) {
        MOCHA_ReportError(mc, "too many atoms");
	return MOCHA_FALSE;
    }
    vector = MOCHA_malloc(mc, length * sizeof *vector);
    if (!vector)
	return MOCHA_FALSE;

    do {
        vector[atom->index] = atom;
	atom->flags &= ~ATOM_INDEXED;
	next = atom_next(atom);
	atom->entry.value = 0;
    } while ((atom = next) != 0);
    cg->atomList = 0;
    cg->atomCount = 0;

    map->vector = vector;
    map->length = length;
    return MOCHA_TRUE;
}

void
mocha_FreeAtomMap(MochaContext *mc, MochaAtomMap *map)
{
    unsigned i;

    if (map->vector) {
        for (i = 0; i < map->length; i++)
	    mocha_DropAtom(mc, map->vector[i]);
	free(map->vector);
	map->vector = 0;
    }
    map->length = 0;
}

void
mocha_DropUnmappedAtoms(MochaContext *mc, CodeGenerator *cg)
{
    MochaAtom *atom, *next;

    for (atom = cg->atomList; atom; atom = next) {
	atom->flags &= ~ATOM_INDEXED;
	next = atom_next(atom);
	atom->entry.value = 0;
	mocha_DropAtom(mc, atom);
    }
    cg->atomList = 0;
    cg->atomCount = 0;
}