int js_instanceof(js_State *J) { js_Object *O, *V; if (!js_iscallable(J, -1)) js_typeerror(J, "instanceof: invalid operand"); if (!js_isobject(J, -2)) return 0; js_getproperty(J, -1, "prototype"); if (!js_isobject(J, -1)) js_typeerror(J, "instanceof: 'prototype' property is not an object"); O = js_toobject(J, -1); js_pop(J, 1); V = js_toobject(J, -2); while (V) { V = V->prototype; if (O == V) return 1; } return 0; }
static void O_defineProperty(js_State *J) { if (!js_isobject(J, 1)) js_typeerror(J, "not an object"); if (!js_isobject(J, 3)) js_typeerror(J, "not an object"); ToPropertyDescriptor(J, js_toobject(J, 1), js_tostring(J, 2), js_toobject(J, 3)); js_copy(J, 1); }
static void O_create(js_State *J) { js_Object *obj; js_Object *proto; js_Object *props; js_Property *ref; if (js_isobject(J, 1)) proto = js_toobject(J, 1); else if (js_isnull(J, 1)) proto = NULL; else js_typeerror(J, "not an object or null"); obj = jsV_newobject(J, JS_COBJECT, proto); js_pushobject(J, obj); if (js_isdefined(J, 2)) { if (!js_isobject(J, 2)) js_typeerror(J, "not an object"); props = js_toobject(J, 2); for (ref = props->head; ref; ref = ref->next) { if (!(ref->atts & JS_DONTENUM)) { if (ref->value.type != JS_TOBJECT) js_typeerror(J, "not an object"); ToPropertyDescriptor(J, obj, ref->name, ref->value.u.object); } } } }
static void jsR_defproperty(js_State *J, js_Object *obj, const char *name, int atts, js_Value *value, js_Object *getter, js_Object *setter) { js_Property *ref; unsigned int k; if (obj->type == JS_CARRAY) if (!strcmp(name, "length")) goto readonly; if (obj->type == JS_CSTRING) { if (!strcmp(name, "length")) goto readonly; if (js_isarrayindex(J, name, &k)) if (js_runeat(J, obj->u.s.string, k)) goto readonly; } if (obj->type == JS_CREGEXP) { if (!strcmp(name, "source")) goto readonly; if (!strcmp(name, "global")) goto readonly; if (!strcmp(name, "ignoreCase")) goto readonly; if (!strcmp(name, "multiline")) goto readonly; if (!strcmp(name, "lastIndex")) goto readonly; } ref = jsV_setproperty(J, obj, name); if (ref) { if (value) { if (!(ref->atts & JS_READONLY)) ref->value = *value; else if (J->strict) js_typeerror(J, "'%s' is read-only", name); } if (getter) { if (!(ref->atts & JS_DONTCONF)) ref->getter = getter; else if (J->strict) js_typeerror(J, "'%s' is non-configurable", name); } if (setter) { if (!(ref->atts & JS_DONTCONF)) ref->setter = setter; else if (J->strict) js_typeerror(J, "'%s' is non-configurable", name); } ref->atts |= atts; } return; readonly: if (J->strict) js_typeerror(J, "'%s' is read-only or non-configurable", name); }
/* ToObject() on a value */ js_Object *jsV_toobject(js_State *J, const js_Value *v) { switch (v->type) { default: case JS_TUNDEFINED: js_typeerror(J, "cannot convert undefined to object"); case JS_TNULL: js_typeerror(J, "cannot convert null to object"); case JS_TBOOLEAN: return jsV_newboolean(J, v->u.boolean); case JS_TNUMBER: return jsV_newnumber(J, v->u.number); case JS_TSTRING: return jsV_newstring(J, v->u.string); case JS_TOBJECT: return v->u.object; } }
static void ToPropertyDescriptor(js_State *J, js_Object *obj, const char *name, js_Object *desc) { int haswritable = 0; int hasvalue = 0; int enumerable = 0; int configurable = 0; int writable = 0; int atts = 0; js_pushobject(J, obj); js_pushobject(J, desc); if (js_hasproperty(J, -1, "writable")) { haswritable = 1; writable = js_toboolean(J, -1); js_pop(J, 1); } if (js_hasproperty(J, -1, "enumerable")) { enumerable = js_toboolean(J, -1); js_pop(J, 1); } if (js_hasproperty(J, -1, "configurable")) { configurable = js_toboolean(J, -1); js_pop(J, 1); } if (js_hasproperty(J, -1, "value")) { hasvalue = 1; js_setproperty(J, -3, name); } if (!writable) atts |= JS_READONLY; if (!enumerable) atts |= JS_DONTENUM; if (!configurable) atts |= JS_DONTCONF; if (js_hasproperty(J, -1, "get")) { if (haswritable || hasvalue) js_typeerror(J, "value/writable and get/set attributes are exclusive"); } else { js_pushundefined(J); } if (js_hasproperty(J, -2, "set")) { if (haswritable || hasvalue) js_typeerror(J, "value/writable and get/set attributes are exclusive"); } else { js_pushundefined(J); } js_defaccessor(J, -4, name, atts); js_pop(J, 2); }
static void Fp_toString(js_State *J) { js_Object *self = js_toobject(J, 0); char *s; unsigned int i, n; if (!js_iscallable(J, 0)) js_typeerror(J, "not a function"); if (self->type == JS_CFUNCTION || self->type == JS_CSCRIPT) { js_Function *F = self->u.f.function; n = strlen("function () { ... }"); n += strlen(F->name); for (i = 0; i < F->numparams; ++i) n += strlen(F->vartab[i]) + 1; s = js_malloc(J, n); strcpy(s, "function "); strcat(s, F->name); strcat(s, "("); for (i = 0; i < F->numparams; ++i) { if (i > 0) strcat(s, ","); strcat(s, F->vartab[i]); } strcat(s, ") { ... }"); if (js_try(J)) { js_free(J, s); js_throw(J); } js_pushstring(J, s); js_free(J, s); js_endtry(J); } else { js_pushliteral(J, "function () { ... }"); } }
static void Ap_filter(js_State *J) { int hasthis = js_gettop(J) >= 3; int k, to, len; if (!js_iscallable(J, 1)) js_typeerror(J, "callback is not a function"); js_newarray(J); to = 0; len = js_getlength(J, 0); for (k = 0; k < len; ++k) { if (js_hasindex(J, 0, k)) { js_copy(J, 1); if (hasthis) js_copy(J, 2); else js_pushundefined(J); js_copy(J, -3); js_pushnumber(J, k); js_copy(J, 0); js_call(J, 3); if (js_toboolean(J, -1)) { js_pop(J, 1); js_setindex(J, -2, to++); } else { js_pop(J, 2); } } } }
static void Ap_forEach(js_State *J) { int hasthis = js_gettop(J) >= 3; int k, len; if (!js_iscallable(J, 1)) js_typeerror(J, "callback is not a function"); len = js_getlength(J, 0); for (k = 0; k < len; ++k) { if (js_hasindex(J, 0, k)) { js_copy(J, 1); if (hasthis) js_copy(J, 2); else js_pushundefined(J); js_copy(J, -3); js_pushnumber(J, k); js_copy(J, 0); js_call(J, 3); js_pop(J, 2); } } js_pushundefined(J); }
/* ToPrimitive() on a value */ js_Value jsV_toprimitive(js_State *J, const js_Value *v, int preferred) { js_Value vv; js_Object *obj; if (v->type != JS_TOBJECT) return *v; obj = v->u.object; if (preferred == JS_HNONE) preferred = obj->type == JS_CDATE ? JS_HSTRING : JS_HNUMBER; if (preferred == JS_HSTRING) { if (jsV_toString(J, obj) || jsV_valueOf(J, obj)) { vv = js_tovalue(J, -1); js_pop(J, 1); return vv; } } else { if (jsV_valueOf(J, obj) || jsV_toString(J, obj)) { vv = js_tovalue(J, -1); js_pop(J, 1); return vv; } } js_typeerror(J, "cannot convert object to primitive"); }
void js_call(js_State *J, int n) { js_Object *obj; int savebot; if (!js_iscallable(J, -n-2)) js_typeerror(J, "called object is not a function"); obj = js_toobject(J, -n-2); savebot = BOT; BOT = TOP - n - 1; if (obj->type == JS_CFUNCTION) { jsR_pushtrace(J, obj->u.f.function->name, obj->u.f.function->filename, obj->u.f.function->line); if (obj->u.f.function->lightweight) jsR_calllwfunction(J, n, obj->u.f.function, obj->u.f.scope); else jsR_callfunction(J, n, obj->u.f.function, obj->u.f.scope); --J->tracetop; } else if (obj->type == JS_CSCRIPT) { jsR_pushtrace(J, obj->u.f.function->name, obj->u.f.function->filename, obj->u.f.function->line); jsR_callscript(J, n, obj->u.f.function, obj->u.f.scope); --J->tracetop; } else if (obj->type == JS_CCFUNCTION) { jsR_pushtrace(J, obj->u.c.name, "[C]", 0); jsR_callcfunction(J, n, obj->u.c.length, obj->u.c.function); --J->tracetop; } BOT = savebot; }
static void O_getOwnPropertyDescriptor(js_State *J) { js_Object *obj; js_Property *ref; if (!js_isobject(J, 1)) js_typeerror(J, "not an object"); obj = js_toobject(J, 1); ref = jsV_getproperty(J, obj, js_tostring(J, 2)); if (!ref) js_pushundefined(J); else { js_newobject(J); if (!ref->getter && !ref->setter) { js_pushvalue(J, ref->value); js_setproperty(J, -2, "value"); js_pushboolean(J, !(ref->atts & JS_READONLY)); js_setproperty(J, -2, "writable"); } else { if (ref->getter) js_pushobject(J, ref->getter); else js_pushundefined(J); js_setproperty(J, -2, "get"); if (ref->setter) js_pushobject(J, ref->setter); else js_pushundefined(J); js_setproperty(J, -2, "set"); } js_pushboolean(J, !(ref->atts & JS_DONTENUM)); js_setproperty(J, -2, "enumerable"); js_pushboolean(J, !(ref->atts & JS_DONTCONF)); js_setproperty(J, -2, "configurable"); } }
/* ToPrimitive() on a value */ void jsV_toprimitive(js_State *J, js_Value *v, int preferred) { js_Object *obj; if (v->type != JS_TOBJECT) return; obj = v->u.object; if (preferred == JS_HNONE) preferred = obj->type == JS_CDATE ? JS_HSTRING : JS_HNUMBER; if (preferred == JS_HSTRING) { if (jsV_toString(J, obj) || jsV_valueOf(J, obj)) { *v = *js_tovalue(J, -1); js_pop(J, 1); return; } } else { if (jsV_valueOf(J, obj) || jsV_toString(J, obj)) { *v = *js_tovalue(J, -1); js_pop(J, 1); return; } } if (J->strict) js_typeerror(J, "cannot convert object to primitive"); v->type = JS_TLITSTR; v->u.litstr = "[object]"; return; }
static void js_setvar(js_State *J, const char *name) { js_Environment *E = J->E; do { js_Property *ref = jsV_getproperty(J, E->variables, name); if (ref) { if (ref->setter) { js_pushobject(J, ref->setter); js_pushobject(J, E->variables); js_copy(J, -3); js_call(J, 1); js_pop(J, 1); return; } if (!(ref->atts & JS_READONLY)) ref->value = *stackidx(J, -1); else if (J->strict) js_typeerror(J, "'%s' is read-only", name); return; } E = E->outer; } while (E); if (J->strict) js_referenceerror(J, "assignment to undeclared variable '%s'", name); jsR_setproperty(J, J->G, name, stackidx(J, -1)); }
static void O_keys(js_State *J) { js_Object *obj; js_Property *ref; int k; int i; if (!js_isobject(J, 1)) js_typeerror(J, "not an object"); obj = js_toobject(J, 1); js_newarray(J); i = 0; for (ref = obj->head; ref; ref = ref->next) { if (!(ref->atts & JS_DONTENUM)) { js_pushliteral(J, ref->name); js_setindex(J, -2, i++); } } if (obj->type == JS_CSTRING) { for (k = 0; k < obj->u.s.length; ++k) { js_pushnumber(J, k); js_setindex(J, -2, i++); } } }
static void O_preventExtensions(js_State *J) { if (!js_isobject(J, 1)) js_typeerror(J, "not an object"); js_toobject(J, 1)->extensible = 0; js_copy(J, 1); }
js_Regexp *js_toregexp(js_State *J, int idx) { js_Value *v = stackidx(J, idx); if (v->type == JS_TOBJECT && v->u.object->type == JS_CREGEXP) return &v->u.object->u.r; js_typeerror(J, "not a regexp"); }
void *js_touserdata(js_State *J, int idx, const char *tag) { js_Value *v = stackidx(J, idx); if (v->type == JS_TOBJECT && v->u.object->type == JS_CUSERDATA) if (!strcmp(tag, v->u.object->u.user.tag)) return v->u.object->u.user.data; js_typeerror(J, "not a %s", tag); }
static void O_defineProperties(js_State *J) { js_Object *props; js_Property *ref; if (!js_isobject(J, 1)) js_typeerror(J, "not an object"); if (!js_isobject(J, 2)) js_typeerror(J, "not an object"); props = js_toobject(J, 2); for (ref = props->head; ref; ref = ref->next) { if (!(ref->atts & JS_DONTENUM)) { js_pushvalue(J, ref->value); ToPropertyDescriptor(J, js_toobject(J, 1), ref->name, js_toobject(J, -1)); js_pop(J, 1); } } js_copy(J, 1); }
static js_Object *jsR_tofunction(js_State *J, int idx) { js_Value *v = stackidx(J, idx); if (v->type == JS_TUNDEFINED || v->type == JS_TNULL) return NULL; if (v->type == JS_TOBJECT) if (v->u.object->type == JS_CFUNCTION || v->u.object->type == JS_CCFUNCTION) return v->u.object; js_typeerror(J, "not a function"); }
static void O_getPrototypeOf(js_State *J) { js_Object *obj; if (!js_isobject(J, 1)) js_typeerror(J, "not an object"); obj = js_toobject(J, 1); if (obj->prototype) js_pushobject(J, obj->prototype); else js_pushnull(J); }
static void Fp_call(js_State *J) { unsigned int i, top = js_gettop(J); if (!js_iscallable(J, 0)) js_typeerror(J, "not a function"); for (i = 0; i < top; ++i) js_copy(J, i); js_call(J, top - 2); }
static void Ap_reduceRight(js_State *J) { int hasinitial = js_gettop(J) >= 3; int k, len; if (!js_iscallable(J, 1)) js_typeerror(J, "callback is not a function"); len = js_getlength(J, 0); k = len - 1; if (len == 0 && !hasinitial) js_typeerror(J, "no initial value"); /* initial value of accumulator */ if (hasinitial) js_copy(J, 2); else { while (k >= 0) if (js_hasindex(J, 0, k--)) break; if (k < 0) js_typeerror(J, "no initial value"); } while (k >= 0) { if (js_hasindex(J, 0, k)) { js_copy(J, 1); js_pushundefined(J); js_rot(J, 4); /* accumulator on top */ js_rot(J, 4); /* property on top */ js_pushnumber(J, k); js_copy(J, 0); js_call(J, 4); /* calculate new accumulator */ } --k; } /* return accumulator */ }
void js_construct(js_State *J, int n) { js_Object *obj; js_Object *prototype; js_Object *newobj; if (!js_iscallable(J, -n-1)) js_typeerror(J, "called object is not a function"); obj = js_toobject(J, -n-1); /* built-in constructors create their own objects, give them a 'null' this */ if (obj->type == JS_CCFUNCTION && obj->u.c.constructor) { int savebot = BOT; js_pushnull(J); if (n > 0) js_rot(J, n + 1); BOT = TOP - n - 1; jsR_pushtrace(J, obj->u.c.name, "[C]", 0); jsR_callcfunction(J, n, obj->u.c.length, obj->u.c.constructor); --J->tracetop; BOT = savebot; return; } /* extract the function object's prototype property */ js_getproperty(J, -n - 1, "prototype"); if (js_isobject(J, -1)) prototype = js_toobject(J, -1); else prototype = J->Object_prototype; js_pop(J, 1); /* create a new object with above prototype, and shift it into the 'this' slot */ newobj = jsV_newobject(J, JS_COBJECT, prototype); js_pushobject(J, newobj); if (n > 0) js_rot(J, n + 1); /* call the function */ js_call(J, n); /* if result is not an object, return the original object we created */ if (!js_isobject(J, -1)) { js_pop(J, 1); js_pushobject(J, newobj); } }
static void O_freeze(js_State *J) { js_Object *obj; js_Property *ref; if (!js_isobject(J, 1)) js_typeerror(J, "not an object"); obj = js_toobject(J, 1); obj->extensible = 0; for (ref = obj->head; ref; ref = ref->next) ref->atts |= JS_READONLY | JS_DONTCONF; js_copy(J, 1); }
static void Fp_apply(js_State *J) { int i, n; if (!js_iscallable(J, 0)) js_typeerror(J, "not a function"); js_copy(J, 0); js_copy(J, 1); n = js_getlength(J, 2); for (i = 0; i < n; ++i) js_getindex(J, 2, i); js_call(J, n); }
static void O_getOwnPropertyNames(js_State *J) { js_Object *obj; js_Property *ref; int k; int i; if (!js_isobject(J, 1)) js_typeerror(J, "not an object"); obj = js_toobject(J, 1); js_newarray(J); i = 0; for (ref = obj->head; ref; ref = ref->next) { js_pushliteral(J, ref->name); js_setindex(J, -2, i++); } if (obj->type == JS_CARRAY) { js_pushliteral(J, "length"); js_setindex(J, -2, i++); } if (obj->type == JS_CSTRING) { js_pushliteral(J, "length"); js_setindex(J, -2, i++); for (k = 0; k < obj->u.s.length; ++k) { js_pushnumber(J, k); js_setindex(J, -2, i++); } } if (obj->type == JS_CREGEXP) { js_pushliteral(J, "source"); js_setindex(J, -2, i++); js_pushliteral(J, "global"); js_setindex(J, -2, i++); js_pushliteral(J, "ignoreCase"); js_setindex(J, -2, i++); js_pushliteral(J, "multiline"); js_setindex(J, -2, i++); js_pushliteral(J, "lastIndex"); js_setindex(J, -2, i++); } }
static int js_delvar(js_State *J, const char *name) { js_Environment *E = J->E; do { js_Property *ref = jsV_getownproperty(J, E->variables, name); if (ref) { if (ref->atts & JS_DONTCONF) { if (J->strict) js_typeerror(J, "'%s' is non-configurable", name); return 0; } jsV_delproperty(J, E->variables, name); return 1; } E = E->outer; } while (E); return jsR_delproperty(J, J->G, name); }
const char *jsV_nextiterator(js_State *J, js_Object *io) { int k; if (io->type != JS_CITERATOR) js_typeerror(J, "not an iterator"); while (io->u.iter.head) { js_Iterator *next = io->u.iter.head->next; const char *name = io->u.iter.head->name; js_free(J, io->u.iter.head); io->u.iter.head = next; if (jsV_getproperty(J, io->u.iter.target, name)) return name; if (io->u.iter.target->type == JS_CSTRING) if (js_isarrayindex(J, name, &k) && k < io->u.iter.target->u.s.length) return name; } return NULL; }
js_Property *jsV_setproperty(js_State *J, js_Object *obj, const char *name) { js_Property *result; if (!obj->extensible) { result = lookup(obj->properties, name); if (J->strict && !result) js_typeerror(J, "object is non-extensible"); return result; } obj->properties = insert(J, obj, obj->properties, name, &result); if (!result->prevp) { result->prevp = obj->tailp; *obj->tailp = result; obj->tailp = &result->next; } return result; }