ref: c6723706a45622ab27cc4e3ce3b1860d7fc1d504
dir: /jscompile.c/
#include "js.h"
#include "jsparse.h"
#include "jscompile.h"
#include "jsrun.h"
#include "jsstate.h"
#define cexp js_cexp /* collision with math.h */
#define JF js_State *J, js_Function *F
static js_Function *newfun(js_State *J, js_Ast *name, js_Ast *params, js_Ast *body);
static void cexp(JF, js_Ast *exp);
static void cstmlist(JF, js_Ast *list);
static int listlen(js_Ast *list)
{
int n = 0;
while (list) {
n++;
list = list->b;
}
return n;
}
static int consteq(js_Value a, js_Value b)
{
if (a.type != b.type) return 0;
if (a.type == JS_TNUMBER) return a.u.number == b.u.number;
if (a.type == JS_TSTRING) return a.u.string == b.u.string;
return a.u.p == b.u.p;
}
static int addconst(JF, js_Value v)
{
int i;
for (i = 0; i < F->klen; i++) {
if (consteq(F->klist[i], v)) {
return i;
}
}
if (F->klen >= F->kcap) {
F->kcap *= 2;
F->klist = realloc(F->klist, F->kcap * sizeof(js_Value));
}
F->klist[F->klen] = v;
return F->klen++;
}
static void emit(JF, int value)
{
if (F->len >= F->cap) {
F->cap *= 2;
F->code = realloc(F->code, F->cap);
}
F->code[F->len++] = value;
}
static void emitfunction(JF, int opcode, js_Function *fun)
{
js_Value v;
v.type = JS_TFUNCTION;
v.u.function = fun;
emit(J, F, opcode);
emit(J, F, addconst(J, F, v));
}
static void emitnumber(JF, int opcode, double n)
{
js_Value v;
v.type = JS_TNUMBER;
v.u.number = n;
emit(J, F, opcode);
emit(J, F, addconst(J, F, v));
}
static void emitstring(JF, int opcode, const char *s)
{
js_Value v;
v.type = JS_TSTRING;
v.u.string = s;
emit(J, F, opcode);
emit(J, F, addconst(J, F, v));
}
static void emitname(JF, int opcode, const char *s)
{
js_Value v;
v.type = JS_TSTRING;
v.u.string = s;
emit(J, F, opcode);
emit(J, F, addconst(J, F, v));
}
static int jump(JF, int opcode)
{
int addr = F->len + 1;
emit(J, F, opcode);
emit(J, F, 0);
emit(J, F, 0);
return addr;
}
static void label(JF, int addr)
{
int dest = F->len;
F->code[addr+0] = (dest >> 8) & 0xFF;
F->code[addr+1] = (dest) & 0xFF;
}
static void unary(JF, js_Ast *exp, int opcode)
{
cexp(J, F, exp->a);
emit(J, F, opcode);
}
static void binary(JF, js_Ast *exp, int opcode)
{
cexp(J, F, exp->a);
cexp(J, F, exp->b);
emit(J, F, opcode);
}
static void carray(JF, js_Ast *list)
{
while (list) {
cexp(J, F, list->a);
emit(J, F, OP_ARRAYPUT);
list = list->b;
}
}
static void cobject(JF, js_Ast *list)
{
while (list) {
js_Ast *kv = list->a;
if (kv->type == EXP_PROP_VAL) {
js_Ast *prop = kv->a;
cexp(J, F, kv->b);
if (prop->type == AST_IDENTIFIER || prop->type == AST_STRING)
emitname(J, F, OP_OBJECTPUT, prop->string);
else if (prop->type == AST_STRING)
emitstring(J, F, OP_OBJECTPUT, prop->string);
else if (prop->type == AST_NUMBER)
emitnumber(J, F, OP_OBJECTPUT, prop->number);
else
jsC_error(J, list, "illegal property name in object initializer");
}
// TODO: set, get
list = list->b;
}
}
static int cargs(JF, js_Ast *list)
{
int n = 0;
while (list) {
cexp(J, F, list->a);
list = list->b;
n++;
}
return n;
}
static void clval(JF, js_Ast *exp)
{
switch (exp->type) {
case AST_IDENTIFIER:
emitname(J, F, OP_AVAR, exp->string);
break;
case EXP_INDEX:
cexp(J, F, exp->a);
cexp(J, F, exp->b);
emit(J, F, OP_AINDEX);
break;
case EXP_MEMBER:
cexp(J, F, exp->a);
emitname(J, F, OP_AMEMBER, exp->b->string);
break;
case EXP_CALL:
/* host functions may return an assignable l-value */
cexp(J, F, exp);
break;
default:
jsC_error(J, exp, "invalid l-value in assignment");
break;
}
}
static void assignop(JF, js_Ast *exp, int opcode)
{
clval(J, F, exp->a);
emit(J, F, OP_LOAD);
cexp(J, F, exp->b);
emit(J, F, opcode);
emit(J, F, OP_STORE);
}
static void cexp(JF, js_Ast *exp)
{
int then, end;
int n;
switch (exp->type) {
case AST_IDENTIFIER: emitname(J, F, OP_LOADVAR, exp->string); break;
case AST_NUMBER: emitnumber(J, F, OP_CONST, exp->number); break;
case AST_STRING: emitstring(J, F, OP_CONST, exp->string); break;
case EXP_UNDEF: emit(J, F, OP_UNDEF); break;
case EXP_NULL: emit(J, F, OP_NULL); break;
case EXP_TRUE: emit(J, F, OP_TRUE); break;
case EXP_FALSE: emit(J, F, OP_FALSE); break;
case EXP_THIS: emit(J, F, OP_THIS); break;
case EXP_OBJECT:
emit(J, F, OP_OBJECT);
cobject(J, F, exp->a);
break;
case EXP_ARRAY:
emit(J, F, OP_ARRAY);
carray(J, F, exp->a);
break;
case EXP_INDEX:
cexp(J, F, exp->a);
cexp(J, F, exp->b);
emit(J, F, OP_LOADINDEX);
break;
case EXP_MEMBER:
cexp(J, F, exp->a);
emitname(J, F, OP_LOADMEMBER, exp->b->string);
break;
case EXP_CALL:
if (exp->a->type == EXP_MEMBER) {
cexp(J, F, exp->a->a);
emit(J, F, OP_DUP);
emitname(J, F, OP_LOADMEMBER, exp->a->b->string);
n = cargs(J, F, exp->b);
emit(J, F, OP_TCALL);
emit(J, F, n);
} else {
cexp(J, F, exp->a);
n = cargs(J, F, exp->b);
emit(J, F, OP_CALL);
emit(J, F, n);
}
break;
case EXP_NEW:
cexp(J, F, exp->a);
n = cargs(J, F, exp->b);
emit(J, F, OP_NEW);
emit(J, F, n);
break;
case EXP_FUNC:
emitfunction(J, F, OP_CLOSURE, newfun(J, exp->a, exp->b, exp->c));
break;
case EXP_DELETE:
clval(J, F, exp->a);
emit(J, F, OP_DELETE);
break;
case EXP_VOID:
cexp(J, F, exp->a);
emit(J, F, OP_POP);
emit(J, F, OP_UNDEF);
break;
case EXP_TYPEOF: unary(J, F, exp, OP_TYPEOF); break;
case EXP_POS: unary(J, F, exp, OP_POS); break;
case EXP_NEG: unary(J, F, exp, OP_NEG); break;
case EXP_BITNOT: unary(J, F, exp, OP_BITNOT); break;
case EXP_LOGNOT: unary(J, F, exp, OP_LOGNOT); break;
case EXP_PREINC: clval(J, F, exp->a); emit(J, F, OP_PREINC); break;
case EXP_PREDEC: clval(J, F, exp->a); emit(J, F, OP_PREDEC); break;
case EXP_POSTINC: clval(J, F, exp->a); emit(J, F, OP_POSTINC); break;
case EXP_POSTDEC: clval(J, F, exp->a); emit(J, F, OP_POSTDEC); break;
case EXP_BITOR: binary(J, F, exp, OP_BITOR); break;
case EXP_BITXOR: binary(J, F, exp, OP_BITXOR); break;
case EXP_BITAND: binary(J, F, exp, OP_BITAND); break;
case EXP_EQ: binary(J, F, exp, OP_EQ); break;
case EXP_NE: binary(J, F, exp, OP_NE); break;
case EXP_EQ3: binary(J, F, exp, OP_EQ3); break;
case EXP_NE3: binary(J, F, exp, OP_NE3); break;
case EXP_LT: binary(J, F, exp, OP_LT); break;
case EXP_GT: binary(J, F, exp, OP_GT); break;
case EXP_LE: binary(J, F, exp, OP_LE); break;
case EXP_GE: binary(J, F, exp, OP_GE); break;
case EXP_INSTANCEOF: binary(J, F, exp, OP_INSTANCEOF); break;
case EXP_IN: binary(J, F, exp, OP_IN); break;
case EXP_SHL: binary(J, F, exp, OP_SHL); break;
case EXP_SHR: binary(J, F, exp, OP_SHR); break;
case EXP_USHR: binary(J, F, exp, OP_USHR); break;
case EXP_ADD: binary(J, F, exp, OP_ADD); break;
case EXP_SUB: binary(J, F, exp, OP_SUB); break;
case EXP_MUL: binary(J, F, exp, OP_MUL); break;
case EXP_DIV: binary(J, F, exp, OP_DIV); break;
case EXP_MOD: binary(J, F, exp, OP_MOD); break;
case EXP_ASS:
clval(J, F, exp->a);
cexp(J, F, exp->b);
emit(J, F, OP_STORE);
break;
case EXP_ASS_MUL: assignop(J, F, exp, OP_MUL); break;
case EXP_ASS_DIV: assignop(J, F, exp, OP_DIV); break;
case EXP_ASS_MOD: assignop(J, F, exp, OP_MOD); break;
case EXP_ASS_ADD: assignop(J, F, exp, OP_ADD); break;
case EXP_ASS_SUB: assignop(J, F, exp, OP_SUB); break;
case EXP_ASS_SHL: assignop(J, F, exp, OP_SHL); break;
case EXP_ASS_SHR: assignop(J, F, exp, OP_SHR); break;
case EXP_ASS_USHR: assignop(J, F, exp, OP_USHR); break;
case EXP_ASS_BITAND: assignop(J, F, exp, OP_BITAND); break;
case EXP_ASS_BITXOR: assignop(J, F, exp, OP_BITXOR); break;
case EXP_ASS_BITOR: assignop(J, F, exp, OP_BITOR); break;
case EXP_COMMA:
cexp(J, F, exp->a);
emit(J, F, OP_POP);
cexp(J, F, exp->b);
break;
case EXP_LOGOR:
/* if a == true then a else b */
cexp(J, F, exp->a);
emit(J, F, OP_DUP);
end = jump(J, F, OP_JTRUE);
emit(J, F, OP_POP);
cexp(J, F, exp->b);
label(J, F, end);
break;
case EXP_LOGAND:
/* if a == false then a else b */
cexp(J, F, exp->a);
emit(J, F, OP_DUP);
end = jump(J, F, OP_JFALSE);
emit(J, F, OP_POP);
cexp(J, F, exp->b);
label(J, F, end);
break;
case EXP_COND:
cexp(J, F, exp->a);
then = jump(J, F, OP_JTRUE);
cexp(J, F, exp->c);
end = jump(J, F, OP_JUMP);
label(J, F, then);
cexp(J, F, exp->b);
label(J, F, end);
break;
default:
jsC_error(J, exp, "unknown expression");
}
}
static void cvarinit(JF, js_Ast *list)
{
while (list) {
js_Ast *var = list->a;
if (var->b) {
cexp(J, F, var->b);
emitname(J, F, OP_AVAR, var->a->string);
emit(J, F, OP_STORE);
emit(J, F, OP_POP);
}
list = list->b;
}
}
static void cstm(JF, js_Ast *stm)
{
int then, end;
switch (stm->type) {
case STM_FUNC:
break;
case STM_BLOCK:
cstmlist(J, F, stm->a);
break;
case STM_NOP:
break;
case STM_VAR:
cvarinit(J, F, stm->a);
break;
case STM_IF:
if (stm->c) {
cexp(J, F, stm->a);
then = jump(J, F, OP_JTRUE);
cstm(J, F, stm->c);
end = jump(J, F, OP_JUMP);
label(J, F, then);
cstm(J, F, stm->b);
label(J, F, end);
} else {
cexp(J, F, stm->a);
end = jump(J, F, OP_JFALSE);
cstm(J, F, stm->b);
label(J, F, end);
}
break;
case STM_RETURN:
if (stm->a)
cexp(J, F, stm->a);
else
emit(J, F, OP_UNDEF);
emit(J, F, OP_RETURN);
break;
case STM_WITH:
cexp(J, F, stm->a);
emit(J, F, OP_WITH);
cstm(J, F, stm->b);
emit(J, F, OP_ENDWITH);
break;
// switch
// throw, try
// label
case STM_THROW:
cexp(J, F, stm->a);
emit(J, F, OP_THROW);
break;
case STM_DEBUGGER:
emit(J, F, OP_DEBUGGER);
break;
default:
cexp(J, F, stm);
emit(J, F, OP_POP);
break;
}
}
static void cstmlist(JF, js_Ast *list)
{
while (list) {
cstm(J, F, list->a);
list = list->b;
}
}
static void cfundecs(JF, js_Ast *list)
{
while (list) {
js_Ast *stm = list->a;
if (stm->type == STM_FUNC) {
emitfunction(J, F, OP_CLOSURE, newfun(J, stm->a, stm->b, stm->c));
emitname(J, F, OP_FUNDEC, stm->a->string);
}
list = list->b;
}
}
static void cvardecs(JF, js_Ast *node)
{
if (node->type == EXP_VAR) {
emitname(J, F, OP_VARDEC, node->a->string);
} else if (node->type != EXP_FUNC && node->type != STM_FUNC) {
if (node->a) cvardecs(J, F, node->a);
if (node->b) cvardecs(J, F, node->b);
if (node->c) cvardecs(J, F, node->c);
if (node->d) cvardecs(J, F, node->d);
}
}
static void cfunbody(JF, js_Ast *name, js_Ast *params, js_Ast *body)
{
if (name) {
emitfunction(J, F, OP_CLOSURE, F);
emitname(J, F, OP_FUNDEC, name->string);
}
if (body) {
cfundecs(J, F, body);
cvardecs(J, F, body);
cstmlist(J, F, body);
}
if (F->len == 0 || F->code[F->len - 1] != OP_RETURN) {
emit(J, F, OP_UNDEF);
emit(J, F, OP_RETURN);
}
}
static js_Function *newfun(js_State *J, js_Ast *name, js_Ast *params, js_Ast *body)
{
js_Function *F = malloc(sizeof(js_Function));
F->name = name ? name->string : "<anonymous>";
F->numparams = listlen(params);
F->cap = 256;
F->len = 0;
F->code = malloc(F->cap);
F->kcap = 16;
F->klen = 0;
F->klist = malloc(F->kcap * sizeof(js_Value));
F->next = J->fun;
J->fun = F;
cfunbody(J, F, name, params, body);
return F;
}
static void freefun(js_State *J, js_Function *F)
{
free(F->klist);
free(F->code);
free(F);
}
void jsC_freecompile(js_State *J)
{
js_Function *F = J->fun;
while (F) {
js_Function *next = F->next;
freefun(J, F);
F = next;
}
J->fun = NULL;
}
int jsC_error(js_State *J, js_Ast *node, const char *fmt, ...)
{
va_list ap;
fprintf(stderr, "%s:%d: error: ", J->filename, node->line);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fprintf(stderr, "\n");
longjmp(J->jb, 1);
return 0;
}
js_Function *jsC_compile(js_State *J, js_Ast *prog)
{
js_Function *F;
if (setjmp(J->jb)) {
jsC_freecompile(J);
return NULL;
}
F = newfun(J, NULL, NULL, prog);
J->fun = NULL;
return F;
}