Value wrenListRemoveAt(WrenVM* vm, ObjList* list, int index) { Value removed = list->elements[index]; if (IS_OBJ(removed)) WREN_PIN(vm, AS_OBJ(removed)); // Shift items up. for (int i = index; i < list->count - 1; i++) { list->elements[i] = list->elements[i + 1]; } // If we have too much excess capacity, shrink it. if (list->capacity / LIST_GROW_FACTOR >= list->count) { list->elements = wrenReallocate(vm, list->elements, sizeof(Value) * list->capacity, sizeof(Value) * (list->capacity / LIST_GROW_FACTOR)); list->capacity /= LIST_GROW_FACTOR; } if (IS_OBJ(removed)) WREN_UNPIN(vm); list->count--; return removed; }
void wrenListAdd(WrenVM* vm, ObjList* list, Value value) { if (IS_OBJ(value)) WREN_PIN(vm, AS_OBJ(value)); ensureListCapacity(vm, list, list->count + 1); if (IS_OBJ(value)) WREN_UNPIN(vm); list->elements[list->count++] = value; }
int wrenDefineGlobal(WrenVM* vm, const char* name, size_t length, Value value) { if (vm->globals.count == MAX_GLOBALS) return -2; if (IS_OBJ(value)) WREN_PIN(vm, AS_OBJ(value)); int symbol = wrenSymbolTableAdd(vm, &vm->globalNames, name, length); if (symbol != -1) wrenValueBufferWrite(vm, &vm->globals, value); if (IS_OBJ(value)) WREN_UNPIN(vm); return symbol; }
void wrenDumpValue(Value value) { #if WREN_NAN_TAGGING if (IS_NUM(value)) { printf("%.14g", AS_NUM(value)); } else if (IS_OBJ(value)) { dumpObject(AS_OBJ(value)); } else { switch (GET_TAG(value)) { case TAG_FALSE: printf("false"); break; case TAG_NAN: printf("NaN"); break; case TAG_NULL: printf("null"); break; case TAG_TRUE: printf("true"); break; case TAG_UNDEFINED: UNREACHABLE(); } } #else switch (value.type) { case VAL_FALSE: printf("false"); break; case VAL_NULL: printf("null"); break; case VAL_NUM: printf("%.14g", AS_NUM(value)); break; case VAL_TRUE: printf("true"); break; case VAL_OBJ: dumpObject(AS_OBJ(value)); break; case VAL_UNDEFINED: UNREACHABLE(); } #endif }
void wrenPrintValue(Value value) { #if WREN_NAN_TAGGING if (IS_NUM(value)) { printf("%.14g", AS_NUM(value)); } else if (IS_OBJ(value)) { printObject(AS_OBJ(value)); } else { switch (GET_TAG(value)) { case TAG_FALSE: printf("false"); break; case TAG_NAN: printf("NaN"); break; case TAG_NULL: printf("null"); break; case TAG_TRUE: printf("true"); break; } } #else switch (value.type) { case VAL_FALSE: printf("false"); break; case VAL_NULL: printf("null"); break; case VAL_NUM: printf("%.14g", AS_NUM(value)); break; case VAL_TRUE: printf("true"); break; case VAL_OBJ: { printObject(AS_OBJ(value)); } } #endif }
ObjClass* wrenGetClass(WrenVM* vm, Value value) { #if WREN_NAN_TAGGING if (IS_NUM(value)) return vm->numClass; if (IS_OBJ(value)) return getObjectClass(vm, AS_OBJ(value)); switch (GET_TAG(value)) { case TAG_FALSE: return vm->boolClass; case TAG_NAN: return vm->numClass; case TAG_NULL: return vm->nullClass; case TAG_TRUE: return vm->boolClass; } #else switch (value.type) { case VAL_FALSE: return vm->boolClass; case VAL_NULL: return vm->nullClass; case VAL_NUM: return vm->numClass; case VAL_TRUE: return vm->boolClass; case VAL_OBJ: return getObjectClass(vm, value.obj); } #endif return NULL; // Unreachable. }
// Debug an object with extra verbosity, displaying non-enumerable properties. void fh_debug_verbose(FILE *stream, js_val *val, int indent) { switch (val->type) { case T_BOOLEAN: fprintf(stream, "Boolean: (%s)", !val->boolean.val ? "false" : "true"); break; case T_NUMBER: debug_num(stream, val); break; case T_STRING: cfprintf(stream, ANSI_YELLOW, "String: '%s'", val->string.ptr); break; case T_NULL: cfprintf(stream, ANSI_GRAY, "null"); break; case T_UNDEF: cfprintf(stream, ANSI_GRAY, "undefined"); break; case T_OBJECT: if (IS_ARR(val)) fprintf(stream, "Array: "); else if (IS_FUNC(val)) cfprintf(stream, ANSI_BLUE, "Function: "); else fprintf(stream, "Object: "); break; } if (IS_OBJ(val)) debug_obj(stream, val, indent, true); fprintf(stream, "\n"); }
void wrenListInsert(WrenVM* vm, ObjList* list, Value value, int index) { if (IS_OBJ(value)) WREN_PIN(vm, AS_OBJ(value)); ensureListCapacity(vm, list, list->count + 1); if (IS_OBJ(value)) WREN_UNPIN(vm); // Shift items down. for (int i = list->count; i > index; i--) { list->elements[i] = list->elements[i - 1]; } list->elements[index] = value; list->count++; }
// Error.prototype.toString() js_val * error_proto_to_string(js_val *instance, js_args *args, eval_state *state) { if (!IS_OBJ(instance)) fh_error(state, E_TYPE, "Error.prototype.toString called on a non-object"); js_val *name_prop = fh_get_proto(instance, "name"); js_val *msg_prop = fh_get_proto(instance, "message"); js_val *name = IS_UNDEF(name_prop) ? JSSTR("Error") : TO_STR(name_prop); js_val *msg = IS_UNDEF(msg_prop) ? JSSTR("") : TO_STR(msg_prop); if (strlen(name->string.ptr) == 0) return msg; if (strlen(msg->string.ptr) == 0) return name; return JSSTR(fh_str_concat(fh_str_concat(name->string.ptr, ": "), msg->string.ptr)); }
static void ffi_ptr_add(VMState *state, CallInfo *info) { VM_ASSERT(info->args_len == 1, "wrong arity: expected 1, got %i", info->args_len); Object *pointer_base = state->shared->vcache.pointer_base; Object *thisptr = OBJ_OR_NULL(load_arg(state->frame, info->this_arg)); VM_ASSERT(thisptr && thisptr->parent == pointer_base, "internal error"); PointerObject *thisptr_obj = (PointerObject*) thisptr; void *ptr = (void*) thisptr_obj->ptr; int elemsize = 1; Value target_type = OBJECT_LOOKUP(thisptr, target_type); if (!IS_NULL(target_type)) { VM_ASSERT(IS_OBJ(target_type), "target type must be ffi type"); Value sizeof_val = OBJECT_LOOKUP(AS_OBJ(target_type), sizeof); VM_ASSERT(IS_INT(sizeof_val), "internal error: sizeof wrong type or undefined"); elemsize = AS_INT(sizeof_val); }
static void fh_gc_mark(js_val *val) { if (!val || val->marked) return; val->marked = true; fh_gc_mark(val->proto); if (IS_OBJ(val)) { fh_gc_mark(val->object.primitive); fh_gc_mark(val->object.bound_this); fh_gc_mark(val->object.scope); fh_gc_mark(val->object.instance); fh_gc_mark(val->object.parent); } if (val->map) { js_prop *prop; OBJ_ITER(val, prop) { if (prop->ptr && !prop->circular) fh_gc_mark(prop->ptr); } }
// extern __thread jmp_buf jumpBuf; int VM::call(Value A, int nEffArgs, Value *regs, Stack *stack) { Vector<RetInfo> retInfo; // only used if FAST_CALL if (!(IS_O_TYPE(A, O_FUNC) || IS_CF(A) || IS_O_TYPE(A, O_CFUNC))) { return -1; } regs = stack->maybeGrow(regs, 256); int nExpectedArgs = IS_O_TYPE(A, O_FUNC) ? ((Func *)GET_OBJ(A))->proto->nArgs : NARGS_CFUNC; nEffArgs = prepareStackForCall(regs, nExpectedArgs, nEffArgs, gc); if (IS_CF(A) || IS_O_TYPE(A, O_CFUNC)) { if (IS_CF(A)) { tfunc f = GET_CF(A); *regs = f(this, CFunc::CFUNC_CALL, 0, regs, nEffArgs); } else { ((CFunc *) GET_OBJ(A))->call(this, regs, nEffArgs); } return 0; } unsigned code = 0; Value B; Value *ptrC; Func *activeFunc = (Func *) GET_OBJ(A); unsigned *pc = (unsigned *) activeFunc->proto->code.buf(); static void *dispatch[] = { #define _(name) &&name #include "opcodes.inc" #undef _ }; assert(sizeof(dispatch)/sizeof(dispatch[0]) == N_OPCODES); copyUpvals(activeFunc, regs); STEP; JMP: pc += OD(code); STEP; JT: if (!IS_FALSE(*ptrC)) { pc += OD(code); } STEP; JF: if ( IS_FALSE(*ptrC)) { pc += OD(code); } STEP; JLT: if (lessThan(A, B)) { pc += OSC(code); } STEP; JNIS: if (A != B) { pc += OSC(code); } STEP; FOR: A = *(ptrC + 1); B = *(ptrC + 2); if (!IS_NUM(A) || !IS_NUM(B)) { goto error; } // E_FOR_NOT_NUMBER *ptrC = B; if (!(GET_NUM(B) < GET_NUM(A))) { pc += OD(code); } STEP; LOOP: { const double counter = GET_NUM(*ptrC) + 1; if (counter < GET_NUM(*(ptrC+1))) { pc += OD(code); } *ptrC = VAL_NUM(counter); STEP; } FUNC: assert(IS_PROTO(A)); *ptrC = VAL_OBJ(Func::alloc(gc, PROTO(A), regs + 256, regs, OB(code))); STEP; // index, A[B] GETI: *ptrC = types->type(A)->indexGet(A, B); if (*ptrC == VERR) { goto error; } STEP; GETF: *ptrC = types->type(A)->fieldGet(A, B); if (*ptrC == VERR) { goto error; } STEP; SETI: if (!types->type(*ptrC)->indexSet(*ptrC, A, B)) { goto error; } STEP; SETF: if (!types->type(*ptrC)->fieldSet(*ptrC, A, B)) { goto error; } STEP; /* const int oa = OA(code); const int ob = OB(code); int top = max(oa, ob) + 1; top = max(top, activeFunc->proto->localsTop); Value *base = regs + top; printf("top %d\n", top); base[0] = A; base[1] = B; int cPos = ptrC - regs; DO_CALL(v, 2, regs, base, stack); regs[cPos] = base[0]; break; if (*ptrC == VERR) { goto error; } */ GETS: *ptrC = getSlice(gc, A, B, regs[OB(code)+1]); if (*ptrC==VERR) { goto error; } STEP; SETS: if (setSlice(*ptrC, A, regs[OA(code)+1], B)) { goto error; } STEP; RET: { regs[0] = A; Value *root = stack->base; gc->maybeCollect(root, regs - root + 1); #if FAST_CALL if (!retInfo.size()) { return 0; } RetInfo *ri = retInfo.top(); pc = ri->pc; regs = stack->base + ri->base; activeFunc = ri->func; retInfo.pop(); copyUpvals(activeFunc, regs); STEP; #else return 0; #endif } CALL: { if (!IS_OBJ(A) && !IS_CF(A)) { goto error; } // E_CALL_NOT_FUNC int nEffArgs = OSB(code); assert(nEffArgs != 0); Value *base = ptrC; #if FAST_CALL if (IS_O_TYPE(A, O_FUNC)) { Func *f = (Func *) GET_OBJ(A); Proto *proto = f->proto; prepareStackForCall(base, proto->nArgs, nEffArgs, gc); RetInfo *ret = retInfo.push(); ret->pc = pc; ret->base = regs - stack->base; ret->func = activeFunc; regs = stack->maybeGrow(base, 256); copyUpvals(f, regs); pc = proto->code.buf(); activeFunc = f; } else { #endif int ret = DO_CALL(A, nEffArgs, regs, base, stack); if (ret) { goto error; } #if FAST_CALL } #endif STEP; } MOVEUP: { const int slot = regs + 256 - ptrC; activeFunc->setUp(slot, A); } MOVE_R: *ptrC = A; STEP; MOVE_I: *ptrC = VAL_NUM(OD(code)); STEP; MOVE_V: { int id = OA(code); *ptrC = id == CONST_NIL ? VNIL : id == CONST_EMPTY_STRING ? EMPTY_STRING : id == CONST_EMPTY_ARRAY ? VAL_OBJ(emptyArray->copy(gc)) : VAL_OBJ(emptyMap->copy(gc)); STEP; } MOVE_C: { Value v = *pc | (((u64) *(pc+1)) << 32); pc += 2; if (IS_ARRAY(v)) { v = VAL_OBJ(ARRAY(v)->copy(gc)); } else if (IS_MAP(v)) { v = VAL_OBJ(MAP(v)->copy(gc)); } *ptrC = v; STEP; } LEN: *ptrC = VAL_NUM(len(A)); STEP; NOTL: *ptrC = IS_FALSE(A) ? TRUE : FALSE; STEP; // notb: *ptrC = IS_INT(A)? VAL_INT(~getInteger(A)):ERROR(E_WRONG_TYPE); STEP; ADD: *ptrC = doAdd(gc, A, B); if (*ptrC == VERR) { goto error; } STEP; SUB: *ptrC = BINOP(-, A, B); STEP; MUL: *ptrC = BINOP(*, A, B); STEP; DIV: *ptrC = BINOP(/, A, B); STEP; MOD: *ptrC = doMod(A, B); if (*ptrC == VERR) { goto error; } STEP; POW: *ptrC = doPow(A, B); if (*ptrC == VERR) { goto error; } STEP; AND: *ptrC = BITOP(&, A, B); STEP; OR: *ptrC = BITOP(|, A, B); STEP; XOR: *ptrC = BITOP(^, A, B); STEP; SHL_RR: ERR(!IS_NUM(B), E_WRONG_TYPE); *ptrC = doSHL(A, (int)GET_NUM(B)); STEP; SHR_RR: ERR(!IS_NUM(B), E_WRONG_TYPE); *ptrC = doSHR(A, (int)GET_NUM(B)); STEP; SHL_RI: *ptrC = doSHL(A, OSB(code)); STEP; SHR_RI: *ptrC = doSHR(A, OSB(code)); STEP; EQ: *ptrC = equals(A, B) ? TRUE : FALSE; STEP; NEQ: *ptrC = !equals(A, B) ? TRUE : FALSE; STEP; IS: *ptrC = A == B ? TRUE : FALSE; STEP; NIS: *ptrC = A != B ? TRUE : FALSE; STEP; LT: *ptrC = lessThan(A, B) ? TRUE : FALSE; STEP; LE: *ptrC = (equals(A, B) || lessThan(A, B)) ? TRUE : FALSE; STEP; error: return pc - (unsigned *) activeFunc->proto->code.buf(); }
void wrenMarkValue(WrenVM* vm, Value value) { if (!IS_OBJ(value)) return; wrenMarkObj(vm, AS_OBJ(value)); }