| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063 |
- #include "jsi.h"
- #include "utf.h"
- #include <assert.h>
- static void jsR_run(js_State *J, js_Function *F);
- /* Push values on stack */
- #define STACK (J->stack)
- #define TOP (J->top)
- #define BOT (J->bot)
- static void js_trystackoverflow(js_State *J)
- {
- STACK[TOP].t.type = JS_TLITSTR;
- STACK[TOP].u.litstr = "exception stack overflow";
- ++TOP;
- js_throw(J);
- }
- static void js_stackoverflow(js_State *J)
- {
- STACK[TOP].t.type = JS_TLITSTR;
- STACK[TOP].u.litstr = "stack overflow";
- ++TOP;
- js_throw(J);
- }
- static void js_outofmemory(js_State *J)
- {
- STACK[TOP].t.type = JS_TLITSTR;
- STACK[TOP].u.litstr = "out of memory";
- ++TOP;
- js_throw(J);
- }
- void *js_malloc(js_State *J, int size)
- {
- void *ptr = J->alloc(J->actx, NULL, size);
- if (!ptr)
- js_outofmemory(J);
- return ptr;
- }
- void *js_realloc(js_State *J, void *ptr, int size)
- {
- ptr = J->alloc(J->actx, ptr, size);
- if (!ptr)
- js_outofmemory(J);
- return ptr;
- }
- char *js_strdup(js_State *J, const char *s)
- {
- int n = strlen(s) + 1;
- char *p = js_malloc(J, n);
- memcpy(p, s, n);
- return p;
- }
- void js_free(js_State *J, void *ptr)
- {
- J->alloc(J->actx, ptr, 0);
- }
- js_String *jsV_newmemstring(js_State *J, const char *s, int n)
- {
- js_String *v = js_malloc(J, soffsetof(js_String, p) + n + 1);
- memcpy(v->p, s, n);
- v->p[n] = 0;
- v->gcmark = 0;
- v->gcnext = J->gcstr;
- J->gcstr = v;
- ++J->gccounter;
- return v;
- }
- #define CHECKSTACK(n) if (TOP + n >= JS_STACKSIZE) js_stackoverflow(J)
- void js_pushvalue(js_State *J, js_Value v)
- {
- CHECKSTACK(1);
- STACK[TOP] = v;
- ++TOP;
- }
- void js_pushundefined(js_State *J)
- {
- CHECKSTACK(1);
- STACK[TOP].t.type = JS_TUNDEFINED;
- ++TOP;
- }
- void js_pushnull(js_State *J)
- {
- CHECKSTACK(1);
- STACK[TOP].t.type = JS_TNULL;
- ++TOP;
- }
- void js_pushboolean(js_State *J, int v)
- {
- CHECKSTACK(1);
- STACK[TOP].t.type = JS_TBOOLEAN;
- STACK[TOP].u.boolean = !!v;
- ++TOP;
- }
- void js_pushnumber(js_State *J, double v)
- {
- CHECKSTACK(1);
- STACK[TOP].t.type = JS_TNUMBER;
- STACK[TOP].u.number = v;
- ++TOP;
- }
- void js_pushstring(js_State *J, const char *v)
- {
- size_t n = strlen(v);
- if (n > JS_STRLIMIT)
- js_rangeerror(J, "invalid string length");
- CHECKSTACK(1);
- if (n <= soffsetof(js_Value, t.type)) {
- char *s = STACK[TOP].u.shrstr;
- while (n--) *s++ = *v++;
- *s = 0;
- STACK[TOP].t.type = JS_TSHRSTR;
- } else {
- STACK[TOP].t.type = JS_TMEMSTR;
- STACK[TOP].u.memstr = jsV_newmemstring(J, v, n);
- }
- ++TOP;
- }
- void js_pushlstring(js_State *J, const char *v, int n)
- {
- if (n > JS_STRLIMIT)
- js_rangeerror(J, "invalid string length");
- CHECKSTACK(1);
- if (n <= soffsetof(js_Value, t.type)) {
- char *s = STACK[TOP].u.shrstr;
- while (n--) *s++ = *v++;
- *s = 0;
- STACK[TOP].t.type = JS_TSHRSTR;
- } else {
- STACK[TOP].t.type = JS_TMEMSTR;
- STACK[TOP].u.memstr = jsV_newmemstring(J, v, n);
- }
- ++TOP;
- }
- void js_pushliteral(js_State *J, const char *v)
- {
- CHECKSTACK(1);
- STACK[TOP].t.type = JS_TLITSTR;
- STACK[TOP].u.litstr = v;
- ++TOP;
- }
- void js_pushobject(js_State *J, js_Object *v)
- {
- CHECKSTACK(1);
- STACK[TOP].t.type = JS_TOBJECT;
- STACK[TOP].u.object = v;
- ++TOP;
- }
- void js_pushglobal(js_State *J)
- {
- js_pushobject(J, J->G);
- }
- void js_currentfunction(js_State *J)
- {
- CHECKSTACK(1);
- if (BOT > 0)
- STACK[TOP] = STACK[BOT-1];
- else
- STACK[TOP].t.type = JS_TUNDEFINED;
- ++TOP;
- }
- void *js_currentfunctiondata(js_State *J)
- {
- if (BOT > 0)
- return STACK[BOT-1].u.object->u.c.data;
- return NULL;
- }
- /* Read values from stack */
- static js_Value *stackidx(js_State *J, int idx)
- {
- static js_Value undefined = { { {0}, JS_TUNDEFINED } };
- idx = idx < 0 ? TOP + idx : BOT + idx;
- if (idx < 0 || idx >= TOP)
- return &undefined;
- return STACK + idx;
- }
- js_Value *js_tovalue(js_State *J, int idx)
- {
- return stackidx(J, idx);
- }
- int js_isdefined(js_State *J, int idx) { return stackidx(J, idx)->t.type != JS_TUNDEFINED; }
- int js_isundefined(js_State *J, int idx) { return stackidx(J, idx)->t.type == JS_TUNDEFINED; }
- int js_isnull(js_State *J, int idx) { return stackidx(J, idx)->t.type == JS_TNULL; }
- int js_isboolean(js_State *J, int idx) { return stackidx(J, idx)->t.type == JS_TBOOLEAN; }
- int js_isnumber(js_State *J, int idx) { return stackidx(J, idx)->t.type == JS_TNUMBER; }
- int js_isstring(js_State *J, int idx) { enum js_Type t = stackidx(J, idx)->t.type; return t == JS_TSHRSTR || t == JS_TLITSTR || t == JS_TMEMSTR; }
- int js_isprimitive(js_State *J, int idx) { return stackidx(J, idx)->t.type != JS_TOBJECT; }
- int js_isobject(js_State *J, int idx) { return stackidx(J, idx)->t.type == JS_TOBJECT; }
- int js_iscoercible(js_State *J, int idx) { js_Value *v = stackidx(J, idx); return v->t.type != JS_TUNDEFINED && v->t.type != JS_TNULL; }
- int js_iscallable(js_State *J, int idx)
- {
- js_Value *v = stackidx(J, idx);
- if (v->t.type == JS_TOBJECT)
- return v->u.object->type == JS_CFUNCTION ||
- v->u.object->type == JS_CSCRIPT ||
- v->u.object->type == JS_CCFUNCTION;
- return 0;
- }
- int js_isarray(js_State *J, int idx)
- {
- js_Value *v = stackidx(J, idx);
- return v->t.type == JS_TOBJECT && v->u.object->type == JS_CARRAY;
- }
- int js_isregexp(js_State *J, int idx)
- {
- js_Value *v = stackidx(J, idx);
- return v->t.type == JS_TOBJECT && v->u.object->type == JS_CREGEXP;
- }
- int js_isuserdata(js_State *J, int idx, const char *tag)
- {
- js_Value *v = stackidx(J, idx);
- if (v->t.type == JS_TOBJECT && v->u.object->type == JS_CUSERDATA)
- return !strcmp(tag, v->u.object->u.user.tag);
- return 0;
- }
- int js_iserror(js_State *J, int idx)
- {
- js_Value *v = stackidx(J, idx);
- return v->t.type == JS_TOBJECT && v->u.object->type == JS_CERROR;
- }
- const char *js_typeof(js_State *J, int idx)
- {
- js_Value *v = stackidx(J, idx);
- switch (v->t.type) {
- default:
- case JS_TSHRSTR: return "string";
- case JS_TUNDEFINED: return "undefined";
- case JS_TNULL: return "object";
- case JS_TBOOLEAN: return "boolean";
- case JS_TNUMBER: return "number";
- case JS_TLITSTR: return "string";
- case JS_TMEMSTR: return "string";
- case JS_TOBJECT:
- if (v->u.object->type == JS_CFUNCTION || v->u.object->type == JS_CCFUNCTION)
- return "function";
- return "object";
- }
- }
- int js_type(js_State *J, int idx)
- {
- js_Value *v = stackidx(J, idx);
- switch (v->t.type) {
- default:
- case JS_TSHRSTR: return JS_ISSTRING;
- case JS_TUNDEFINED: return JS_ISUNDEFINED;
- case JS_TNULL: return JS_ISNULL;
- case JS_TBOOLEAN: return JS_ISBOOLEAN;
- case JS_TNUMBER: return JS_ISNUMBER;
- case JS_TLITSTR: return JS_ISSTRING;
- case JS_TMEMSTR: return JS_ISSTRING;
- case JS_TOBJECT:
- if (v->u.object->type == JS_CFUNCTION || v->u.object->type == JS_CCFUNCTION)
- return JS_ISFUNCTION;
- return JS_ISOBJECT;
- }
- }
- int js_toboolean(js_State *J, int idx)
- {
- return jsV_toboolean(J, stackidx(J, idx));
- }
- double js_tonumber(js_State *J, int idx)
- {
- return jsV_tonumber(J, stackidx(J, idx));
- }
- int js_tointeger(js_State *J, int idx)
- {
- return jsV_numbertointeger(jsV_tonumber(J, stackidx(J, idx)));
- }
- int js_toint32(js_State *J, int idx)
- {
- return jsV_numbertoint32(jsV_tonumber(J, stackidx(J, idx)));
- }
- unsigned int js_touint32(js_State *J, int idx)
- {
- return jsV_numbertouint32(jsV_tonumber(J, stackidx(J, idx)));
- }
- short js_toint16(js_State *J, int idx)
- {
- return jsV_numbertoint16(jsV_tonumber(J, stackidx(J, idx)));
- }
- unsigned short js_touint16(js_State *J, int idx)
- {
- return jsV_numbertouint16(jsV_tonumber(J, stackidx(J, idx)));
- }
- const char *js_tostring(js_State *J, int idx)
- {
- return jsV_tostring(J, stackidx(J, idx));
- }
- js_Object *js_toobject(js_State *J, int idx)
- {
- return jsV_toobject(J, stackidx(J, idx));
- }
- void js_toprimitive(js_State *J, int idx, int hint)
- {
- jsV_toprimitive(J, stackidx(J, idx), hint);
- }
- js_Regexp *js_toregexp(js_State *J, int idx)
- {
- js_Value *v = stackidx(J, idx);
- if (v->t.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->t.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 js_Object *jsR_tofunction(js_State *J, int idx)
- {
- js_Value *v = stackidx(J, idx);
- if (v->t.type == JS_TUNDEFINED || v->t.type == JS_TNULL)
- return NULL;
- if (v->t.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");
- }
- /* Stack manipulation */
- int js_gettop(js_State *J)
- {
- return TOP - BOT;
- }
- void js_pop(js_State *J, int n)
- {
- TOP -= n;
- if (TOP < BOT) {
- TOP = BOT;
- js_error(J, "stack underflow!");
- }
- }
- void js_remove(js_State *J, int idx)
- {
- idx = idx < 0 ? TOP + idx : BOT + idx;
- if (idx < BOT || idx >= TOP)
- js_error(J, "stack error!");
- for (;idx < TOP - 1; ++idx)
- STACK[idx] = STACK[idx+1];
- --TOP;
- }
- void js_insert(js_State *J, int idx)
- {
- js_error(J, "not implemented yet");
- }
- void js_replace(js_State* J, int idx)
- {
- idx = idx < 0 ? TOP + idx : BOT + idx;
- if (idx < BOT || idx >= TOP)
- js_error(J, "stack error!");
- STACK[idx] = STACK[--TOP];
- }
- void js_copy(js_State *J, int idx)
- {
- CHECKSTACK(1);
- STACK[TOP] = *stackidx(J, idx);
- ++TOP;
- }
- void js_dup(js_State *J)
- {
- CHECKSTACK(1);
- STACK[TOP] = STACK[TOP-1];
- ++TOP;
- }
- void js_dup2(js_State *J)
- {
- CHECKSTACK(2);
- STACK[TOP] = STACK[TOP-2];
- STACK[TOP+1] = STACK[TOP-1];
- TOP += 2;
- }
- void js_rot2(js_State *J)
- {
- /* A B -> B A */
- js_Value tmp = STACK[TOP-1]; /* A B (B) */
- STACK[TOP-1] = STACK[TOP-2]; /* A A */
- STACK[TOP-2] = tmp; /* B A */
- }
- void js_rot3(js_State *J)
- {
- /* A B C -> C A B */
- js_Value tmp = STACK[TOP-1]; /* A B C (C) */
- STACK[TOP-1] = STACK[TOP-2]; /* A B B */
- STACK[TOP-2] = STACK[TOP-3]; /* A A B */
- STACK[TOP-3] = tmp; /* C A B */
- }
- void js_rot4(js_State *J)
- {
- /* A B C D -> D A B C */
- js_Value tmp = STACK[TOP-1]; /* A B C D (D) */
- STACK[TOP-1] = STACK[TOP-2]; /* A B C C */
- STACK[TOP-2] = STACK[TOP-3]; /* A B B C */
- STACK[TOP-3] = STACK[TOP-4]; /* A A B C */
- STACK[TOP-4] = tmp; /* D A B C */
- }
- void js_rot2pop1(js_State *J)
- {
- /* A B -> B */
- STACK[TOP-2] = STACK[TOP-1];
- --TOP;
- }
- void js_rot3pop2(js_State *J)
- {
- /* A B C -> C */
- STACK[TOP-3] = STACK[TOP-1];
- TOP -= 2;
- }
- void js_rot(js_State *J, int n)
- {
- int i;
- js_Value tmp = STACK[TOP-1];
- for (i = 1; i < n; ++i)
- STACK[TOP-i] = STACK[TOP-i-1];
- STACK[TOP-i] = tmp;
- }
- /* Property access that takes care of attributes and getters/setters */
- int js_isarrayindex(js_State *J, const char *p, int *idx)
- {
- int n = 0;
- /* check for empty string */
- if (p[0] == 0)
- return 0;
- /* check for '0' and integers with leading zero */
- if (p[0] == '0')
- return (p[1] == 0) ? *idx = 0, 1 : 0;
- while (*p) {
- int c = *p++;
- if (c >= '0' && c <= '9') {
- if (n >= INT_MAX / 10)
- return 0;
- n = n * 10 + (c - '0');
- } else {
- return 0;
- }
- }
- return *idx = n, 1;
- }
- static void js_pushrune(js_State *J, Rune rune)
- {
- char buf[UTFmax + 1];
- if (rune >= 0) {
- buf[runetochar(buf, &rune)] = 0;
- js_pushstring(J, buf);
- } else {
- js_pushundefined(J);
- }
- }
- void jsR_unflattenarray(js_State *J, js_Object *obj) {
- if (obj->type == JS_CARRAY && obj->u.a.simple) {
- js_Property *ref;
- int i;
- char name[32];
- if (js_try(J)) {
- obj->properties = NULL;
- js_throw(J);
- }
- for (i = 0; i < obj->u.a.flat_length; ++i) {
- js_itoa(name, i);
- ref = jsV_setproperty(J, obj, name);
- ref->value = obj->u.a.array[i];
- }
- js_free(J, obj->u.a.array);
- obj->u.a.simple = 0;
- obj->u.a.flat_length = 0;
- obj->u.a.flat_capacity = 0;
- obj->u.a.array = NULL;
- js_endtry(J);
- }
- }
- static int jsR_hasproperty(js_State *J, js_Object *obj, const char *name)
- {
- js_Property *ref;
- int k;
- if (obj->type == JS_CARRAY) {
- if (!strcmp(name, "length")) {
- js_pushnumber(J, obj->u.a.length);
- return 1;
- }
- if (obj->u.a.simple) {
- if (js_isarrayindex(J, name, &k)) {
- if (k >= 0 && k < obj->u.a.flat_length) {
- js_pushvalue(J, obj->u.a.array[k]);
- return 1;
- }
- return 0;
- }
- }
- }
- else if (obj->type == JS_CSTRING) {
- if (!strcmp(name, "length")) {
- js_pushnumber(J, obj->u.s.length);
- return 1;
- }
- if (js_isarrayindex(J, name, &k)) {
- if (k >= 0 && k < obj->u.s.length) {
- js_pushrune(J, js_runeat(J, obj->u.s.string, k));
- return 1;
- }
- }
- }
- else if (obj->type == JS_CREGEXP) {
- if (!strcmp(name, "source")) {
- js_pushstring(J, obj->u.r.source);
- return 1;
- }
- if (!strcmp(name, "global")) {
- js_pushboolean(J, obj->u.r.flags & JS_REGEXP_G);
- return 1;
- }
- if (!strcmp(name, "ignoreCase")) {
- js_pushboolean(J, obj->u.r.flags & JS_REGEXP_I);
- return 1;
- }
- if (!strcmp(name, "multiline")) {
- js_pushboolean(J, obj->u.r.flags & JS_REGEXP_M);
- return 1;
- }
- if (!strcmp(name, "lastIndex")) {
- js_pushnumber(J, obj->u.r.last);
- return 1;
- }
- }
- else if (obj->type == JS_CUSERDATA) {
- if (obj->u.user.has && obj->u.user.has(J, obj->u.user.data, name))
- return 1;
- }
- ref = jsV_getproperty(J, obj, name);
- if (ref) {
- if (ref->getter) {
- js_pushobject(J, ref->getter);
- js_pushobject(J, obj);
- js_call(J, 0);
- } else {
- js_pushvalue(J, ref->value);
- }
- return 1;
- }
- return 0;
- }
- static void jsR_getproperty(js_State *J, js_Object *obj, const char *name)
- {
- if (!jsR_hasproperty(J, obj, name))
- js_pushundefined(J);
- }
- static int jsR_hasindex(js_State *J, js_Object *obj, int k)
- {
- char buf[32];
- if (obj->type == JS_CARRAY && obj->u.a.simple) {
- if (k >= 0 && k < obj->u.a.flat_length) {
- js_pushvalue(J, obj->u.a.array[k]);
- return 1;
- }
- return 0;
- }
- return jsR_hasproperty(J, obj, js_itoa(buf, k));
- }
- static void jsR_getindex(js_State *J, js_Object *obj, int k)
- {
- if (!jsR_hasindex(J, obj, k))
- js_pushundefined(J);
- }
- static void jsR_setarrayindex(js_State *J, js_Object *obj, int k, js_Value *value)
- {
- int newlen = k + 1;
- assert(obj->u.a.simple);
- assert(k >= 0);
- if (newlen > JS_ARRAYLIMIT)
- js_rangeerror(J, "array too large");
- if (newlen > obj->u.a.flat_length) {
- assert(newlen == obj->u.a.flat_length + 1);
- if (newlen > obj->u.a.flat_capacity) {
- int newcap = obj->u.a.flat_capacity;
- if (newcap == 0)
- newcap = 8;
- while (newcap < newlen)
- newcap <<= 1;
- obj->u.a.array = js_realloc(J, obj->u.a.array, newcap * sizeof(js_Value));
- obj->u.a.flat_capacity = newcap;
- }
- obj->u.a.flat_length = newlen;
- }
- if (newlen > obj->u.a.length)
- obj->u.a.length = newlen;
- obj->u.a.array[k] = *value;
- }
- static void jsR_setproperty(js_State *J, js_Object *obj, const char *name, int transient)
- {
- js_Value *value = stackidx(J, -1);
- js_Property *ref;
- int k;
- int own;
- if (obj->type == JS_CARRAY) {
- if (!strcmp(name, "length")) {
- double rawlen = jsV_tonumber(J, value);
- int newlen = jsV_numbertointeger(rawlen);
- if (newlen != rawlen || newlen < 0)
- js_rangeerror(J, "invalid array length");
- if (newlen > JS_ARRAYLIMIT)
- js_rangeerror(J, "array too large");
- if (obj->u.a.simple) {
- obj->u.a.length = newlen;
- if (newlen <= obj->u.a.flat_length)
- obj->u.a.flat_length = newlen;
- } else {
- jsV_resizearray(J, obj, newlen);
- }
- return;
- }
- if (js_isarrayindex(J, name, &k)) {
- if (obj->u.a.simple) {
- if (k >= 0 && k <= obj->u.a.flat_length) {
- jsR_setarrayindex(J, obj, k, value);
- } else {
- jsR_unflattenarray(J, obj);
- if (obj->u.a.length < k + 1)
- obj->u.a.length = k + 1;
- }
- } else {
- if (obj->u.a.length < k + 1)
- obj->u.a.length = k + 1;
- }
- }
- }
- else if (obj->type == JS_CSTRING) {
- if (!strcmp(name, "length"))
- goto readonly;
- if (js_isarrayindex(J, name, &k))
- if (k >= 0 && k < obj->u.s.length)
- goto readonly;
- }
- else 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")) {
- obj->u.r.last = jsV_tointeger(J, value);
- return;
- }
- }
- else if (obj->type == JS_CUSERDATA) {
- if (obj->u.user.put && obj->u.user.put(J, obj->u.user.data, name))
- return;
- }
- /* First try to find a setter in prototype chain */
- ref = jsV_getpropertyx(J, obj, name, &own);
- if (ref) {
- if (ref->setter) {
- js_pushobject(J, ref->setter);
- js_pushobject(J, obj);
- js_pushvalue(J, *value);
- js_call(J, 1);
- js_pop(J, 1);
- return;
- } else {
- if (J->strict)
- if (ref->getter)
- js_typeerror(J, "setting property '%s' that only has a getter", name);
- if (ref->atts & JS_READONLY)
- goto readonly;
- }
- }
- /* Property not found on this object, so create one */
- if (!ref || !own) {
- if (transient) {
- if (J->strict)
- js_typeerror(J, "cannot create property '%s' on transient object", name);
- return;
- }
- ref = jsV_setproperty(J, obj, name);
- }
- if (ref) {
- if (!(ref->atts & JS_READONLY))
- ref->value = *value;
- else
- goto readonly;
- }
- return;
- readonly:
- if (J->strict)
- js_typeerror(J, "'%s' is read-only", name);
- }
- static void jsR_setindex(js_State *J, js_Object *obj, int k, int transient)
- {
- char buf[32];
- if (obj->type == JS_CARRAY && obj->u.a.simple && k >= 0 && k <= obj->u.a.flat_length) {
- jsR_setarrayindex(J, obj, k, stackidx(J, -1));
- } else {
- jsR_setproperty(J, obj, js_itoa(buf, k), transient);
- }
- }
- static void jsR_defproperty(js_State *J, js_Object *obj, const char *name,
- int atts, js_Value *value, js_Object *getter, js_Object *setter,
- int throw)
- {
- js_Property *ref;
- int k;
- if (obj->type == JS_CARRAY) {
- if (!strcmp(name, "length"))
- goto readonly;
- if (obj->u.a.simple)
- jsR_unflattenarray(J, obj);
- }
- else if (obj->type == JS_CSTRING) {
- if (!strcmp(name, "length"))
- goto readonly;
- if (js_isarrayindex(J, name, &k))
- if (k >= 0 && k < obj->u.s.length)
- goto readonly;
- }
- else 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;
- }
- else if (obj->type == JS_CUSERDATA) {
- if (obj->u.user.put && obj->u.user.put(J, obj->u.user.data, name))
- return;
- }
- 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 || throw)
- js_typeerror(J, "'%s' is read-only or non-configurable", name);
- }
- static int jsR_delproperty(js_State *J, js_Object *obj, const char *name)
- {
- js_Property *ref;
- int k;
- if (obj->type == JS_CARRAY) {
- if (!strcmp(name, "length"))
- goto dontconf;
- if (obj->u.a.simple)
- jsR_unflattenarray(J, obj);
- }
- else if (obj->type == JS_CSTRING) {
- if (!strcmp(name, "length"))
- goto dontconf;
- if (js_isarrayindex(J, name, &k))
- if (k >= 0 && k < obj->u.s.length)
- goto dontconf;
- }
- else if (obj->type == JS_CREGEXP) {
- if (!strcmp(name, "source")) goto dontconf;
- if (!strcmp(name, "global")) goto dontconf;
- if (!strcmp(name, "ignoreCase")) goto dontconf;
- if (!strcmp(name, "multiline")) goto dontconf;
- if (!strcmp(name, "lastIndex")) goto dontconf;
- }
- else if (obj->type == JS_CUSERDATA) {
- if (obj->u.user.delete && obj->u.user.delete(J, obj->u.user.data, name))
- return 1;
- }
- ref = jsV_getownproperty(J, obj, name);
- if (ref) {
- if (ref->atts & JS_DONTCONF)
- goto dontconf;
- jsV_delproperty(J, obj, name);
- }
- return 1;
- dontconf:
- if (J->strict)
- js_typeerror(J, "'%s' is non-configurable", name);
- return 0;
- }
- static void jsR_delindex(js_State *J, js_Object *obj, int k)
- {
- char buf[32];
- /* Allow deleting last element of a simple array without unflattening */
- if (obj->type == JS_CARRAY && obj->u.a.simple && k == obj->u.a.flat_length - 1)
- obj->u.a.flat_length = k;
- else
- jsR_delproperty(J, obj, js_itoa(buf, k));
- }
- /* Registry, global and object property accessors */
- const char *js_ref(js_State *J)
- {
- js_Value *v = stackidx(J, -1);
- const char *s;
- char buf[32];
- switch (v->t.type) {
- case JS_TUNDEFINED: s = "_Undefined"; break;
- case JS_TNULL: s = "_Null"; break;
- case JS_TBOOLEAN:
- s = v->u.boolean ? "_True" : "_False";
- break;
- case JS_TOBJECT:
- sprintf(buf, "%p", (void*)v->u.object);
- s = js_intern(J, buf);
- break;
- default:
- sprintf(buf, "%d", J->nextref++);
- s = js_intern(J, buf);
- break;
- }
- js_setregistry(J, s);
- return s;
- }
- void js_unref(js_State *J, const char *ref)
- {
- js_delregistry(J, ref);
- }
- void js_getregistry(js_State *J, const char *name)
- {
- jsR_getproperty(J, J->R, name);
- }
- void js_setregistry(js_State *J, const char *name)
- {
- jsR_setproperty(J, J->R, name, 0);
- js_pop(J, 1);
- }
- void js_delregistry(js_State *J, const char *name)
- {
- jsR_delproperty(J, J->R, name);
- }
- void js_getglobal(js_State *J, const char *name)
- {
- jsR_getproperty(J, J->G, name);
- }
- void js_setglobal(js_State *J, const char *name)
- {
- jsR_setproperty(J, J->G, name, 0);
- js_pop(J, 1);
- }
- void js_defglobal(js_State *J, const char *name, int atts)
- {
- jsR_defproperty(J, J->G, name, atts, stackidx(J, -1), NULL, NULL, 0);
- js_pop(J, 1);
- }
- void js_delglobal(js_State *J, const char *name)
- {
- jsR_delproperty(J, J->G, name);
- }
- void js_getproperty(js_State *J, int idx, const char *name)
- {
- jsR_getproperty(J, js_toobject(J, idx), name);
- }
- void js_setproperty(js_State *J, int idx, const char *name)
- {
- jsR_setproperty(J, js_toobject(J, idx), name, !js_isobject(J, idx));
- js_pop(J, 1);
- }
- void js_defproperty(js_State *J, int idx, const char *name, int atts)
- {
- jsR_defproperty(J, js_toobject(J, idx), name, atts, stackidx(J, -1), NULL, NULL, 1);
- js_pop(J, 1);
- }
- void js_delproperty(js_State *J, int idx, const char *name)
- {
- jsR_delproperty(J, js_toobject(J, idx), name);
- }
- void js_defaccessor(js_State *J, int idx, const char *name, int atts)
- {
- jsR_defproperty(J, js_toobject(J, idx), name, atts, NULL, jsR_tofunction(J, -2), jsR_tofunction(J, -1), 1);
- js_pop(J, 2);
- }
- int js_hasproperty(js_State *J, int idx, const char *name)
- {
- return jsR_hasproperty(J, js_toobject(J, idx), name);
- }
- void js_getindex(js_State *J, int idx, int i)
- {
- jsR_getindex(J, js_toobject(J, idx), i);
- }
- int js_hasindex(js_State *J, int idx, int i)
- {
- return jsR_hasindex(J, js_toobject(J, idx), i);
- }
- void js_setindex(js_State *J, int idx, int i)
- {
- jsR_setindex(J, js_toobject(J, idx), i, !js_isobject(J, idx));
- js_pop(J, 1);
- }
- void js_delindex(js_State *J, int idx, int i)
- {
- jsR_delindex(J, js_toobject(J, idx), i);
- }
- /* Iterator */
- void js_pushiterator(js_State *J, int idx, int own)
- {
- js_pushobject(J, jsV_newiterator(J, js_toobject(J, idx), own));
- }
- const char *js_nextiterator(js_State *J, int idx)
- {
- return jsV_nextiterator(J, js_toobject(J, idx));
- }
- /* Environment records */
- js_Environment *jsR_newenvironment(js_State *J, js_Object *vars, js_Environment *outer)
- {
- js_Environment *E = js_malloc(J, sizeof *E);
- E->gcmark = 0;
- E->gcnext = J->gcenv;
- J->gcenv = E;
- ++J->gccounter;
- E->outer = outer;
- E->variables = vars;
- return E;
- }
- static void js_initvar(js_State *J, const char *name, int idx)
- {
- jsR_defproperty(J, J->E->variables, name, JS_DONTENUM | JS_DONTCONF, stackidx(J, idx), NULL, NULL, 0);
- }
- static int js_hasvar(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->getter) {
- js_pushobject(J, ref->getter);
- js_pushobject(J, E->variables);
- js_call(J, 0);
- } else {
- js_pushvalue(J, ref->value);
- }
- return 1;
- }
- E = E->outer;
- } while (E);
- return 0;
- }
- 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, 0);
- }
- 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);
- }
- /* Function calls */
- static void jsR_savescope(js_State *J, js_Environment *newE)
- {
- if (J->envtop + 1 >= JS_ENVLIMIT)
- js_stackoverflow(J);
- J->envstack[J->envtop++] = J->E;
- J->E = newE;
- }
- static void jsR_restorescope(js_State *J)
- {
- J->E = J->envstack[--J->envtop];
- }
- static void jsR_calllwfunction(js_State *J, int n, js_Function *F, js_Environment *scope)
- {
- js_Value v;
- int i;
- jsR_savescope(J, scope);
- if (n > F->numparams) {
- js_pop(J, n - F->numparams);
- n = F->numparams;
- }
- for (i = n; i < F->varlen; ++i)
- js_pushundefined(J);
- jsR_run(J, F);
- v = *stackidx(J, -1);
- TOP = --BOT; /* clear stack */
- js_pushvalue(J, v);
- jsR_restorescope(J);
- }
- static void jsR_callfunction(js_State *J, int n, js_Function *F, js_Environment *scope)
- {
- js_Value v;
- int i;
- scope = jsR_newenvironment(J, jsV_newobject(J, JS_COBJECT, NULL), scope);
- jsR_savescope(J, scope);
- if (F->arguments) {
- js_newarguments(J);
- if (!J->strict) {
- js_currentfunction(J);
- js_defproperty(J, -2, "callee", JS_DONTENUM);
- }
- js_pushnumber(J, n);
- js_defproperty(J, -2, "length", JS_DONTENUM);
- for (i = 0; i < n; ++i) {
- js_copy(J, i + 1);
- js_setindex(J, -2, i);
- }
- js_initvar(J, "arguments", -1);
- js_pop(J, 1);
- }
- for (i = 0; i < n && i < F->numparams; ++i)
- js_initvar(J, F->vartab[i], i + 1);
- js_pop(J, n);
- for (; i < F->varlen; ++i) {
- js_pushundefined(J);
- js_initvar(J, F->vartab[i], -1);
- js_pop(J, 1);
- }
- jsR_run(J, F);
- v = *stackidx(J, -1);
- TOP = --BOT; /* clear stack */
- js_pushvalue(J, v);
- jsR_restorescope(J);
- }
- static void jsR_callscript(js_State *J, int n, js_Function *F, js_Environment *scope)
- {
- js_Value v;
- int i;
- if (scope)
- jsR_savescope(J, scope);
- /* scripts take no arguments */
- js_pop(J, n);
- for (i = 0; i < F->varlen; ++i) {
- /* Bug 701886: don't redefine existing vars in eval/scripts */
- if (!js_hasvar(J, F->vartab[i])) {
- js_pushundefined(J);
- js_initvar(J, F->vartab[i], -1);
- js_pop(J, 1);
- }
- }
- jsR_run(J, F);
- v = *stackidx(J, -1);
- TOP = --BOT; /* clear stack */
- js_pushvalue(J, v);
- if (scope)
- jsR_restorescope(J);
- }
- static void jsR_callcfunction(js_State *J, int n, int min, js_CFunction F)
- {
- int save_top;
- int i;
- js_Value v;
- for (i = n; i < min; ++i)
- js_pushundefined(J);
- save_top = TOP;
- F(J);
- if (TOP > save_top) {
- v = *stackidx(J, -1);
- TOP = --BOT; /* clear stack */
- js_pushvalue(J, v);
- } else {
- TOP = --BOT; /* clear stack */
- js_pushundefined(J);
- }
- }
- static void jsR_pushtrace(js_State *J, const char *name, const char *file, int line)
- {
- if (J->tracetop + 1 == JS_ENVLIMIT)
- js_error(J, "call stack overflow");
- ++J->tracetop;
- J->trace[J->tracetop].name = name;
- J->trace[J->tracetop].file = file;
- J->trace[J->tracetop].line = line;
- }
- void js_call(js_State *J, int n)
- {
- js_Object *obj;
- int savebot;
- if (n < 0)
- js_rangeerror(J, "number of arguments cannot be negative");
- if (!js_iscallable(J, -n-2))
- js_typeerror(J, "%s is not callable", js_typeof(J, -n-2));
- 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, "native", 0);
- jsR_callcfunction(J, n, obj->u.c.length, obj->u.c.function);
- --J->tracetop;
- }
- BOT = savebot;
- }
- 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, "%s is not callable", js_typeof(J, -n-1));
- 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, "native", 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);
- /* and save a copy to return */
- js_pushobject(J, newobj);
- js_rot(J, n + 3);
- /* 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);
- } else {
- js_rot2pop1(J);
- }
- }
- void js_eval(js_State *J)
- {
- if (!js_isstring(J, -1))
- return;
- js_loadeval(J, "(eval)", js_tostring(J, -1));
- js_rot2pop1(J);
- js_copy(J, 0); /* copy 'this' */
- js_call(J, 0);
- }
- int js_pconstruct(js_State *J, int n)
- {
- int savetop = TOP - n - 2;
- if (js_try(J)) {
- /* clean up the stack to only hold the error object */
- STACK[savetop] = STACK[TOP-1];
- TOP = savetop + 1;
- return 1;
- }
- js_construct(J, n);
- js_endtry(J);
- return 0;
- }
- int js_pcall(js_State *J, int n)
- {
- int savetop = TOP - n - 2;
- if (js_try(J)) {
- /* clean up the stack to only hold the error object */
- STACK[savetop] = STACK[TOP-1];
- TOP = savetop + 1;
- return 1;
- }
- js_call(J, n);
- js_endtry(J);
- return 0;
- }
- /* Exceptions */
- void *js_savetrypc(js_State *J, js_Instruction *pc)
- {
- if (J->trytop == JS_TRYLIMIT)
- js_trystackoverflow(J);
- J->trybuf[J->trytop].E = J->E;
- J->trybuf[J->trytop].envtop = J->envtop;
- J->trybuf[J->trytop].tracetop = J->tracetop;
- J->trybuf[J->trytop].top = J->top;
- J->trybuf[J->trytop].bot = J->bot;
- J->trybuf[J->trytop].strict = J->strict;
- J->trybuf[J->trytop].pc = pc;
- return J->trybuf[J->trytop++].buf;
- }
- void *js_savetry(js_State *J)
- {
- if (J->trytop == JS_TRYLIMIT)
- js_trystackoverflow(J);
- J->trybuf[J->trytop].E = J->E;
- J->trybuf[J->trytop].envtop = J->envtop;
- J->trybuf[J->trytop].tracetop = J->tracetop;
- J->trybuf[J->trytop].top = J->top;
- J->trybuf[J->trytop].bot = J->bot;
- J->trybuf[J->trytop].strict = J->strict;
- J->trybuf[J->trytop].pc = NULL;
- return J->trybuf[J->trytop++].buf;
- }
- void js_endtry(js_State *J)
- {
- if (J->trytop == 0)
- js_error(J, "endtry: exception stack underflow");
- --J->trytop;
- }
- void js_throw(js_State *J)
- {
- if (J->trytop > 0) {
- js_Value v = *stackidx(J, -1);
- --J->trytop;
- J->E = J->trybuf[J->trytop].E;
- J->envtop = J->trybuf[J->trytop].envtop;
- J->tracetop = J->trybuf[J->trytop].tracetop;
- J->top = J->trybuf[J->trytop].top;
- J->bot = J->trybuf[J->trytop].bot;
- J->strict = J->trybuf[J->trytop].strict;
- js_pushvalue(J, v);
- longjmp(J->trybuf[J->trytop].buf, 1);
- }
- if (J->panic)
- J->panic(J);
- abort();
- }
- /* Main interpreter loop */
- static void js_dumpvalue(js_State *J, js_Value v)
- {
- switch (v.t.type) {
- case JS_TUNDEFINED: printf("undefined"); break;
- case JS_TNULL: printf("null"); break;
- case JS_TBOOLEAN: printf(v.u.boolean ? "true" : "false"); break;
- case JS_TNUMBER: printf("%.9g", v.u.number); break;
- case JS_TSHRSTR: printf("'%s'", v.u.shrstr); break;
- case JS_TLITSTR: printf("'%s'", v.u.litstr); break;
- case JS_TMEMSTR: printf("'%s'", v.u.memstr->p); break;
- case JS_TOBJECT:
- if (v.u.object == J->G) {
- printf("[Global]");
- break;
- }
- switch (v.u.object->type) {
- case JS_COBJECT: printf("[Object %p]", (void*)v.u.object); break;
- case JS_CARRAY: printf("[Array %p]", (void*)v.u.object); break;
- case JS_CFUNCTION:
- printf("[Function %p, %s, %s:%d]",
- (void*)v.u.object,
- v.u.object->u.f.function->name,
- v.u.object->u.f.function->filename,
- v.u.object->u.f.function->line);
- break;
- case JS_CSCRIPT: printf("[Script %s]", v.u.object->u.f.function->filename); break;
- case JS_CCFUNCTION: printf("[CFunction %s]", v.u.object->u.c.name); break;
- case JS_CBOOLEAN: printf("[Boolean %d]", v.u.object->u.boolean); break;
- case JS_CNUMBER: printf("[Number %g]", v.u.object->u.number); break;
- case JS_CSTRING: printf("[String'%s']", v.u.object->u.s.string); break;
- case JS_CERROR: printf("[Error]"); break;
- case JS_CARGUMENTS: printf("[Arguments %p]", (void*)v.u.object); break;
- case JS_CITERATOR: printf("[Iterator %p]", (void*)v.u.object); break;
- case JS_CUSERDATA:
- printf("[Userdata %s %p]", v.u.object->u.user.tag, v.u.object->u.user.data);
- break;
- default: printf("[Object %p]", (void*)v.u.object); break;
- }
- break;
- }
- }
- static void js_stacktrace(js_State *J)
- {
- int n;
- printf("stack trace:\n");
- for (n = J->tracetop; n >= 0; --n) {
- const char *name = J->trace[n].name;
- const char *file = J->trace[n].file;
- int line = J->trace[n].line;
- if (line > 0) {
- if (name[0])
- printf("\tat %s (%s:%d)\n", name, file, line);
- else
- printf("\tat %s:%d\n", file, line);
- } else
- printf("\tat %s (%s)\n", name, file);
- }
- }
- static void js_dumpstack(js_State *J)
- {
- int i;
- printf("stack {\n");
- for (i = 0; i < TOP; ++i) {
- putchar(i == BOT ? '>' : ' ');
- printf("%4d: ", i);
- js_dumpvalue(J, STACK[i]);
- putchar('\n');
- }
- printf("}\n");
- }
- void js_trap(js_State *J, int pc)
- {
- js_dumpstack(J);
- js_stacktrace(J);
- }
- static int jsR_isindex(js_State *J, int idx, int *k)
- {
- js_Value *v = stackidx(J, idx);
- if (v->t.type == JS_TNUMBER) {
- *k = v->u.number;
- return *k == v->u.number && *k >= 0;
- }
- return 0;
- }
- static void jsR_run(js_State *J, js_Function *F)
- {
- js_Function **FT = F->funtab;
- const char **VT = F->vartab-1;
- int lightweight = F->lightweight;
- js_Instruction *pcstart = F->code;
- js_Instruction *pc = F->code;
- enum js_OpCode opcode;
- int offset;
- int savestrict;
- const char *str;
- js_Object *obj;
- double x, y;
- unsigned int ux, uy;
- int ix, iy, okay;
- int b;
- int transient;
- savestrict = J->strict;
- J->strict = F->strict;
- #define READSTRING() \
- memcpy(&str, pc, sizeof(str)); \
- pc += sizeof(str) / sizeof(*pc)
- while (1) {
- if (J->gccounter > J->gcthresh)
- js_gc(J, 0);
- J->trace[J->tracetop].line = *pc++;
- opcode = *pc++;
- switch (opcode) {
- case OP_POP: js_pop(J, 1); break;
- case OP_DUP: js_dup(J); break;
- case OP_DUP2: js_dup2(J); break;
- case OP_ROT2: js_rot2(J); break;
- case OP_ROT3: js_rot3(J); break;
- case OP_ROT4: js_rot4(J); break;
- case OP_INTEGER:
- js_pushnumber(J, *pc++ - 32768);
- break;
- case OP_NUMBER:
- memcpy(&x, pc, sizeof(x));
- pc += sizeof(x) / sizeof(*pc);
- js_pushnumber(J, x);
- break;
- case OP_STRING:
- READSTRING();
- js_pushliteral(J, str);
- break;
- case OP_CLOSURE: js_newfunction(J, FT[*pc++], J->E); break;
- case OP_NEWOBJECT: js_newobject(J); break;
- case OP_NEWARRAY: js_newarray(J); break;
- case OP_NEWREGEXP:
- READSTRING();
- js_newregexp(J, str, *pc++);
- break;
- case OP_UNDEF: js_pushundefined(J); break;
- case OP_NULL: js_pushnull(J); break;
- case OP_TRUE: js_pushboolean(J, 1); break;
- case OP_FALSE: js_pushboolean(J, 0); break;
- case OP_THIS:
- if (J->strict) {
- js_copy(J, 0);
- } else {
- if (js_iscoercible(J, 0))
- js_copy(J, 0);
- else
- js_pushglobal(J);
- }
- break;
- case OP_CURRENT:
- js_currentfunction(J);
- break;
- case OP_GETLOCAL:
- if (lightweight) {
- CHECKSTACK(1);
- STACK[TOP++] = STACK[BOT + *pc++];
- } else {
- str = VT[*pc++];
- if (!js_hasvar(J, str))
- js_referenceerror(J, "'%s' is not defined", str);
- }
- break;
- case OP_SETLOCAL:
- if (lightweight) {
- STACK[BOT + *pc++] = STACK[TOP-1];
- } else {
- js_setvar(J, VT[*pc++]);
- }
- break;
- case OP_DELLOCAL:
- if (lightweight) {
- ++pc;
- js_pushboolean(J, 0);
- } else {
- b = js_delvar(J, VT[*pc++]);
- js_pushboolean(J, b);
- }
- break;
- case OP_GETVAR:
- READSTRING();
- if (!js_hasvar(J, str))
- js_referenceerror(J, "'%s' is not defined", str);
- break;
- case OP_HASVAR:
- READSTRING();
- if (!js_hasvar(J, str))
- js_pushundefined(J);
- break;
- case OP_SETVAR:
- READSTRING();
- js_setvar(J, str);
- break;
- case OP_DELVAR:
- READSTRING();
- b = js_delvar(J, str);
- js_pushboolean(J, b);
- break;
- case OP_IN:
- str = js_tostring(J, -2);
- if (!js_isobject(J, -1))
- js_typeerror(J, "operand to 'in' is not an object");
- b = js_hasproperty(J, -1, str);
- js_pop(J, 2 + b);
- js_pushboolean(J, b);
- break;
- case OP_SKIPARRAY:
- js_setlength(J, -1, js_getlength(J, -1) + 1);
- break;
- case OP_INITARRAY:
- js_setindex(J, -2, js_getlength(J, -2));
- break;
- case OP_INITPROP:
- obj = js_toobject(J, -3);
- str = js_tostring(J, -2);
- jsR_setproperty(J, obj, str, 0);
- js_pop(J, 2);
- break;
- case OP_INITGETTER:
- obj = js_toobject(J, -3);
- str = js_tostring(J, -2);
- jsR_defproperty(J, obj, str, 0, NULL, jsR_tofunction(J, -1), NULL, 0);
- js_pop(J, 2);
- break;
- case OP_INITSETTER:
- obj = js_toobject(J, -3);
- str = js_tostring(J, -2);
- jsR_defproperty(J, obj, str, 0, NULL, NULL, jsR_tofunction(J, -1), 0);
- js_pop(J, 2);
- break;
- case OP_GETPROP:
- if (jsR_isindex(J, -1, &ix)) {
- obj = js_toobject(J, -2);
- jsR_getindex(J, obj, ix);
- } else {
- str = js_tostring(J, -1);
- obj = js_toobject(J, -2);
- jsR_getproperty(J, obj, str);
- }
- js_rot3pop2(J);
- break;
- case OP_GETPROP_S:
- READSTRING();
- obj = js_toobject(J, -1);
- jsR_getproperty(J, obj, str);
- js_rot2pop1(J);
- break;
- case OP_SETPROP:
- if (jsR_isindex(J, -2, &ix)) {
- obj = js_toobject(J, -3);
- transient = !js_isobject(J, -3);
- jsR_setindex(J, obj, ix, transient);
- } else {
- str = js_tostring(J, -2);
- obj = js_toobject(J, -3);
- transient = !js_isobject(J, -3);
- jsR_setproperty(J, obj, str, transient);
- }
- js_rot3pop2(J);
- break;
- case OP_SETPROP_S:
- READSTRING();
- obj = js_toobject(J, -2);
- transient = !js_isobject(J, -2);
- jsR_setproperty(J, obj, str, transient);
- js_rot2pop1(J);
- break;
- case OP_DELPROP:
- str = js_tostring(J, -1);
- obj = js_toobject(J, -2);
- b = jsR_delproperty(J, obj, str);
- js_pop(J, 2);
- js_pushboolean(J, b);
- break;
- case OP_DELPROP_S:
- READSTRING();
- obj = js_toobject(J, -1);
- b = jsR_delproperty(J, obj, str);
- js_pop(J, 1);
- js_pushboolean(J, b);
- break;
- case OP_ITERATOR:
- if (js_iscoercible(J, -1)) {
- obj = jsV_newiterator(J, js_toobject(J, -1), 0);
- js_pop(J, 1);
- js_pushobject(J, obj);
- }
- break;
- case OP_NEXTITER:
- if (js_isobject(J, -1)) {
- obj = js_toobject(J, -1);
- str = jsV_nextiterator(J, obj);
- if (str) {
- js_pushstring(J, str);
- js_pushboolean(J, 1);
- } else {
- js_pop(J, 1);
- js_pushboolean(J, 0);
- }
- } else {
- js_pop(J, 1);
- js_pushboolean(J, 0);
- }
- break;
- /* Function calls */
- case OP_EVAL:
- js_eval(J);
- break;
- case OP_CALL:
- js_call(J, *pc++);
- break;
- case OP_NEW:
- js_construct(J, *pc++);
- break;
- /* Unary operators */
- case OP_TYPEOF:
- str = js_typeof(J, -1);
- js_pop(J, 1);
- js_pushliteral(J, str);
- break;
- case OP_POS:
- x = js_tonumber(J, -1);
- js_pop(J, 1);
- js_pushnumber(J, x);
- break;
- case OP_NEG:
- x = js_tonumber(J, -1);
- js_pop(J, 1);
- js_pushnumber(J, -x);
- break;
- case OP_BITNOT:
- ix = js_toint32(J, -1);
- js_pop(J, 1);
- js_pushnumber(J, ~ix);
- break;
- case OP_LOGNOT:
- b = js_toboolean(J, -1);
- js_pop(J, 1);
- js_pushboolean(J, !b);
- break;
- case OP_INC:
- x = js_tonumber(J, -1);
- js_pop(J, 1);
- js_pushnumber(J, x + 1);
- break;
- case OP_DEC:
- x = js_tonumber(J, -1);
- js_pop(J, 1);
- js_pushnumber(J, x - 1);
- break;
- case OP_POSTINC:
- x = js_tonumber(J, -1);
- js_pop(J, 1);
- js_pushnumber(J, x + 1);
- js_pushnumber(J, x);
- break;
- case OP_POSTDEC:
- x = js_tonumber(J, -1);
- js_pop(J, 1);
- js_pushnumber(J, x - 1);
- js_pushnumber(J, x);
- break;
- /* Multiplicative operators */
- case OP_MUL:
- x = js_tonumber(J, -2);
- y = js_tonumber(J, -1);
- js_pop(J, 2);
- js_pushnumber(J, x * y);
- break;
- case OP_DIV:
- x = js_tonumber(J, -2);
- y = js_tonumber(J, -1);
- js_pop(J, 2);
- js_pushnumber(J, x / y);
- break;
- case OP_MOD:
- x = js_tonumber(J, -2);
- y = js_tonumber(J, -1);
- js_pop(J, 2);
- js_pushnumber(J, fmod(x, y));
- break;
- /* Additive operators */
- case OP_ADD:
- js_concat(J);
- break;
- case OP_SUB:
- x = js_tonumber(J, -2);
- y = js_tonumber(J, -1);
- js_pop(J, 2);
- js_pushnumber(J, x - y);
- break;
- /* Shift operators */
- case OP_SHL:
- ix = js_toint32(J, -2);
- uy = js_touint32(J, -1);
- js_pop(J, 2);
- js_pushnumber(J, ix << (uy & 0x1F));
- break;
- case OP_SHR:
- ix = js_toint32(J, -2);
- uy = js_touint32(J, -1);
- js_pop(J, 2);
- js_pushnumber(J, ix >> (uy & 0x1F));
- break;
- case OP_USHR:
- ux = js_touint32(J, -2);
- uy = js_touint32(J, -1);
- js_pop(J, 2);
- js_pushnumber(J, ux >> (uy & 0x1F));
- break;
- /* Relational operators */
- case OP_LT: b = js_compare(J, &okay); js_pop(J, 2); js_pushboolean(J, okay && b < 0); break;
- case OP_GT: b = js_compare(J, &okay); js_pop(J, 2); js_pushboolean(J, okay && b > 0); break;
- case OP_LE: b = js_compare(J, &okay); js_pop(J, 2); js_pushboolean(J, okay && b <= 0); break;
- case OP_GE: b = js_compare(J, &okay); js_pop(J, 2); js_pushboolean(J, okay && b >= 0); break;
- case OP_INSTANCEOF:
- b = js_instanceof(J);
- js_pop(J, 2);
- js_pushboolean(J, b);
- break;
- /* Equality */
- case OP_EQ: b = js_equal(J); js_pop(J, 2); js_pushboolean(J, b); break;
- case OP_NE: b = js_equal(J); js_pop(J, 2); js_pushboolean(J, !b); break;
- case OP_STRICTEQ: b = js_strictequal(J); js_pop(J, 2); js_pushboolean(J, b); break;
- case OP_STRICTNE: b = js_strictequal(J); js_pop(J, 2); js_pushboolean(J, !b); break;
- case OP_JCASE:
- offset = *pc++;
- b = js_strictequal(J);
- if (b) {
- js_pop(J, 2);
- pc = pcstart + offset;
- } else {
- js_pop(J, 1);
- }
- break;
- /* Binary bitwise operators */
- case OP_BITAND:
- ix = js_toint32(J, -2);
- iy = js_toint32(J, -1);
- js_pop(J, 2);
- js_pushnumber(J, ix & iy);
- break;
- case OP_BITXOR:
- ix = js_toint32(J, -2);
- iy = js_toint32(J, -1);
- js_pop(J, 2);
- js_pushnumber(J, ix ^ iy);
- break;
- case OP_BITOR:
- ix = js_toint32(J, -2);
- iy = js_toint32(J, -1);
- js_pop(J, 2);
- js_pushnumber(J, ix | iy);
- break;
- /* Try and Catch */
- case OP_THROW:
- js_throw(J);
- case OP_TRY:
- offset = *pc++;
- if (js_trypc(J, pc)) {
- pc = J->trybuf[J->trytop].pc;
- } else {
- pc = pcstart + offset;
- }
- break;
- case OP_ENDTRY:
- js_endtry(J);
- break;
- case OP_CATCH:
- READSTRING();
- obj = jsV_newobject(J, JS_COBJECT, NULL);
- js_pushobject(J, obj);
- js_rot2(J);
- js_setproperty(J, -2, str);
- J->E = jsR_newenvironment(J, obj, J->E);
- js_pop(J, 1);
- break;
- case OP_ENDCATCH:
- J->E = J->E->outer;
- break;
- /* With */
- case OP_WITH:
- obj = js_toobject(J, -1);
- J->E = jsR_newenvironment(J, obj, J->E);
- js_pop(J, 1);
- break;
- case OP_ENDWITH:
- J->E = J->E->outer;
- break;
- /* Branching */
- case OP_DEBUGGER:
- js_trap(J, (int)(pc - pcstart) - 1);
- break;
- case OP_JUMP:
- pc = pcstart + *pc;
- break;
- case OP_JTRUE:
- offset = *pc++;
- b = js_toboolean(J, -1);
- js_pop(J, 1);
- if (b)
- pc = pcstart + offset;
- break;
- case OP_JFALSE:
- offset = *pc++;
- b = js_toboolean(J, -1);
- js_pop(J, 1);
- if (!b)
- pc = pcstart + offset;
- break;
- case OP_RETURN:
- J->strict = savestrict;
- return;
- }
- }
- }
|