#include "common/cs_varint.h"
#include "common/str_util.h"
#include "mjs_bcode.h"
#include "mjs_builtin.h"
#include "mjs_core.h"
#include "mjs_exec.h"
#include "mjs_ffi.h"
#include "mjs_internal.h"
#include "mjs_object.h"
#include "mjs_primitive.h"
#include "mjs_string.h"
#include "mjs_util.h"
#ifndef MJS_OBJECT_ARENA_SIZE
#define MJS_OBJECT_ARENA_SIZE 20
#endif
#ifndef MJS_PROPERTY_ARENA_SIZE
#define MJS_PROPERTY_ARENA_SIZE 20
#endif
#ifndef MJS_FUNC_FFI_ARENA_SIZE
#define MJS_FUNC_FFI_ARENA_SIZE 20
#endif
#ifndef MJS_OBJECT_ARENA_INC_SIZE
#define MJS_OBJECT_ARENA_INC_SIZE 10
#endif
#ifndef MJS_PROPERTY_ARENA_INC_SIZE
#define MJS_PROPERTY_ARENA_INC_SIZE 10
#endif
#ifndef MJS_FUNC_FFI_ARENA_INC_SIZE
#define MJS_FUNC_FFI_ARENA_INC_SIZE 10
#endif
void mjs_destroy(struct mjs *mjs) {
{
int parts_cnt = mjs_bcode_parts_cnt(mjs);
int i;
for (i = 0; i < parts_cnt; i++) {
struct mjs_bcode_part *bp = mjs_bcode_part_get(mjs, i);
if (!bp->in_rom) {
free((void *) bp->data.p);
}
}
}
mbuf_free(&mjs->bcode_gen);
mbuf_free(&mjs->bcode_parts);
mbuf_free(&mjs->stack);
mbuf_free(&mjs->call_stack);
mbuf_free(&mjs->arg_stack);
mbuf_free(&mjs->owned_strings);
mbuf_free(&mjs->foreign_strings);
mbuf_free(&mjs->owned_values);
mbuf_free(&mjs->scopes);
mbuf_free(&mjs->loop_addresses);
mbuf_free(&mjs->json_visited_stack);
free(mjs->error_msg);
free(mjs->stack_trace);
mjs_ffi_args_free_list(mjs);
gc_arena_destroy(mjs, &mjs->object_arena);
gc_arena_destroy(mjs, &mjs->property_arena);
gc_arena_destroy(mjs, &mjs->ffi_sig_arena);
free(mjs);
}
struct mjs *mjs_create(void) {
mjs_val_t global_object;
struct mjs *mjs = calloc(1, sizeof(*mjs));
mbuf_init(&mjs->stack, 0);
mbuf_init(&mjs->call_stack, 0);
mbuf_init(&mjs->arg_stack, 0);
mbuf_init(&mjs->owned_strings, 0);
mbuf_init(&mjs->foreign_strings, 0);
mbuf_init(&mjs->bcode_gen, 0);
mbuf_init(&mjs->bcode_parts, 0);
mbuf_init(&mjs->owned_values, 0);
mbuf_init(&mjs->scopes, 0);
mbuf_init(&mjs->loop_addresses, 0);
mbuf_init(&mjs->json_visited_stack, 0);
mjs->bcode_len = 0;
{
char z = 0;
mbuf_append(&mjs->owned_strings, &z, 1);
}
gc_arena_init(&mjs->object_arena, sizeof(struct mjs_object),
MJS_OBJECT_ARENA_SIZE, MJS_OBJECT_ARENA_INC_SIZE);
gc_arena_init(&mjs->property_arena, sizeof(struct mjs_property),
MJS_PROPERTY_ARENA_SIZE, MJS_PROPERTY_ARENA_INC_SIZE);
gc_arena_init(&mjs->ffi_sig_arena, sizeof(struct mjs_ffi_sig),
MJS_FUNC_FFI_ARENA_SIZE, MJS_FUNC_FFI_ARENA_INC_SIZE);
mjs->ffi_sig_arena.destructor = mjs_ffi_sig_destructor;
global_object = mjs_mk_object(mjs);
mjs_init_builtin(mjs, global_object);
mjs_set_ffi_resolver(mjs, dlsym);
push_mjs_val(&mjs->scopes, global_object);
mjs->vals.this_obj = MJS_UNDEFINED;
mjs->vals.dataview_proto = MJS_UNDEFINED;
return mjs;
}
mjs_err_t mjs_set_errorf(struct mjs *mjs, mjs_err_t err, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
free(mjs->error_msg);
mjs->error_msg = NULL;
mjs->error = err;
if (fmt != NULL) {
mg_avprintf(&mjs->error_msg, 0, fmt, ap);
}
va_end(ap);
return err;
}
mjs_err_t mjs_prepend_errorf(struct mjs *mjs, mjs_err_t err, const char *fmt,
...) {
char *old_error_msg = mjs->error_msg;
char *new_error_msg = NULL;
va_list ap;
va_start(ap, fmt);
assert(err != MJS_OK);
mjs->error_msg = NULL;
if (mjs->error == MJS_OK) {
mjs->error = err;
}
mg_avprintf(&new_error_msg, 0, fmt, ap);
va_end(ap);
if (old_error_msg != NULL) {
mg_asprintf(&mjs->error_msg, 0, "%s: %s", new_error_msg, old_error_msg);
free(new_error_msg);
free(old_error_msg);
} else {
mjs->error_msg = new_error_msg;
}
return err;
}
void mjs_print_error(struct mjs *mjs, FILE *fp, const char *msg,
int print_stack_trace) {
if (print_stack_trace && mjs->stack_trace != NULL) {
fprintf(fp, "%s", mjs->stack_trace);
}
if (msg == NULL) {
msg = "MJS error";
}
fprintf(fp, "%s: %s\n", msg, mjs_strerror(mjs, mjs->error));
}
MJS_PRIVATE void mjs_die(struct mjs *mjs) {
mjs_val_t msg_v = MJS_UNDEFINED;
const char *msg = NULL;
size_t msg_len = 0;
if (!mjs_check_arg(mjs, 0, "msg", MJS_TYPE_STRING, &msg_v)) {
goto clean;
}
msg = mjs_get_string(mjs, &msg_v, &msg_len);
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, "%.*s", (int) msg_len, msg);
clean:
mjs_return(mjs, MJS_UNDEFINED);
}
const char *mjs_strerror(struct mjs *mjs, enum mjs_err err) {
const char *err_names[] = {
"NO_ERROR", "SYNTAX_ERROR", "REFERENCE_ERROR",
"TYPE_ERROR", "OUT_OF_MEMORY", "INTERNAL_ERROR",
"NOT_IMPLEMENTED", "FILE_OPEN_ERROR", "BAD_ARGUMENTS"};
return mjs->error_msg == NULL || mjs->error_msg[0] == '\0' ? err_names[err]
: mjs->error_msg;
}
MJS_PRIVATE size_t mjs_get_func_addr(mjs_val_t v) {
return v & ~MJS_TAG_MASK;
}
MJS_PRIVATE enum mjs_type mjs_get_type(mjs_val_t v) {
int tag;
if (mjs_is_number(v)) {
return MJS_TYPE_NUMBER;
}
tag = (v & MJS_TAG_MASK) >> 48;
switch (tag) {
case MJS_TAG_FOREIGN >> 48:
return MJS_TYPE_FOREIGN;
case MJS_TAG_UNDEFINED >> 48:
return MJS_TYPE_UNDEFINED;
case MJS_TAG_OBJECT >> 48:
return MJS_TYPE_OBJECT_GENERIC;
case MJS_TAG_ARRAY >> 48:
return MJS_TYPE_OBJECT_ARRAY;
case MJS_TAG_FUNCTION >> 48:
return MJS_TYPE_OBJECT_FUNCTION;
case MJS_TAG_STRING_I >> 48:
case MJS_TAG_STRING_O >> 48:
case MJS_TAG_STRING_F >> 48:
case MJS_TAG_STRING_D >> 48:
case MJS_TAG_STRING_5 >> 48:
return MJS_TYPE_STRING;
case MJS_TAG_BOOLEAN >> 48:
return MJS_TYPE_BOOLEAN;
case MJS_TAG_NULL >> 48:
return MJS_TYPE_NULL;
default:
abort();
return MJS_TYPE_UNDEFINED;
}
}
mjs_val_t mjs_get_global(struct mjs *mjs) {
return *vptr(&mjs->scopes, 0);
}
static void mjs_append_stack_trace_line(struct mjs *mjs, size_t offset) {
if (offset != MJS_BCODE_OFFSET_EXIT) {
const char *filename = mjs_get_bcode_filename_by_offset(mjs, offset);
int line_no = mjs_get_lineno_by_offset(mjs, offset);
char *new_line = NULL;
const char *fmt = " at %s:%d\n";
if (filename == NULL) {
fprintf(stderr,
"ERROR during stack trace generation: wrong bcode offset %d\n",
(int) offset);
filename = "<unknown-filename>";
}
mg_asprintf(&new_line, 0, fmt, filename, line_no);
if (mjs->stack_trace != NULL) {
char *old = mjs->stack_trace;
mg_asprintf(&mjs->stack_trace, 0, "%s%s", mjs->stack_trace, new_line);
free(old);
free(new_line);
} else {
mjs->stack_trace = new_line;
}
}
}
MJS_PRIVATE void mjs_gen_stack_trace(struct mjs *mjs, size_t offset) {
mjs_append_stack_trace_line(mjs, offset);
while (mjs->call_stack.len >=
sizeof(mjs_val_t) * CALL_STACK_FRAME_ITEMS_CNT) {
int i;
offset = mjs_get_int(
mjs, *vptr(&mjs->call_stack, -1 - CALL_STACK_FRAME_ITEM_RETURN_ADDR));
for (i = 0; i < CALL_STACK_FRAME_ITEMS_CNT; i++) {
mjs_pop_val(&mjs->call_stack);
}
mjs_append_stack_trace_line(mjs, offset);
}
}
void mjs_own(struct mjs *mjs, mjs_val_t *v) {
mbuf_append(&mjs->owned_values, &v, sizeof(v));
}
int mjs_disown(struct mjs *mjs, mjs_val_t *v) {
mjs_val_t **vp = (mjs_val_t **) (mjs->owned_values.buf +
mjs->owned_values.len - sizeof(v));
for (; (char *) vp >= mjs->owned_values.buf; vp--) {
if (*vp == v) {
*vp = *(mjs_val_t **) (mjs->owned_values.buf + mjs->owned_values.len -
sizeof(v));
mjs->owned_values.len -= sizeof(v);
return 1;
}
}
return 0;
}
MJS_PRIVATE int mjs_getretvalpos(struct mjs *mjs) {
int pos;
mjs_val_t *ppos = vptr(&mjs->call_stack, -1);
assert(ppos != NULL && mjs_is_number(*ppos));
pos = mjs_get_int(mjs, *ppos) - 1;
assert(pos < (int) mjs_stack_size(&mjs->stack));
return pos;
}
int mjs_nargs(struct mjs *mjs) {
int top = mjs_stack_size(&mjs->stack);
int pos = mjs_getretvalpos(mjs) + 1;
return pos > 0 && pos < top ? top - pos : 0;
}
mjs_val_t mjs_arg(struct mjs *mjs, int arg_index) {
mjs_val_t res = MJS_UNDEFINED;
int top = mjs_stack_size(&mjs->stack);
int pos = mjs_getretvalpos(mjs) + 1;
if (pos > 0 && pos + arg_index < top) {
res = *vptr(&mjs->stack, pos + arg_index);
}
return res;
}
void mjs_return(struct mjs *mjs, mjs_val_t v) {
int pos = mjs_getretvalpos(mjs);
mjs->stack.len = sizeof(mjs_val_t) * pos;
mjs_push(mjs, v);
}
MJS_PRIVATE mjs_val_t vtop(struct mbuf *m) {
size_t size = mjs_stack_size(m);
return size > 0 ? *vptr(m, size - 1) : MJS_UNDEFINED;
}
MJS_PRIVATE size_t mjs_stack_size(const struct mbuf *m) {
return m->len / sizeof(mjs_val_t);
}
MJS_PRIVATE mjs_val_t *vptr(struct mbuf *m, int idx) {
int size = mjs_stack_size(m);
if (idx < 0) idx = size + idx;
return idx >= 0 && idx < size ? &((mjs_val_t *) m->buf)[idx] : NULL;
}
MJS_PRIVATE mjs_val_t mjs_pop(struct mjs *mjs) {
if (mjs->stack.len == 0) {
mjs_set_errorf(mjs, MJS_INTERNAL_ERROR, "stack underflow");
return MJS_UNDEFINED;
} else {
return mjs_pop_val(&mjs->stack);
}
}
MJS_PRIVATE void push_mjs_val(struct mbuf *m, mjs_val_t v) {
mbuf_append(m, &v, sizeof(v));
}
MJS_PRIVATE mjs_val_t mjs_pop_val(struct mbuf *m) {
mjs_val_t v = MJS_UNDEFINED;
assert(m->len >= sizeof(v));
if (m->len >= sizeof(v)) {
memcpy(&v, m->buf + m->len - sizeof(v), sizeof(v));
m->len -= sizeof(v);
}
return v;
}
MJS_PRIVATE void mjs_push(struct mjs *mjs, mjs_val_t v) {
push_mjs_val(&mjs->stack, v);
}
void mjs_set_generate_jsc(struct mjs *mjs, int generate_jsc) {
mjs->generate_jsc = generate_jsc;
}