Milán Major

netscape-revival

1 branch
Code

mocha/src/mo_fun.c

/*
** Mocha function methods.
**
** Brendan Eich, 11/15/95
*/
#include <ctype.h>
#include <string.h>
#include "mo_atom.h"
#include "mo_bcode.h"
#include "mo_cntxt.h"
#include "mo_emit.h"
#include "mo_parse.h"
#include "mo_scan.h"
#include "mo_scope.h"
#include "mocha.h"
#include "mochaapi.h"
#include "mochalib.h"

static MochaAtom *
function_to_atom(MochaContext *mc, MochaFunction *fun)
{
    MochaPrinter *mp;
    MochaBoolean ok;
    char *str;

    mp = mocha_NewPrinter(mc, atom_name(fun->atom), 0);
    if (!mp)
	return 0;
    ok = mocha_DecompileFunction(fun, mp);
    if (ok)
	ok = mocha_GetPrinterOutput(mp, &str);
    mocha_DestroyPrinter(mp);
    if (!ok)
	return 0;
    return mocha_Atomize(mc, str, ATOM_STRING);
}

MochaBoolean
mocha_FunctionToString(MochaContext *mc, MochaFunction *fun, MochaAtom **atomp)
{
    MochaAtom *atom;

    atom = function_to_atom(mc, fun);
    if (!atom)
	return MOCHA_FALSE;
    *atomp = mocha_HoldAtom(mc, atom);
    return MOCHA_TRUE;
}

MochaBoolean
mocha_DatumToFunction(MochaContext *mc, MochaDatum d, MochaFunction **funp)
{
    MochaDatum aval, rval;
    MochaObject *obj;
    MochaAtom *atom;

    aval = d;
    if (!mocha_ResolveValue(mc, &d))
	return MOCHA_FALSE;
    switch (d.tag) {
      case MOCHA_FUNCTION:
	obj = &d.u.fun->object;
	break;
      case MOCHA_OBJECT:
	obj = d.u.obj;
	if (obj) {
	    rval = MOCHA_void;
	    if (!OBJ_CONVERT(mc, obj, MOCHA_FUNCTION, &rval))
		return MOCHA_FALSE;
	    MOCHA_MIX_TAINT(mc, mc->taintInfo->accum, rval.taint);
	    if (rval.tag == MOCHA_FUNCTION) {
		obj = &rval.u.fun->object;
		break;
	    }
	}
	/* FALL THROUGH */
      default:
	if (mocha_RawDatumToString(mc, aval, &atom)) {
	    MOCHA_ReportError(mc, "%s is not a function", atom_name(atom));
	    mocha_DropAtom(mc, atom);
	}
	return MOCHA_FALSE;
    }
    *funp = (MochaFunction *)MOCHA_HoldObject(mc, obj);
    return MOCHA_TRUE;
}

static MochaBoolean
IsIdentifier(const char *s)
{
    char c = *s;

    if (!isalpha(c) && c != '_')
	return MOCHA_FALSE;
    while ((c = *++s) != '\0') {
	if (!isalnum(c) && c != '_')
	    return MOCHA_FALSE;
    }
    return MOCHA_TRUE;
}

enum fun_slot {
    FUN_CALLER    = -1,         /* function that called this fun activation */
    FUN_ARGUMENTS = -2,         /* actual arguments for this call to fun */
    FUN_LENGTH    = -3          /* number of arguments to this call */
};

static MochaPropertySpec function_props[] = {
    {"caller",          FUN_CALLER},
    {"arguments",       FUN_ARGUMENTS,  MDF_BACKEDGE},
    {"length",          FUN_LENGTH},
    {0}
};

static MochaBoolean
fun_get_property(MochaContext *mc, MochaObject *obj, MochaSlot slot,
		 MochaDatum *dp)
{
    MochaFunction *fun;
    MochaStackFrame *fp;
    MochaBoolean valid;

    fun = (MochaFunction *)obj;
    fp = mc->stack.frame;
    valid = (fp && fp->fun == fun);

    switch (slot) {
      case FUN_CALLER:
	if (valid && fp->down) {
	    MOCHA_INIT_DATUM(mc, dp, MOCHA_FUNCTION, u.fun, fp->down->fun);
	} else {
	    *dp = MOCHA_null;
	}
	break;

      case FUN_ARGUMENTS:
	MOCHA_INIT_DATUM(mc, dp, MOCHA_OBJECT, u.obj, valid ? obj : 0);
	break;

      case FUN_LENGTH:
	MOCHA_INIT_DATUM(mc, dp, MOCHA_NUMBER, u.fval,
			 valid ? fp->argc : fun->nargs);
	break;

      default:
	if (slot < 0)
	    return MOCHA_TRUE;
	if (valid && (uint32)slot < (uint32)fp->argc)
	    *dp = fp->argv[slot];
	break;
    }

    return MOCHA_TRUE;
}

static MochaBoolean
fun_convert(MochaContext *mc, MochaObject *obj, MochaTag tag, MochaDatum *dp);

static void
fun_finalize(MochaContext *mc, MochaObject *obj)
{
    MochaFunction *fun;

    fun = (MochaFunction *)obj;
    mocha_DropAtom(mc, fun->atom);
    if (fun->bound)
	MOCHA_DropObject(mc, fun->object.parent);
    if (fun->script)
	mocha_DestroyScript(mc, fun->script);
}

static MochaClass function_class = {
    "Function",
    fun_get_property, MOCHA_PropertyStub, MOCHA_ListPropStub,
    MOCHA_ResolveStub, fun_convert, fun_finalize
};

/* This needs function_class, so it has a forward declaration above. */
static MochaBoolean
fun_convert(MochaContext *mc, MochaObject *obj, MochaTag tag, MochaDatum *dp)
{
    switch (tag) {
      case MOCHA_FUNCTION:
	MOCHA_INIT_DATUM(mc, dp, MOCHA_FUNCTION, u.fun, (MochaFunction *)obj);
	return MOCHA_TRUE;
      default:
	return MOCHA_TRUE;
    }
}

static MochaBoolean
fun_to_string(MochaContext *mc, MochaObject *obj,
	      unsigned argc, MochaDatum *argv, MochaDatum *rval)
{
    MochaFunction *fun;
    MochaAtom *atom;

    if (!MOCHA_InstanceOf(mc, obj, &function_class, argv[-1].u.fun))
	return MOCHA_FALSE;
    fun = (MochaFunction *)obj;
    atom = function_to_atom(mc, fun);
    if (!atom)
	return MOCHA_FALSE;
    MOCHA_INIT_DATUM(mc, rval, MOCHA_STRING, u.atom, atom);
    return MOCHA_TRUE;
}

static MochaBoolean
fun_value_of(MochaContext *mc, MochaObject *obj,
	     unsigned argc, MochaDatum *argv, MochaDatum *rval)
{
    if (!MOCHA_InstanceOf(mc, obj, &function_class, argv[-1].u.fun))
	return MOCHA_FALSE;
    MOCHA_INIT_DATUM(mc, rval, MOCHA_FUNCTION, u.fun, (MochaFunction *)obj);
    return MOCHA_TRUE;
}

static MochaFunctionSpec function_methods[] = {
    {mocha_toStringStr,		fun_to_string,		0},
    {mocha_valueOfStr,		fun_value_of,		0},
    {0}
};

static MochaBoolean
Function(MochaContext *mc, MochaObject *obj,
	 unsigned argc, MochaDatum *argv, MochaDatum *rval)
{
    MochaFunction *fun;
    unsigned i, nargs, lineno;
    MochaAtom *atom;
    const char *name, *filename;
    MochaSymbol *arg, *args, **argp;
    MochaTokenStream *ts;
    MochaBoolean ok;

    nargs = argc ? argc - 1 : 0;
    fun = mocha_NewFunction(mc, 0, nargs, obj->parent, mocha_anonymousAtom);
    if (!fun)
	return MOCHA_FALSE;

    args = 0;
    argp = &args;
    for (i = 0; i < nargs; i++) {
	if (!mocha_DatumToString(mc, argv[i], &atom))
	    goto fail;
	name = atom_name(atom);
	if (!IsIdentifier(name)) {
	    MOCHA_ReportError(mc, "illegal formal argument name %s", name);
	    mocha_DropAtom(mc, atom);
	    goto fail;
	}

	arg = mocha_DefineSymbol(mc, fun->object.scope, atom, SYM_ARGUMENT, 0);
	mocha_DropAtom(mc, atom);
	if (!arg) goto fail;
	arg->slot = i;
	*argp = arg;
	argp = &arg->next;
    }

    if (i < argc) {
	if (!mocha_DatumToString(mc, argv[i], &atom))
	    goto fail;
    } else {
	atom = mocha_HoldAtom(mc, MOCHA_empty.u.atom);
    }
    if (mc->script) {
	filename = mc->script->filename;
	lineno = mocha_PCtoLineNumber(mc->script, mc->pc);
    } else {
	filename = 0;
	lineno = 0;
    }
    ts = mocha_NewTokenStream(mc, atom_name(atom), atom->length,
			      filename, lineno);
    if (ts) {
	ok = mocha_ParseFunctionBody(mc, ts, fun);
	(void) mocha_CloseTokenStream(ts);
    } else {
	ok = MOCHA_FALSE;
    }
    mocha_DropAtom(mc, atom);
    if (!ok)
	goto fail;
    fun->script->args = args;
    MOCHA_INIT_DATUM(mc, rval, MOCHA_OBJECT, u.obj, &fun->object);
    return MOCHA_TRUE;

fail:
    mocha_DestroyFunction(mc, fun);
    *rval = MOCHA_null;
    return MOCHA_TRUE;
}

MochaObject *
mocha_InitFunctionClass(MochaContext *mc, MochaObject *obj)
{
    return MOCHA_InitClass(mc, obj, &function_class, 0, Function, 1,
			   function_props, function_methods, 0, 0);
}

MochaObject *
mocha_FunctionToObject(MochaContext *mc, MochaFunction *fun)
{
    return &fun->object;
}

MochaFunction *
mocha_NewFunction(MochaContext *mc, MochaNativeCall call, unsigned nargs,
		  MochaObject *parent, MochaAtom *atom)
{
    MochaFunction *fun;
    MochaObject *prototype;

    /* Allocate a function object. */
    fun = MOCHA_malloc(mc, sizeof *fun);
    if (!fun)
	return 0;

    /* Initialize base state. */
    if (!mocha_GetPrototype(mc, &function_class, &prototype) ||
	!mocha_InitObject(mc, &fun->object, &function_class, 0, prototype,
			  parent)) {
	MOCHA_free(mc, fun);
	return 0;
    }

    /* Initialize derived class state. */
    fun->call = call;
    fun->nargs = nargs;
    fun->bound = MOCHA_FALSE;
    fun->spare = 0;
    fun->atom = mocha_HoldAtom(mc, atom);
    fun->script = 0;
    return fun;
}

void
mocha_DestroyFunction(MochaContext *mc, MochaFunction *fun)
{
    mocha_DestroyObject(mc, &fun->object);
}

/* XXX orthogonalize a la NewObject/DefineObject/DefineNewObject */
MochaFunction *
mocha_DefineFunction(MochaContext *mc, MochaObject *obj, MochaAtom *atom,
		     MochaNativeCall call, unsigned nargs, unsigned flags)
{
    MochaFunction *fun;
    MochaDatum fd;

    fun = mocha_NewFunction(mc, call, nargs, obj, atom);
    if (!fun)
	return 0;
    MOCHA_INIT_FULL_DATUM(mc, &fd, MOCHA_FUNCTION, flags, MOCHA_TAINT_IDENTITY,
			  u.fun, fun);
    if (!mocha_SetProperty(mc, obj->scope, atom, obj->scope->minslot-1, fd)) {
	mocha_DestroyFunction(mc, fun);
	return 0;
    }
    return fun;
}