1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542 |
- /*
- * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
- *
- * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * ----------------------------------------------------------------------------
- * Variables
- * ----------------------------------------------------------------------------
- */
- #include "jsvar.h"
- #include "jslex.h"
- #include "jsparse.h"
- #include "jswrap_json.h"
- #include "jsinteractive.h"
- #include "jswrapper.h"
- /** Basically, JsVars are stored in one big array, so save the need for
- * lots of memory allocation. On Linux, the arrays are in blocks, so that
- * more blocks can be allocated. We can't use realloc on one big block as
- * this may change the address of vars that are already locked!
- *
- */
- #ifdef RESIZABLE_JSVARS
- JsVar **jsVarBlocks = 0;
- unsigned int jsVarsSize = 0;
- #define JSVAR_BLOCK_SIZE 1024
- #define JSVAR_BLOCK_SHIFT 10
- #else
- JsVar jsVars[JSVAR_CACHE_SIZE];
- unsigned int jsVarsSize = JSVAR_CACHE_SIZE;
- #endif
- JsVarRef jsVarFirstEmpty; ///< reference of first unused variable (variables are in a linked list)
- /** Return a pointer - UNSAFE for null refs.
- * This is effectively a Lock without locking! */
- static inline JsVar *jsvGetAddressOf(JsVarRef ref) {
- assert(ref);
- #ifdef RESIZABLE_JSVARS
- JsVarRef t = ref-1;
- return &jsVarBlocks[t>>JSVAR_BLOCK_SHIFT][t&(JSVAR_BLOCK_SIZE-1)];
- #else
- return &jsVars[ref-1];
- #endif
- }
- JsVar *_jsvGetAddressOf(JsVarRef ref) {
- return jsvGetAddressOf(ref);
- }
- // For debugging/testing ONLY - maximum # of vars we are allowed to use
- void jsvSetMaxVarsUsed(unsigned int size) {
- #ifdef RESIZABLE_JSVARS
- assert(size < JSVAR_BLOCK_SIZE); // remember - this is only for DEBUGGING - as such it doesn't use multiple blocks
- #else
- assert(size < JSVAR_CACHE_SIZE);
- #endif
- jsVarsSize = size;
- }
- // maps the empty variables in...
- void jsvSoftInit() {
- jsVarFirstEmpty = 0;
- JsVar *lastEmpty = 0;
- JsVarRef i;
- for (i=1;i<=jsVarsSize;i++) {
- if ((jsvGetAddressOf(i)->flags&JSV_VARTYPEMASK) == JSV_UNUSED) {
- jsvGetAddressOf(i)->nextSibling = 0;
- if (lastEmpty)
- lastEmpty->nextSibling = i;
- else
- jsVarFirstEmpty = i;
- lastEmpty = jsvGetAddressOf(i);
- }
- }
- }
- void jsvSoftKill() {
- }
- /** This links all JsVars together, so we can have our nice
- * linked list of free JsVars. It returns the ref of the first
- * item - that we should set jsVarFirstEmpty to (if it is 0) */
- static JsVarRef jsvInitJsVars(JsVarRef start, unsigned int count) {
- JsVarRef i;
- for (i=start;i<start+count;i++) {
- JsVar *v = jsvGetAddressOf(i);
- v->flags = JSV_UNUSED;
- #ifdef LARGE_MEM
- v->this = i;
- #endif
- // v->locks = 0; // locks is 0 anyway because it is stored in flags
- v->nextSibling = (JsVarRef)(i+1); // link to next
- }
- jsvGetAddressOf((JsVarRef)(start+count-1))->nextSibling = (JsVarRef)0; // set the final one to 0
- return start;
- }
- void jsvInit() {
- #ifdef RESIZABLE_JSVARS
- jsVarsSize = JSVAR_BLOCK_SIZE;
- jsVarBlocks = malloc(sizeof(JsVar*)); // just 1
- jsVarBlocks[0] = malloc(sizeof(JsVar) * JSVAR_BLOCK_SIZE);
- #endif
- jsVarFirstEmpty = jsvInitJsVars(1/*first*/, jsVarsSize);
- jsvSoftInit();
- }
- void jsvKill() {
- #ifdef RESIZABLE_JSVARS
- unsigned int i;
- for (i=0;i<jsVarsSize>>JSVAR_BLOCK_SHIFT;i++)
- free(jsVarBlocks[i]);
- free(jsVarBlocks);
- jsVarBlocks = 0;
- jsVarsSize = 0;
- #endif
- }
- /** Find or create the ROOT variable item - used mainly
- * if recovering from a saved state. */
- JsVar *jsvFindOrCreateRoot() {
- JsVarRef i;
- for (i=1;i<=jsVarsSize;i++)
- if (jsvIsRoot(jsvGetAddressOf(i)))
- return jsvLock(i);
- return jsvRef(jsvNewWithFlags(JSV_ROOT));
- }
- /// Get number of memory records (JsVars) used
- unsigned int jsvGetMemoryUsage() {
- unsigned int usage = 0;
- unsigned int i;
- for (i=1;i<=jsVarsSize;i++) {
- JsVar *v = jsvGetAddressOf((JsVarRef)i);
- if ((v->flags&JSV_VARTYPEMASK) != JSV_UNUSED)
- usage++;
- }
- return usage;
- }
- /// Get total amount of memory records
- unsigned int jsvGetMemoryTotal() {
- return jsVarsSize;
- }
- /// Try and allocate more memory - only works if RESIZABLE_JSVARS is defined
- void jsvSetMemoryTotal(unsigned int jsNewVarCount) {
- #ifdef RESIZABLE_JSVARS
- if (jsNewVarCount <= jsVarsSize) return; // never allow us to have less!
- // When resizing, we just allocate a bunch more
- unsigned int oldSize = jsVarsSize;
- unsigned int oldBlockCount = jsVarsSize >> JSVAR_BLOCK_SHIFT;
- unsigned int newBlockCount = (jsNewVarCount+JSVAR_BLOCK_SIZE-1) >> JSVAR_BLOCK_SHIFT;
- jsVarsSize = newBlockCount << JSVAR_BLOCK_SHIFT;
- // resize block table
- jsVarBlocks = realloc(jsVarBlocks, sizeof(JsVar*)*newBlockCount);
- // allocate more blocks
- unsigned int i;
- for (i=oldBlockCount;i<newBlockCount;i++)
- jsVarBlocks[i] = malloc(sizeof(JsVar) * JSVAR_BLOCK_SIZE);
- /** and now reset all the newly allocated vars. We know jsVarFirstEmpty
- * is 0 (because jsiFreeMoreMemory returned 0) so we can just assign it. */
- assert(!jsVarFirstEmpty);
- jsVarFirstEmpty = jsvInitJsVars(oldSize+1, jsVarsSize-oldSize);
- // jsiConsolePrintf("Resized memory from %d blocks to %d\n", oldBlockCount, newBlockCount);
- #else
- NOT_USED(jsNewVarCount);
- assert(0);
- #endif
- }
- /// Get whether memory is full or not
- bool jsvIsMemoryFull() {
- return !jsVarFirstEmpty;
- }
- // Show what is still allocated, for debugging memory problems
- void jsvShowAllocated() {
- JsVarRef i;
- for (i=1;i<=jsVarsSize;i++) {
- if ((jsvGetAddressOf(i)->flags&JSV_VARTYPEMASK) != JSV_UNUSED) {
- jsiConsolePrintf("USED VAR #%d:",i);
- jsvTrace(i, 2);
- }
- }
- }
- bool jsvHasCharacterData(const JsVar *v) {
- return jsvIsString(v) || jsvIsStringExt(v);
- }
- bool jsvHasStringExt(const JsVar *v) {
- return jsvIsString(v) || jsvIsStringExt(v);
- }
- bool jsvHasChildren(const JsVar *v) {
- return jsvIsFunction(v) || jsvIsObject(v) || jsvIsArray(v) || jsvIsRoot(v);
- }
- /// Is this variable a type that uses firstChild to point to a single Variable (ie. it doesn't have multiple children)
- bool jsvHasSingleChild(const JsVar *v) {
- return jsvIsName(v) || jsvIsArrayBuffer(v) || jsvIsArrayBufferName(v);
- }
- JsVar *jsvNew() {
- if (jsVarFirstEmpty!=0) {
- JsVar *v = jsvLock(jsVarFirstEmpty);
- jsVarFirstEmpty = v->nextSibling; // move our reference to the next in the free list
- assert((v->flags&JSV_VARTYPEMASK) == JSV_UNUSED);
- // reset it
- v->refs = 0;
- //v->locks = 1;
- v->flags = JSV_LOCK_ONE;
- v->varData.callback = 0;
- v->firstChild = 0;
- v->lastChild = 0;
- v->prevSibling = 0;
- v->nextSibling = 0;
- // return pointer
- return v;
- }
- /* we don't have memort - second last hope - run garbage collector */
- if (jsvGarbageCollect())
- return jsvNew(); // if it freed something, continue
- /* we don't have memory - last hope - ask jsInteractive to try and free some it
- may have kicking around */
- if (jsiFreeMoreMemory())
- return jsvNew();
- /* We couldn't claim any more memory by Garbage collecting... */
- #ifdef RESIZABLE_JSVARS
- jsvSetMemoryTotal(jsVarsSize*2);
- return jsvNew();
- #else
- // On a micro, we're screwed.
- jsError("Out of Memory!");
- jspSetInterrupted(true);
- return 0;
- #endif
- }
- void jsvFreePtr(JsVar *var) {
- /* To be here, we're not supposed to be part of anything else. If
- * we were, we'd have been freed by jsvGarbageCollect */
- assert(jsvIsStringExt(var) || (!var->nextSibling && !var->prevSibling));
- // Names that Link to other things
- if (jsvHasSingleChild(var)) {
- if (var->firstChild) {
- JsVar *child = jsvLock(var->firstChild);
- jsvUnRef(child); var->firstChild = 0; // unlink the child
- jsvUnLock(child); // unlock should trigger a free
- }
- }
- /* No else, because a String Name may have a single child, but
- * also StringExts */
- /* Now, free children - see jsvar.h comments for how! */
- if (jsvHasStringExt(var)) {
- // TODO: make string free this non-recursive
- JsVarRef stringDataRef = var->lastChild;
- var->lastChild = 0;
- if (stringDataRef) {
- JsVar *child = jsvLock(stringDataRef);
- assert(jsvIsStringExt(child));
- jsvFreePtr(child);
- jsvUnLock(child);
- }
- } else if (jsvHasChildren(var)) {
- JsVarRef childref = var->firstChild;
- var->firstChild = 0;
- var->lastChild = 0;
- while (childref) {
- JsVar *child = jsvLock(childref);
- assert(jsvIsName(child));
- childref = child->nextSibling;
- child->prevSibling = 0;
- child->nextSibling = 0;
- jsvUnRef(child);
- jsvUnLock(child);
- }
- } else {
- assert(!var->firstChild);
- assert(!var->lastChild);
- }
- // free!
- var->flags = (var->flags & ~JSV_VARTYPEMASK) | JSV_UNUSED;
- // add this to our free list
- var->nextSibling = jsVarFirstEmpty;
- jsVarFirstEmpty = jsvGetRef(var);
- }
- /// Get a reference from a var - SAFE for null vars
- JsVarRef jsvGetRef(JsVar *var) {
- if (!var) return 0;
- #ifdef LARGE_MEM
- return var->this;
- #else
- #ifdef RESIZABLE_JSVARS
- unsigned int i, c = jsVarsSize>>JSVAR_BLOCK_SHIFT;
- for (i=0;i<c;i++) {
- if (var>=jsVarBlocks[i] && var<&jsVarBlocks[i][JSVAR_BLOCK_SIZE]) {
- JsVarRef r = (JsVarRef)(1 + (i<<JSVAR_BLOCK_SHIFT) + (var - jsVarBlocks[i]));
- return r;
- }
- }
- return 0;
- #else
- return (JsVarRef)(1 + (var - jsVars));
- #endif
- #endif
- }
- /// Lock this reference and return a pointer - UNSAFE for null refs
- JsVar *jsvLock(JsVarRef ref) {
- JsVar *var = jsvGetAddressOf(ref);
- //var->locks++;
- assert(jsvGetLocks(var) < JSV_LOCK_MAX);
- var->flags += JSV_LOCK_ONE;
- #ifdef DEBUG
- if (jsvGetLocks(var)==0) {
- jsError("Too many locks to Variable!");
- //jsPrint("Var #");jsPrintInt(ref);jsPrint("\n");
- }
- #endif
- return var;
- }
- /// Lock this pointer and return a pointer - UNSAFE for null pointer
- JsVar *jsvLockAgain(JsVar *var) {
- assert(var);
- assert(jsvGetLocks(var) < JSV_LOCK_MAX);
- var->flags += JSV_LOCK_ONE;
- #ifdef DEBUG
- if (var->locks==0) {
- jsError("Too many locks to Variable!");
- //jsPrint("Var #");jsPrintInt(ref);jsPrint("\n");
- }
- #endif
- return var;
- }
- /// Unlock this variable - this is SAFE for null variables
- void jsvUnLock(JsVar *var) {
- if (!var) return;
- assert(jsvGetLocks(var)>0);
- var->flags -= JSV_LOCK_ONE;
- /* if we know we're free, then we can just free
- * this variable right now. Loops of variables
- * are handled by the Garbage Collector.
- * Note: we check var->refs first as it is fastest and most likely to be false */
- if (var->refs == 0 && jsvHasRef(var) && jsvGetLocks(var) == 0 && (var->flags&JSV_VARTYPEMASK)!=JSV_UNUSED) {
- jsvFreePtr(var);
- }
- }
- /// Reference - set this variable as used by something
- JsVar *jsvRef(JsVar *v) {
- assert(v && jsvHasRef(v));
- v->refs++;
- return v;
- }
- /// Unreference - set this variable as not used by anything
- void jsvUnRef(JsVar *var) {
- assert(var && var->refs>0 && jsvHasRef(var));
- var->refs--;
- // locks are never 0 here, so why bother checking!
- assert(jsvGetLocks(var)>0);
- }
- /// Helper fn, Reference - set this variable as used by something
- JsVarRef jsvRefRef(JsVarRef ref) {
- JsVar *v;
- assert(ref);
- v = jsvLock(ref);
- assert(!jsvIsStringExt(v));
- jsvRef(v);
- jsvUnLock(v);
- return ref;
- }
- /// Helper fn, Unreference - set this variable as not used by anything
- JsVarRef jsvUnRefRef(JsVarRef ref) {
- JsVar *v;
- assert(ref);
- v = jsvLock(ref);
- assert(!jsvIsStringExt(v));
- jsvUnRef(v);
- jsvUnLock(v);
- return 0;
- }
- JsVar *jsvNewFromString(const char *str) {
- // Create a var
- JsVar *first = jsvNewWithFlags(JSV_STRING);
- if (!first) {
- jsWarn("Unable to create string as not enough memory");
- return 0;
- }
- // Now we copy the string, but keep creating new jsVars if we go
- // over the end
- JsVar *var = jsvLockAgain(first);
- while (*str) {
- // copy data in
- size_t i, l = jsvGetMaxCharactersInVar(var);
- for (i=0;i<l && *str;i++)
- var->varData.str[i] = *(str++);
- // might as well shove a zero terminator on it if we can
- if (i<l) var->varData.str[i]=0;
- // we've stopped if the string was empty
- jsvSetCharactersInVar(var, i);
- // if there is still some left, it's because we filled up our var...
- // make a new one, link it in, and unlock the old one.
- if (*str) {
- JsVar *next = jsvNewWithFlags(JSV_STRING_EXT);
- if (!next) {
- jsWarn("Truncating string as not enough memory");
- jsvUnLock(var);
- return first;
- }
- // we don't ref, because StringExts are never reffed as they only have one owner (and ALWAYS have an owner)
- var->lastChild = jsvGetRef(next);
- jsvUnLock(var);
- var = next;
- }
- }
- jsvUnLock(var);
- // return
- return first;
- }
- JsVar *jsvNewStringOfLength(unsigned int byteLength) {
- // Create a var
- JsVar *first = jsvNewWithFlags(JSV_STRING);
- if (!first) {
- jsWarn("Unable to create string as not enough memory");
- return 0;
- }
- // Now zero the string, but keep creating new jsVars if we go
- // over the end
- JsVar *var = jsvLockAgain(first);
- while (byteLength>0) {
- // copy data in
- size_t i, l = jsvGetMaxCharactersInVar(var);
- for (i=0;i<l && byteLength>0;i++,byteLength--)
- var->varData.str[i] = 0;
- // might as well shove a zero terminator on it if we can
- if (i<l) var->varData.str[i]=0;
- // we've stopped if the string was empty
- jsvSetCharactersInVar(var, i);
- // if there is still some left, it's because we filled up our var...
- // make a new one, link it in, and unlock the old one.
- if (byteLength>0) {
- JsVar *next = jsvNewWithFlags(JSV_STRING_EXT);
- if (!next) {
- jsWarn("Truncating string as not enough memory");
- jsvUnLock(var);
- return first;
- }
- // we don't ref, because StringExts are never reffed as they only have one owner (and ALWAYS have an owner)
- var->lastChild = jsvGetRef(next);
- jsvUnLock(var);
- var = next;
- }
- }
- jsvUnLock(var);
- // return
- return first;
- }
- JsVar *jsvNewFromLexer(struct JsLex *lex, JslCharPos charFrom, JslCharPos charTo) {
- // Create a var
- JsVar *var = jsvNewFromEmptyString();
- if (!var) { // out of memory
- return 0;
- }
- jsvAppendStringVar(var, lex->sourceVar, charFrom, (JslCharPos)charTo-charFrom);
- return var;
- }
- JsVar *jsvNewWithFlags(JsVarFlags flags) {
- JsVar *var = jsvNew();
- if (!var) return 0; // no memory
- var->flags = (var->flags&(JsVarFlags)(~JSV_VARTYPEMASK)) | (flags&(JsVarFlags)(~JSV_LOCK_MASK));
- return var;
- }
- JsVar *jsvNewFromInteger(JsVarInt value) {
- JsVar *var = jsvNewWithFlags(JSV_INTEGER);
- if (!var) return 0; // no memory
- var->varData.integer = value;
- return var;
- }
- JsVar *jsvNewFromBool(bool value) {
- JsVar *var = jsvNewWithFlags(JSV_BOOLEAN);
- if (!var) return 0; // no memory
- var->varData.integer = value ? 1 : 0;
- return var;
- }
- JsVar *jsvNewFromFloat(JsVarFloat value) {
- JsVar *var = jsvNewWithFlags(JSV_FLOAT);
- if (!var) return 0; // no memory
- var->varData.floating = value;
- return var;
- }
- JsVar *jsvMakeIntoVariableName(JsVar *var, JsVar *valueOrZero) {
- if (!var) return 0;
- assert(var->refs==0); // make sure it's unused
- var->flags |= JSV_NAME;
- if (valueOrZero)
- var->firstChild = jsvGetRef(jsvRef(valueOrZero));
- return var;
- }
- JsVar *jsvNewFromPin(int pin) {
- JsVar *v = jsvNewFromInteger((JsVarInt)pin);
- if (v) {
- v->flags = (JsVarFlags)((v->flags & ~JSV_VARTYPEMASK) | JSV_PIN);
- }
- return v;
- }
- bool jsvIsBasicVarEqual(JsVar *a, JsVar *b) {
- // quick checks
- if (a==b) return true;
- if (!a || !b) return false; // one of them is undefined
- // OPT: would this be useful as compare instead?
- assert(jsvIsBasic(a) && jsvIsBasic(b));
- if (jsvIsNumeric(a) && jsvIsNumeric(b)) {
- if (jsvIsInt(a)) {
- if (jsvIsInt(b)) {
- return a->varData.integer == b->varData.integer;
- } else {
- assert(jsvIsFloat(b));
- return a->varData.integer == b->varData.floating;
- }
- } else {
- assert(jsvIsFloat(a));
- if (jsvIsInt(b)) {
- return a->varData.floating == b->varData.integer;
- } else {
- assert(jsvIsFloat(b));
- return a->varData.floating == b->varData.floating;
- }
- }
- } else if (jsvIsString(a) && jsvIsString(b)) {
- JsvStringIterator ita, itb;
- jsvStringIteratorNew(&ita, a, 0);
- jsvStringIteratorNew(&itb, b, 0);
- while (true) {
- char a = jsvStringIteratorGetChar(&ita);
- char b = jsvStringIteratorGetChar(&itb);
- if (a != b) {
- jsvStringIteratorFree(&ita);
- jsvStringIteratorFree(&itb);
- return false;
- }
- if (!a) { // equal, but end of string
- jsvStringIteratorFree(&ita);
- jsvStringIteratorFree(&itb);
- return true;
- }
- jsvStringIteratorNext(&ita);
- jsvStringIteratorNext(&itb);
- }
- // we never get here
- return false; // make compiler happy
- } else {
- //TODO: are there any other combinations we should check here?? String v int?
- return false;
- }
- }
- bool jsvIsEqual(JsVar *a, JsVar *b) {
- if (jsvIsBasic(a) && jsvIsBasic(b))
- return jsvIsBasicVarEqual(a,b);
- return jsvGetRef(a)==jsvGetRef(b);
- }
- /// Get a const string representing this variable - if we can. Otherwise return 0
- const char *jsvGetConstString(const JsVar *v) {
- if (jsvIsUndefined(v)) {
- return "undefined";
- } else if (jsvIsNull(v)) {
- return "null";
- } else if (jsvIsBoolean(v)) {
- return jsvGetBool(v) ? "true" : "false";
- } else if (jsvIsRoot(v)) {
- return "[object Hardware]";
- } else if (jsvIsObject(v)) {
- return "[object Object]";
- }
- return 0;
- }
- /// Return the 'type' of the JS variable (eg. JS's typeof operator)
- const char *jsvGetTypeOf(const JsVar *v) {
- if (jsvIsNull(v)) return "object";
- if (jsvIsUndefined(v)) return "undefined";
- if (jsvIsFunction(v)) return "function";
- if (jsvIsObject(v) || jsvIsArray(v)) return "object";
- if (jsvIsString(v)) return "string";
- if (jsvIsBoolean(v)) return "boolean";
- if (jsvIsNumeric(v)) return "number";
- return "?";
- }
- /// Save this var as a string to the given buffer, and return how long it was (return val doesn't include terminating 0)
- size_t jsvGetString(const JsVar *v, char *str, size_t len) {
- const char *s = jsvGetConstString(v);
- if (s) {
- strncpy(str, s, len);
- return strlen(s);
- } else if (jsvIsInt(v)) {
- itoa(v->varData.integer, str, 10);
- return strlen(str);
- } else if (jsvIsFloat(v)) {
- ftoa(v->varData.floating, str);
- return strlen(str);
- } else if (jsvHasCharacterData(v)) {
- if (jsvIsStringExt(v))
- jsErrorInternal("Calling jsvGetString on a JSV_STRING_EXT");
- size_t l = len;
- JsvStringIterator it;
- jsvStringIteratorNewConst(&it, v, 0);
- while (jsvStringIteratorHasChar(&it)) {
- if (l--<=1) {
- *str = 0;
- jsWarn("jsvGetString overflowed\n");
- jsvStringIteratorFree(&it);
- return len;
- }
- *(str++) = jsvStringIteratorGetChar(&it);
- jsvStringIteratorNext(&it);
- }
- jsvStringIteratorFree(&it);
- *str = 0;
- return len-l;
- } else {
- // Try and get as a JsVar string, and try again
- JsVar *stringVar = jsvAsString((JsVar*)v, false); // we know we're casting to non-const here
- if (stringVar) {
- size_t l = jsvGetString(stringVar, str, len); // call again - but this time with converted var
- jsvUnLock(stringVar);
- return l;
- } else {
- strncpy(str, "", len);
- jsErrorInternal("Variable type cannot be converted to string");
- return 0;
- }
- }
- }
- /// Set the Data in this string. This must JUST overwrite - not extend or shrink
- void jsvSetString(JsVar *v, char *str, size_t len) {
- assert(jsvHasCharacterData(v));
- assert(len == jsvGetStringLength(v));
- JsvStringIterator it;
- jsvStringIteratorNew(&it, v, 0);
- size_t i;
- for (i=0;i<len;i++) {
- jsvStringIteratorSetChar(&it, str[i]);
- jsvStringIteratorNext(&it);
- }
- jsvStringIteratorFree(&it);
- }
- /** If var is a string, lock and return it, else
- * create a new string. unlockVar means this will auto-unlock 'var' */
- JsVar *jsvAsString(JsVar *v, bool unlockVar) {
- JsVar *str = 0;
- // If it is string-ish, but not quite a string, copy it
- if (jsvHasCharacterData(v) && jsvIsName(v)) {
- str = jsvNewFromEmptyString();
- if (str) jsvAppendStringVarComplete(str,v);
- } else if (jsvIsString(v)) { // If it is a string - just return a reference
- str = jsvLockAgain(v);
- } else {
- const char *constChar = jsvGetConstString(v);
- if (constChar) {
- // if we could get this as a simple const char, do that..
- str = jsvNewFromString(constChar);
- } else if (jsvIsPin(v)) {
- char buf[8];
- jshGetPinString(buf, (Pin)v->varData.integer);
- str = jsvNewFromString(buf);
- } else if (jsvIsInt(v)) {
- char buf[JS_NUMBER_BUFFER_SIZE];
- itoa(v->varData.integer, buf, 10);
- str = jsvNewFromString(buf);
- } else if (jsvIsFloat(v)) {
- char buf[JS_NUMBER_BUFFER_SIZE];
- ftoa(v->varData.floating, buf);
- str = jsvNewFromString(buf);
- } else if (jsvIsArray(v) || jsvIsArrayBuffer(v)) {
- JsVar *filler = jsvNewFromString(",");
- str = jsvArrayJoin(v, filler);
- jsvUnLock(filler);
- } else if (jsvIsFunction(v)) {
- str = jsvNewFromEmptyString();
- if (str) jsfGetJSON(v, str);
- } else {
- jsErrorInternal("Variable type cannot be converted to string");
- str = 0;
- }
- }
- if (unlockVar) jsvUnLock(v);
- return str;
- }
- size_t jsvGetStringLength(JsVar *v) {
- size_t strLength = 0;
- JsVar *var = v;
- JsVarRef ref = 0;
- if (!jsvHasCharacterData(v)) return 0;
- while (var) {
- JsVarRef refNext = var->lastChild;
- strLength += jsvGetCharactersInVar(var);
- // Go to next
- if (ref) jsvUnLock(var); // note use of if (ref), not var
- ref = refNext;
- var = ref ? jsvLock(ref) : 0;
- }
- if (ref) jsvUnLock(var); // note use of if (ref), not var
- return strLength;
- }
- // IN A STRING get the number of lines in the string (min=1)
- int jsvGetLinesInString(JsVar *v) {
- int lines = 1;
- JsvStringIterator it;
- jsvStringIteratorNew(&it, v, 0);
- while (jsvStringIteratorHasChar(&it)) {
- if (jsvStringIteratorGetChar(&it)=='\n') lines++;
- jsvStringIteratorNext(&it);
- }
- jsvStringIteratorFree(&it);
- return lines;
- }
- // IN A STRING Get the number of characters on a line - lines start at 1
- int jsvGetCharsOnLine(JsVar *v, int line) {
- int currentLine = 1;
- int chars = 0;
- JsvStringIterator it;
- jsvStringIteratorNew(&it, v, 0);
- while (jsvStringIteratorHasChar(&it)) {
- if (jsvStringIteratorGetChar(&it)=='\n') {
- currentLine++;
- if (currentLine > line) break;
- } else if (currentLine==line) chars++;
- jsvStringIteratorNext(&it);
- }
- jsvStringIteratorFree(&it);
- return chars;
- }
- // IN A STRING, get the line and column of the given character. Both values must be non-null
- void jsvGetLineAndCol(JsVar *v, int charIdx, int* line, int *col) {
- int x = 1;
- int y = 1;
- int n = 0;
- assert(line && col);
- JsvStringIterator it;
- jsvStringIteratorNew(&it, v, 0);
- while (jsvStringIteratorHasChar(&it)) {
- char ch = jsvStringIteratorGetChar(&it);
- if (n==charIdx) {
- jsvStringIteratorFree(&it);
- *line = y;
- *col = x;
- return;
- }
- x++;
- if (ch=='\n') {
- x=1; y++;
- }
- n++;
- jsvStringIteratorNext(&it);
- }
- jsvStringIteratorFree(&it);
- // uh-oh - not found
- *line = y;
- *col = x;
- }
- // IN A STRING, get a character index from a line and column
- int jsvGetIndexFromLineAndCol(JsVar *v, int line, int col) {
- int x = 1;
- int y = 1;
- int n = 0;
- JsvStringIterator it;
- jsvStringIteratorNew(&it, v, 0);
- while (jsvStringIteratorHasChar(&it)) {
- char ch = jsvStringIteratorGetChar(&it);
- if ((y==line && x>=col) || y>line) {
- jsvStringIteratorFree(&it);
- return (y>line) ? (n-1) : n;
- }
- x++;
- if (ch=='\n') {
- x=1; y++;
- }
- n++;
- jsvStringIteratorNext(&it);
- }
- jsvStringIteratorFree(&it);
- return n;
- }
- void jsvAppendString(JsVar *var, const char *str) {
- assert(jsvIsString(var));
- JsVar *block = jsvLockAgain(var);
- // Find the block at end of the string...
- while (block->lastChild) {
- JsVarRef next = block->lastChild;
- jsvUnLock(block);
- block = jsvLock(next);
- }
- // find how full the block is
- size_t blockChars = jsvGetCharactersInVar(block);
- // now start appending
- while (*str) {
- // copy data in
- size_t i, l=jsvGetMaxCharactersInVar(block);
- for (i=blockChars;i<l && *str;i++) {
- block->varData.str[i] = *(str++);
- }
- jsvSetCharactersInVar(block, i);
- // if there is still some left, it's because we filled up our var...
- // make a new one, link it in, and unlock the old one.
- if (*str) {
- JsVar *next = jsvNewWithFlags(JSV_STRING_EXT);
- if (!next) break;
- // we don't ref, because StringExts are never reffed as they only have one owner (and ALWAYS have an owner)
- block->lastChild = jsvGetRef(next);
- jsvUnLock(block);
- block = next;
- blockChars=0; // it's new, so empty
- }
- }
- jsvUnLock(block);
- }
- void jsvAppendStringBuf(JsVar *var, const char *str, int length) {
- assert(jsvIsString(var));
- JsVar *block = jsvLockAgain(var);
- // Find the block at end of the string...
- while (block->lastChild) {
- JsVarRef next = block->lastChild;
- jsvUnLock(block);
- block = jsvLock(next);
- }
- // find how full the block is
- size_t blockChars = jsvGetCharactersInVar(block);
- // now start appending
- while (length) {
- // copy data in
- size_t i, l=jsvGetMaxCharactersInVar(block);
- for (i=blockChars;i<l && length;i++) {
- block->varData.str[i] = *(str++);
- length--;
- }
- jsvSetCharactersInVar(block, i);
- // if there is still some left, it's because we filled up our var...
- // make a new one, link it in, and unlock the old one.
- if (length) {
- JsVar *next = jsvNewWithFlags(JSV_STRING_EXT);
- if (!next) break;
- // we don't ref, because StringExts are never reffed as they only have one owner (and ALWAYS have an owner)
- block->lastChild = jsvGetRef(next);
- jsvUnLock(block);
- block = next;
- blockChars=0; // it's new, so empty
- }
- }
- jsvUnLock(block);
- }
- static void _jsvAppendPrintf(const char *str, void *user_data) {
- while (*str)
- jsvStringIteratorAppend((JsvStringIterator *)user_data, *(str++));
- }
- void jsvAppendPrintf(JsVar *var, const char *fmt, ...) {
- JsvStringIterator it;
- jsvStringIteratorNew(&it, var, 0);
- jsvStringIteratorGotoEnd(&it);
- va_list argp;
- va_start(argp, fmt);
- vcbprintf((vcbprintf_callback)&_jsvAppendPrintf,&it, fmt, argp);
- va_end(argp);
- jsvStringIteratorFree(&it);
- }
- /** Append str to var. Both must be strings. stridx = start char or str, maxLength = max number of characters (can be JSVAPPENDSTRINGVAR_MAXLENGTH).
- * stridx can be negative to go from end of string */
- void jsvAppendStringVar(JsVar *var, const JsVar *str, int stridx, int maxLength) {
- JsVar *block = jsvLockAgain(var);
- assert(jsvIsString(var));
- // Find the block at end of the string...
- while (block->lastChild) {
- JsVarRef next = block->lastChild;
- jsvUnLock(block);
- block = jsvLock(next);
- }
- // find how full the block is
- size_t blockChars = jsvGetCharactersInVar(block);
- // now start appending
- JsvStringIterator it;
- jsvStringIteratorNewConst(&it, str, stridx);
- while (jsvStringIteratorHasChar(&it) && (maxLength-->0)) {
- char ch = jsvStringIteratorGetChar(&it);
- if (blockChars >= jsvGetMaxCharactersInVar(block)) {
- jsvSetCharactersInVar(block, blockChars);
- JsVar *next = jsvNewWithFlags(JSV_STRING_EXT);
- if (!next) break; // out of memory
- // we don't ref, because StringExts are never reffed as they only have one owner (and ALWAYS have an owner)
- block->lastChild = jsvGetRef(next);
- jsvUnLock(block);
- block = next;
- blockChars=0; // it's new, so empty
- }
- block->varData.str[blockChars++] = ch;
- jsvStringIteratorNext(&it);
- }
- jsvStringIteratorFree(&it);
- jsvSetCharactersInVar(block, blockChars);
- jsvUnLock(block);
- }
- /** Append all of str to var. Both must be strings. */
- void jsvAppendStringVarComplete(JsVar *var, const JsVar *str) {
- jsvAppendStringVar(var, str, 0, JSVAPPENDSTRINGVAR_MAXLENGTH);
- }
- char jsvGetCharInString(JsVar *v, int idx) {
- if (!jsvIsString(v)) return 0;
- if (idx<0) idx += (int)jsvGetStringLength(v); // <0 goes from end of string
- if (idx<0) return 0;
- JsvStringIterator it;
- jsvStringIteratorNew(&it, v, idx);
- char ch = jsvStringIteratorGetChar(&it);
- jsvStringIteratorFree(&it);
- return ch;
- }
- /** Does this string contain only Numeric characters? */
- bool jsvIsStringNumeric(const JsVar *var) {
- assert(jsvIsString(var));
- JsvStringIterator it;
- jsvStringIteratorNewConst(&it, var, 0); // we know it's non const
- int chars = 0;
- while (jsvStringIteratorHasChar(&it)) {
- chars++;
- char ch = jsvStringIteratorGetChar(&it);
- if (!isNumeric(ch)) { // FIXME: should check for non-integer values (floating point?)
- jsvStringIteratorFree(&it);
- return false;
- }
- jsvStringIteratorNext(&it);
- }
- jsvStringIteratorFree(&it);
- return chars>0;
- }
- /** Does this string contain only Numeric characters? This is for arrays
- * and makes the assertion that int_to_string(string_to_int(var))==var */
- bool jsvIsStringNumericStrict(const JsVar *var) {
- assert(jsvIsString(var));
- JsvStringIterator it;
- jsvStringIteratorNewConst(&it, var, 0); // we know it's non const
- bool hadNonZero = false;
- bool hasLeadingZero = false;
- int chars = 0;
- while (jsvStringIteratorHasChar(&it)) {
- chars++;
- char ch = jsvStringIteratorGetChar(&it);
- if (!isNumeric(ch)) {
- // test for leading zero ensures int_to_string(string_to_int(var))==var
- jsvStringIteratorFree(&it);
- return false;
- }
- if (!hadNonZero && ch=='0') hasLeadingZero=true;
- if (ch!='0') hadNonZero=true;
- jsvStringIteratorNext(&it);
- }
- jsvStringIteratorFree(&it);
- return chars>0 && (!hasLeadingZero || chars==1);
- }
- JsVarInt jsvGetInteger(const JsVar *v) {
- if (!v) return 0; // undefined
- /* strtol understands about hex and octal */
- if (jsvIsInt(v) || jsvIsBoolean(v) || jsvIsPin(v) || jsvIsArrayBufferName(v)) return v->varData.integer;
- if (jsvIsNull(v)) return 0;
- if (jsvIsUndefined(v)) return 0;
- if (jsvIsFloat(v)) return (JsVarInt)v->varData.floating;
- if (jsvIsString(v) && jsvIsStringNumeric(v)) {
- char buf[32];
- jsvGetString(v, buf, sizeof(buf));
- return stringToInt(buf);
- }
- return 0;
- }
- void jsvSetInteger(JsVar *v, JsVarInt value) {
- assert(jsvIsInt(v));
- v->varData.integer = value;
- }
- bool jsvGetBool(const JsVar *v) {
- return jsvGetInteger(v)!=0;
- }
- JsVarFloat jsvGetFloat(const JsVar *v) {
- if (!v) return NAN; // undefined
- if (jsvIsFloat(v)) return v->varData.floating;
- if (jsvIsInt(v)) return (JsVarFloat)v->varData.integer;
- if (jsvIsNull(v)) return 0;
- if (jsvIsString(v) && jsvIsStringNumeric(v)) {
- char buf[32];
- jsvGetString(v, buf, sizeof(buf));
- return stringToFloat(buf);
- }
- return NAN;
- }
- /// Convert the given variable to a number
- JsVar *jsvAsNumber(JsVar *var) {
- if (jsvIsInt(var) || jsvIsFloat(var)) return jsvLockAgain(var);
- if (jsvIsBoolean(var) || jsvIsPin(var)) return jsvNewFromInteger(var->varData.integer);
- return jsvNewFromFloat(jsvGetFloat(var));
- }
- #ifdef SAVE_ON_FLASH
- JsVarInt jsvGetIntegerAndUnLock(JsVar *v) { return _jsvGetIntegerAndUnLock(v); }
- JsVarFloat jsvGetFloatAndUnLock(JsVar *v) { return _jsvGetFloatAndUnLock(v); }
- bool jsvGetBoolAndUnLock(JsVar *v) { return _jsvGetBoolAndUnLock(v); }
- #endif
- /** Get the item at the given location in the array buffer and return the result */
- size_t jsvGetArrayBufferLength(JsVar *arrayBuffer) {
- assert(jsvIsArrayBuffer(arrayBuffer));
- return arrayBuffer->varData.arraybuffer.length;
- }
- /** Get the item at the given location in the array buffer and return the result */
- JsVar *jsvArrayBufferGet(JsVar *arrayBuffer, JsVarInt idx) {
- JsvArrayBufferIterator it;
- jsvArrayBufferIteratorNew(&it, arrayBuffer, idx);
- JsVar *v = jsvArrayBufferIteratorGetValue(&it);
- jsvArrayBufferIteratorFree(&it);
- return v;
- }
- /** Set the item at the given location in the array buffer */
- void jsvArrayBufferSet(JsVar *arrayBuffer, JsVarInt idx, JsVar *value) {
- JsvArrayBufferIterator it;
- jsvArrayBufferIteratorNew(&it, arrayBuffer, idx);
- jsvArrayBufferIteratorSetValue(&it, value);
- jsvArrayBufferIteratorFree(&it);
- }
- /** Given an integer name that points to an arraybuffer or an arraybufferview, evaluate it and return the result */
- JsVar *jsvArrayBufferGetFromName(JsVar *name) {
- assert(jsvIsArrayBufferName(name));
- JsVarInt idx = jsvGetInteger(name);
- JsVar *arrayBuffer = jsvLock(name->firstChild);
- JsVar *value = jsvArrayBufferGet(arrayBuffer, idx);
- jsvUnLock(arrayBuffer);
- return value;
- }
- /** If a is a name skip it and go to what it points to - and so on.
- * ALWAYS locks - so must unlock what it returns. It MAY
- * return 0. */
- JsVar *jsvSkipName(JsVar *a) {
- JsVar *pa = a;
- if (!a) return 0;
- if (jsvIsArrayBufferName(pa)) return jsvArrayBufferGetFromName(pa);
- while (jsvIsName(pa)) {
- JsVarRef n = pa->firstChild;
- if (pa!=a) jsvUnLock(pa);
- if (!n) return 0;
- pa = jsvLock(n);
- }
- if (pa==a) jsvLockAgain(pa);
- return pa;
- }
- /** If a is a name skip it and go to what it points to.
- * ALWAYS locks - so must unlock what it returns. It MAY
- * return 0. */
- JsVar *jsvSkipOneName(JsVar *a) {
- JsVar *pa = a;
- if (!a) return 0;
- if (jsvIsArrayBufferName(pa)) return jsvArrayBufferGetFromName(pa);
- if (jsvIsName(pa)) {
- JsVarRef n = pa->firstChild;
- if (pa!=a) jsvUnLock(pa);
- if (!n) return 0;
- pa = jsvLock(n);
- }
- if (pa==a) jsvLockAgain(pa);
- return pa;
- }
- /** If a is a's child is a name skip it and go to what it points to.
- * ALWAYS locks - so must unlock what it returns. */
- JsVar *jsvSkipToLastName(JsVar *a) {
- assert(jsvIsName(a));
- a = jsvLockAgain(a);
- while (true) {
- if (!a->firstChild) return a;
- JsVar *child = jsvLock(a->firstChild);
- if (jsvIsName(child)) {
- jsvUnLock(a);
- a = child;
- } else {
- jsvUnLock(child);
- return a;
- }
- }
- return 0; // not called
- }
- // Also see jsvIsBasicVarEqual
- bool jsvIsStringEqual(JsVar *var, const char *str) {
- if (!jsvHasCharacterData(var)) {
- assert(jsvIsBasic(var));
- return 0; // not a string so not equal!
- }
- JsvStringIterator it;
- jsvStringIteratorNew(&it, var, 0);
- while (jsvStringIteratorHasChar(&it) && *str) {
- if (jsvStringIteratorGetChar(&it) != *str) {
- jsvStringIteratorFree(&it);
- return false;
- }
- str++;
- jsvStringIteratorNext(&it);
- }
- bool eq = jsvStringIteratorGetChar(&it)==*str; // should both be 0 if equal
- jsvStringIteratorFree(&it);
- return eq;
- }
- /** Compare 2 strings, starting from the given character positions. equalAtEndOfString means that
- * if one of the strings ends, we treat them as equal.
- * For a basic strcmp, do: jsvCompareString(a,b,0,0,false)
- * */
- int jsvCompareString(JsVar *va, JsVar *vb, int starta, int startb, bool equalAtEndOfString) {
- JsvStringIterator ita, itb;
- jsvStringIteratorNew(&ita, va, starta);
- jsvStringIteratorNew(&itb, vb, startb);
- // step to first positions
- while (true) {
- int ca = jsvStringIteratorGetCharOrMinusOne(&ita);
- int cb = jsvStringIteratorGetCharOrMinusOne(&itb);
- if (ca != cb) {
- jsvStringIteratorFree(&ita);
- jsvStringIteratorFree(&itb);
- if ((ca<0 || cb<0) && equalAtEndOfString) return 0;
- return ca - cb;
- }
- if (ca < 0) { // both equal, but end of string
- jsvStringIteratorFree(&ita);
- jsvStringIteratorFree(&itb);
- return 0;
- }
- jsvStringIteratorNext(&ita);
- jsvStringIteratorNext(&itb);
- }
- // never get here, but the compiler warns...
- return true;
- }
- /** Compare 2 integers, >0 if va>vb, <0 if va<vb. If compared with a non-integer, that gets put later */
- int jsvCompareInteger(JsVar *va, JsVar *vb) {
- if (jsvIsInt(va) && jsvIsInt(vb))
- return (int)(jsvGetInteger(va) - jsvGetInteger(vb));
- else if (jsvIsInt(va))
- return -1;
- else if (jsvIsInt(vb))
- return 1;
- else
- return 0;
- }
- /** Copy only a name, not what it points to. ALTHOUGH the link to what it points to is maintained unless linkChildren=false
- If keepAsName==false, this will be converted into a normal variable */
- JsVar *jsvCopyNameOnly(JsVar *src, bool linkChildren, bool keepAsName) {
- assert(jsvIsName(src));
- JsVarFlags flags = src->flags;
- if (!keepAsName) flags &= (JsVarFlags)~JSV_NAME; // make sure this is NOT a name
- JsVar *dst = jsvNewWithFlags(flags);
- if (!dst) return 0; // out of memory
- memcpy(&dst->varData, &src->varData, sizeof(JsVarData));
- dst->lastChild = 0;
- dst->firstChild = 0;
- dst->prevSibling = 0;
- dst->nextSibling = 0;
- // Copy LINK of what it points to
- if (linkChildren && src->firstChild) {
- dst->firstChild = jsvRefRef(src->firstChild);
- }
- // Copy extra string data if there was any
- if (jsvHasStringExt(src)) {
- // copy extra bits of string if there were any
- if (src->lastChild) {
- JsVar *child = jsvLock(src->lastChild);
- JsVar *childCopy = jsvCopy(child);
- if (childCopy) { // could be out of memory
- dst->lastChild = jsvGetRef(childCopy); // no ref for stringext
- jsvUnLock(childCopy);
- }
- jsvUnLock(child);
- }
- } else {
- assert(jsvIsBasic(src)); // in case we missed something!
- }
- return dst;
- }
- JsVar *jsvCopy(JsVar *src) {
- JsVar *dst = jsvNewWithFlags(src->flags);
- if (!dst) return 0; // out of memory
- if (!jsvIsStringExt(src)) {
- memcpy(&dst->varData, &src->varData, sizeof(JsVarData));
- dst->lastChild = 0;
- dst->firstChild = 0;
- dst->prevSibling = 0;
- dst->nextSibling = 0;
- } else {
- // stringexts use the extra pointers after varData to store characters
- // see jsvGetMaxCharactersInVar
- memcpy(&dst->varData, &src->varData, JSVAR_DATA_STRING_MAX_LEN);
- dst->lastChild = 0;
- }
- // Copy what names point to
- if (jsvIsName(src)) {
- if (src->firstChild) {
- JsVar *child = jsvLock(src->firstChild);
- JsVar *childCopy = jsvRef(jsvCopy(child));
- jsvUnLock(child);
- if (childCopy) { // could have been out of memory
- dst->firstChild = jsvGetRef(childCopy);
- jsvUnLock(childCopy);
- }
- }
- }
- if (jsvHasStringExt(src)) {
- // copy extra bits of string if there were any
- if (src->lastChild) {
- JsVar *child = jsvLock(src->lastChild);
- JsVar *childCopy = jsvCopy(child);
- if (childCopy) {// could be out of memory
- dst->lastChild = jsvGetRef(childCopy); // no ref for stringext
- jsvUnLock(childCopy);
- }
- jsvUnLock(child);
- }
- } else if (jsvHasChildren(src)) {
- // Copy children..
- JsVarRef vr;
- vr = src->firstChild;
- while (vr) {
- JsVar *name = jsvLock(vr);
- JsVar *child = jsvCopyNameOnly(name, true/*link children*/, true/*keep as name*/); // NO DEEP COPY!
- if (child) { // could have been out of memory
- jsvAddName(dst, child);
- jsvUnLock(child);
- }
- vr = name->nextSibling;
- jsvUnLock(name);
- }
- } else {
- assert(jsvIsBasic(src)); // in case we missed something!
- }
- return dst;
- }
- void jsvAddName(JsVar *parent, JsVar *namedChild) {
- namedChild = jsvRef(namedChild); // ref here VERY important as adding to structure!
- assert(jsvIsName(namedChild));
- if (parent->lastChild) { // we have children already
- JsVar *insertAfter = jsvLock(parent->lastChild);
- if (jsvIsArray(parent)) {
- // we must insert in order - so step back until we get the right place
- while (insertAfter && jsvCompareInteger(namedChild, insertAfter)<0) {
- JsVarRef prev = insertAfter->prevSibling;
- jsvUnLock(insertAfter);
- insertAfter = prev ? jsvLock(prev) : 0;
- }
- }
- if (insertAfter) {
- if (insertAfter->nextSibling) {
- // great, we're in the middle...
- JsVar *insertBefore = jsvLock(insertAfter->nextSibling);
- insertBefore->prevSibling = jsvGetRef(namedChild);
- namedChild->nextSibling = jsvGetRef(insertBefore);
- jsvUnLock(insertBefore);
- } else {
- // We're at the end - just set up the parent
- parent->lastChild = jsvGetRef(namedChild);
- }
- insertAfter->nextSibling = jsvGetRef(namedChild);
- namedChild->prevSibling = jsvGetRef(insertAfter);
- jsvUnLock(insertAfter);
- } else { // Insert right at the beginning of the array
- // Link 2 children together
- JsVar *firstChild = jsvLock(parent->firstChild);
- firstChild->prevSibling = jsvGetRef(namedChild);
- jsvUnLock(firstChild);
- namedChild->nextSibling = parent->firstChild;
- // finally set the new child as the first one
- parent->firstChild = jsvGetRef(namedChild);
- }
- } else { // we have no children - just add it
- parent->firstChild = parent->lastChild = jsvGetRef(namedChild);
- }
- }
- JsVar *jsvAddNamedChild(JsVar *parent, JsVar *child, const char *name) {
- JsVar *namedChild = jsvMakeIntoVariableName(jsvNewFromString(name), child);
- if (!namedChild) return 0; // Out of memory
- jsvAddName(parent, namedChild);
- return namedChild;
- }
- JsVar *jsvSetNamedChild(JsVar *parent, JsVar *child, const char *name) {
- JsVar *namedChild = jsvFindChildFromString(parent, name, true);
- if (namedChild) // could be out of memory
- return jsvSetValueOfName(namedChild, child);
- return 0;
- }
- JsVar *jsvSetValueOfName(JsVar *name, JsVar *src) {
- assert(name && jsvIsName(name));
- assert(name!=src); // no infinite loops!
- // all is fine, so replace the existing child...
- /* Existing child may be null in the case of Z = 0 where
- * we create 'Z' and pass it down to '=' to have the value
- * filled in (or it may be undefined). */
- if (name->firstChild) jsvUnRefRef(name->firstChild); // free existing
- if (src) {
- // we can link to a name if we want (so can remove the assert!)
- name->firstChild = jsvGetRef(jsvRef(src));
- } else
- name->firstChild = 0;
- return name;
- }
- JsVar *jsvFindChildFromString(JsVar *parent, const char *name, bool addIfNotFound) {
- /* Pull out first 4 bytes, and ensure that everything
- * is 0 padded so that we can do a nice speedy check. */
- char fastCheck[4];
- fastCheck[0] = name[0];
- if (name[0]) {
- fastCheck[1] = name[1];
- if (name[1]) {
- fastCheck[2] = name[2];
- if (name[2]) {
- fastCheck[3] = name[3];
- } else {
- fastCheck[3] = 0;
- }
- } else {
- fastCheck[2] = 0;
- fastCheck[3] = 0;
- }
- } else {
- fastCheck[1] = 0;
- fastCheck[2] = 0;
- fastCheck[3] = 0;
- }
- assert(jsvHasChildren(parent));
- JsVarRef childref = parent->firstChild;
- while (childref) {
- // Don't Lock here, just use GetAddressOf - to try and speed up the finding
- // TODO: We can do this now, but when/if we move to cacheing vars, it'll break
- JsVar *child = jsvGetAddressOf(childref);
- if (*(int*)fastCheck==*(int*)child->varData.str && // speedy check of first 4 bytes
- jsvIsStringEqual(child, name)) {
- // found it! unlock parent but leave child locked
- return jsvLockAgain(child);
- }
- childref = child->nextSibling;
- }
- JsVar *child = 0;
- if (addIfNotFound) {
- child = jsvMakeIntoVariableName(jsvNewFromString(name), 0);
- if (child) // could be out of memory
- jsvAddName(parent, child);
- }
- return child;
- }
- /** Non-recursive finding */
- JsVar *jsvFindChildFromVar(JsVar *parent, JsVar *childName, bool addIfNotFound) {
- JsVar *child;
- JsVarRef childref = parent->firstChild;
- while (childref) {
- child = jsvLock(childref);
- if (jsvIsBasicVarEqual(child, childName)) {
- // found it! unlock parent but leave child locked
- return child;
- }
- childref = child->nextSibling;
- jsvUnLock(child);
- }
- child = 0;
- if (addIfNotFound && childName) {
- if (childName->refs == 0) {
- // Not reffed - great! let's just use it
- if (!jsvIsName(childName))
- childName = jsvMakeIntoVariableName(childName, 0);
- child = jsvLockAgain(childName);
- } else { // it was reffed, we must add a new one
- child = jsvMakeIntoVariableName(jsvCopy(childName), 0);
- }
- jsvAddName(parent, child);
- }
- return child;
- }
- void jsvRemoveChild(JsVar *parent, JsVar *child) {
- assert(jsvHasChildren(parent));
- JsVarRef childref = jsvGetRef(child);
- // unlink from parent
- if (parent->firstChild == childref)
- parent->firstChild = child->nextSibling;
- if (parent->lastChild == childref)
- parent->lastChild = child->prevSibling;
- // unlink from child list
- if (child->prevSibling) {
- JsVar *v = jsvLock(child->prevSibling);
- v->nextSibling = child->nextSibling;
- jsvUnLock(v);
- }
- if (child->nextSibling) {
- JsVar *v = jsvLock(child->nextSibling);
- v->prevSibling = child->prevSibling;
- jsvUnLock(v);
- }
- child->prevSibling = 0;
- child->nextSibling = 0;
- jsvUnRef(child);
- }
- void jsvRemoveAllChildren(JsVar *parent) {
- assert(jsvHasChildren(parent));
- while (parent->firstChild) {
- JsVar *v = jsvLock(parent->firstChild);
- jsvRemoveChild(parent, v);
- jsvUnLock(v);
- }
- }
- /// Get the named child of an object. If createChild!=0 then create the child
- JsVar *jsvObjectGetChild(JsVar *obj, const char *name, JsVarFlags createChild) {
- if (!obj) return 0;
- assert(jsvHasChildren(obj));
- JsVar *childName = jsvFindChildFromString(obj, name, createChild);
- if (!childName && createChild) {
- JsVar *child = jsvNewWithFlags(createChild);
- jsvSetValueOfName(childName, child);
- jsvUnLock(childName);
- return child;
- }
- if (childName)
- return jsvSkipNameAndUnLock(childName);
- return 0;
- }
- /// Set the named child of an object, and return the child (so you can choose to unlock it if you want)
- JsVar *jsvObjectSetChild(JsVar *obj, const char *name, JsVar *child) {
- assert(jsvHasChildren(obj));
- // child can actually be a name (for instance if it is a named function)
- JsVar *childName = jsvFindChildFromString(obj, name, true);
- if (!childName) return 0; // out of memory
- jsvSetValueOfName(childName, child);
- jsvUnLock(childName);
- return child;
- }
- int jsvGetChildren(JsVar *v) {
- //OPT: could length be stored as the value of the array?
- int children = 0;
- JsVarRef childref = v->firstChild;
- while (childref) {
- JsVar *child = jsvLock(childref);
- children++;
- childref = child->nextSibling;
- jsvUnLock(child);
- }
- return children;
- }
- JsVarInt jsvGetArrayLength(JsVar *arr) {
- JsVarRef childref = arr->lastChild;
- // Just look at last non-string element!
- while (childref) {
- JsVar *child = jsvLock(childref);
- if (jsvIsInt(child)) {
- JsVarInt lastIdx = jsvGetInteger(child);
- jsvUnLock(child);
- return lastIdx+1;
- }
- // if not an int, keep going
- childref = child->prevSibling;
- jsvUnLock(child);
- }
- return 0;
- }
- JsVarInt jsvGetLength(JsVar *src) {
- if (jsvIsArray(src)) {
- return jsvGetArrayLength(src);
- } else if (jsvIsArrayBuffer(src)) {
- return (JsVarInt)jsvGetArrayBufferLength(src);
- } else if (jsvIsString(src)) {
- return (JsVarInt)jsvGetStringLength(src);
- } else if (jsvIsObject(src) || jsvIsFunction(src)) {
- return jsvGetChildren(src);
- } else {
- return 1;
- }
- }
- /** Count the amount of JsVars used. Mostly useful for debugging */
- size_t jsvCountJsVarsUsed(JsVar *v) {
- size_t count = 1;
- if (jsvHasChildren(v)) {
- JsVarRef childref = v->firstChild;
- while (childref) {
- JsVar *child = jsvLock(childref);
- count += jsvCountJsVarsUsed(child);
- childref = child->nextSibling;
- jsvUnLock(child);
- }
- }
- if (jsvHasCharacterData(v)) {
- size_t count = 0;
- JsVarRef childref = v->lastChild;
- while (childref) {
- JsVar *child = jsvLock(childref);
- count++;
- childref = child->lastChild;
- jsvUnLock(child);
- }
- }
- if (jsvIsName(v) && v->firstChild) {
- JsVar *child = jsvLock(v->firstChild);
- count += jsvCountJsVarsUsed(child);
- jsvUnLock(child);
- }
- return count;
- }
- JsVar *jsvGetArrayItem(JsVar *arr, int index) {
- JsVarRef childref = arr->firstChild;
- while (childref) {
- JsVarInt childIndex;
- JsVar *child = jsvLock(childref);
- assert(jsvIsInt(child));
- childIndex = jsvGetInteger(child);
- if (childIndex == index) {
- JsVar *item = child->firstChild ? jsvLock(child->firstChild) : 0;
- jsvUnLock(child);
- return item;
- }
- childref = child->nextSibling;
- jsvUnLock(child);
- }
- return 0; // undefined
- }
- /// Get the index of the value in the array (matchExact==use pointer, not equality check)
- JsVar *jsvGetArrayIndexOf(JsVar *arr, JsVar *value, bool matchExact) {
- JsVarRef indexref;
- assert(jsvIsArray(arr) || jsvIsObject(arr));
- indexref = arr->firstChild;
- while (indexref) {
- JsVar *childIndex = jsvLock(indexref);
- assert(jsvIsName(childIndex))
- if (childIndex->firstChild) {
- JsVar *childValue = jsvLock(childIndex->firstChild);
- if ((matchExact && childValue==value) ||
- (!matchExact && jsvIsBasicVarEqual(childValue, value))) {
- jsvUnLock(childValue);
- return childIndex;
- }
- jsvUnLock(childValue);
- } else if (jsvIsUndefined(value))
- return childIndex; // both are undefined, so we return the index
- indexref = childIndex->nextSibling;
- jsvUnLock(childIndex);
- }
- return 0; // undefined
- }
- /// Adds new elements to the end of an array, and returns the new length. initialValue is the item index when no items are currently in the array.
- JsVarInt jsvArrayPushWithInitialSize(JsVar *arr, JsVar *value, JsVarInt initialValue) {
- assert(jsvIsArray(arr));
- JsVarInt index = jsvGetArrayLength(arr);
- if (index==0) index=initialValue;
- JsVar *idx = jsvMakeIntoVariableName(jsvNewFromInteger(index), value);
- if (!idx) {
- jsWarn("Out of memory while appending to array");
- return 0;
- }
- jsvAddName(arr, idx);
- jsvUnLock(idx);
- return index+1; // new size
- }
- /// Adds new elements to the end of an array, and returns the new length
- JsVarInt jsvArrayPush(JsVar *arr, JsVar *value) {
- return jsvArrayPushWithInitialSize(arr, value, 0);
- }
- /// Adds a new element to the end of an array, unlocks it, and returns the new length
- JsVarInt jsvArrayPushAndUnLock(JsVar *arr, JsVar *value) {
- JsVarInt l = jsvArrayPushWithInitialSize(arr, value, 0);
- jsvUnLock(value);
- return l;
- }
- /// Removes the last element of an array, and returns that element (or 0 if empty). includes the NAME
- JsVar *jsvArrayPop(JsVar *arr) {
- assert(jsvIsArray(arr));
- if (arr->lastChild) {
- JsVar *child = jsvLock(arr->lastChild);
- if (arr->firstChild == arr->lastChild)
- arr->firstChild = 0; // if 1 item in array
- arr->lastChild = child->prevSibling; // unlink from end of array
- jsvUnRef(child); // as no longer in array
- if (child->prevSibling) {
- JsVar *v = jsvLock(child->prevSibling);
- v->nextSibling = 0;
- jsvUnLock(v);
- }
- child->prevSibling = 0;
- return child; // and return it
- } else {
- // no children!
- return 0;
- }
- }
- /// Removes the first element of an array, and returns that element (or 0 if empty).
- JsVar *jsvArrayPopFirst(JsVar *arr) {
- assert(jsvIsArray(arr));
- if (arr->firstChild) {
- JsVar *child = jsvLock(arr->firstChild);
- if (arr->firstChild == arr->lastChild)
- arr->lastChild = 0; // if 1 item in array
- arr->firstChild = child->nextSibling; // unlink from end of array
- jsvUnRef(child); // as no longer in array
- if (child->nextSibling) {
- JsVar *v = jsvLock(child->nextSibling);
- v->prevSibling = 0;
- jsvUnLock(v);
- }
- child->nextSibling = 0;
- return child; // and return it
- } else {
- // no children!
- return 0;
- }
- }
- /// Get the last element of an array (does not remove, unlike jsvArrayPop), and returns that element (or 0 if empty) includes the NAME
- JsVar *jsvArrayGetLast(JsVar *arr) {
- assert(jsvIsArray(arr));
- if (arr->lastChild) {
- return jsvLock(arr->lastChild);
- } else { // no children!
- return 0;
- }
- }
- /// Join all elements of an array together into a string
- JsVar *jsvArrayJoin(JsVar *arr, JsVar *filler) {
- JsVar *str = jsvNewFromEmptyString();
- if (!str) return 0; // out of memory
- JsVarInt index = 0;
- JsvIterator it;
- jsvIteratorNew(&it, arr);
- while (jsvIteratorHasElement(&it)) {
- JsVar *key = jsvIteratorGetKey(&it);
- if (jsvIsInt(key)) {
- JsVarInt thisIndex = jsvGetInteger(key);
- // add the filler
- if (filler) {
- while (index<thisIndex) {
- index++;
- jsvAppendStringVarComplete(str, filler);
- }
- }
- // add the value
- JsVar *value = jsvIteratorGetValue(&it);
- if (value) {
- JsVar *valueStr = jsvAsString(value, true /* UNLOCK */);
- if (valueStr) { // could be out of memory
- jsvAppendStringVarComplete(str, valueStr);
- jsvUnLock(valueStr);
- }
- }
- }
- jsvUnLock(key);
- jsvIteratorNext(&it);
- }
- jsvIteratorFree(&it);
- return str;
- }
- /// Insert a new element before beforeIndex, DOES NOT UPDATE INDICES
- void jsvArrayInsertBefore(JsVar *arr, JsVar *beforeIndex, JsVar *element) {
- if (beforeIndex) {
- JsVar *idxVar = jsvMakeIntoVariableName(jsvNewFromInteger(0), element);
- if (!idxVar) return; // out of memory
- JsVarRef idxRef = jsvGetRef(jsvRef(idxVar));
- JsVarRef prev = beforeIndex->prevSibling;
- if (prev) {
- JsVar *prevVar = jsvRef(jsvLock(prev));
- jsvSetInteger(idxVar, jsvGetInteger(prevVar)+1); // update index number
- prevVar->nextSibling = idxRef;
- jsvUnLock(prevVar);
- idxVar->prevSibling = prev;
- } else {
- idxVar->prevSibling = 0;
- arr->firstChild = idxRef;
- }
- beforeIndex->prevSibling = idxRef;
- idxVar->nextSibling = jsvGetRef(jsvRef(beforeIndex));
- jsvUnLock(idxVar);
- } else
- jsvArrayPush(arr, element);
- }
- /** Same as jsvMathsOpPtr, but if a or b are a name, skip them
- * and go to what they point to. */
- JsVar *jsvMathsOpSkipNames(JsVar *a, JsVar *b, int op) {
- JsVar *pa = jsvSkipName(a);
- JsVar *pb = jsvSkipName(b);
- JsVar *res = jsvMathsOp(pa,pb,op);
- jsvUnLock(pa);
- jsvUnLock(pb);
- return res;
- }
- JsVar *jsvMathsOpError(int op, const char *datatype) {
- char buf[JS_ERROR_BUF_SIZE];
- size_t bufpos = 0;
- strncpy(&buf[bufpos], "Operation ", JS_ERROR_BUF_SIZE-bufpos);
- bufpos=strlen(buf);
- jslTokenAsString(op, &buf[bufpos], JS_ERROR_TOKEN_BUF_SIZE-bufpos);
- bufpos=strlen(buf);
- strncat(&buf[bufpos], " not supported on the ", JS_ERROR_BUF_SIZE-bufpos);
- bufpos=strlen(buf);
- strncat(&buf[bufpos], datatype, JS_ERROR_BUF_SIZE-bufpos);
- bufpos=strlen(buf);
- strncat(&buf[bufpos], " datatype", JS_ERROR_BUF_SIZE-bufpos);
- jsError(buf);
- return 0;
- }
- JsVar *jsvMathsOp(JsVar *a, JsVar *b, int op) {
- // Type equality check
- if (op == LEX_TYPEEQUAL || op == LEX_NTYPEEQUAL) {
- // check type first, then call again to check data
- bool eql = (a==0) == (b==0);
- if (a && b) eql = ((a->flags & JSV_VARTYPEMASK) ==
- (b->flags & JSV_VARTYPEMASK));
- if (eql) {
- JsVar *contents = jsvMathsOp(a,b, LEX_EQUAL);
- if (!jsvGetBool(contents)) eql = false;
- jsvUnLock(contents);
- }
- if (op == LEX_TYPEEQUAL)
- return jsvNewFromBool(eql);
- else
- return jsvNewFromBool(!eql);
- }
- bool needsInt = op=='&' || op=='|' || op=='^' || op=='%' || op==LEX_LSHIFT || op==LEX_RSHIFT || op==LEX_RSHIFTUNSIGNED;
- bool needsNumeric = needsInt || op=='*' || op=='/' || op=='%' || op=='-';
- // do maths...
- if (jsvIsUndefined(a) && jsvIsUndefined(b)) {
- if (op == LEX_EQUAL)
- return jsvNewFromBool(true);
- else if (op == LEX_NEQUAL)
- return jsvNewFromBool(false);
- else
- return 0; // undefined
- } else if (needsNumeric ||
- ((jsvIsNumeric(a) || jsvIsUndefined(a) || jsvIsNull(a)) &&
- (jsvIsNumeric(b) || jsvIsUndefined(b) || jsvIsNull(b)))) {
- if (needsInt || !(jsvIsFloat(a) || jsvIsFloat(b))) {
- // use ints
- JsVarInt da = jsvGetInteger(a);
- JsVarInt db = jsvGetInteger(b);
- switch (op) {
- case '+': return jsvNewFromInteger(da+db);
- case '-': return jsvNewFromInteger(da-db);
- case '*': return jsvNewFromInteger(da*db);
- case '/': return jsvNewFromFloat((JsVarFloat)da/(JsVarFloat)db);
- case '&': return jsvNewFromInteger(da&db);
- case '|': return jsvNewFromInteger(da|db);
- case '^': return jsvNewFromInteger(da^db);
- case '%': return jsvNewFromInteger(da%db);
- case LEX_LSHIFT: return jsvNewFromInteger(da << db);
- case LEX_RSHIFT: return jsvNewFromInteger(da >> db);
- case LEX_RSHIFTUNSIGNED: return jsvNewFromInteger((JsVarInt)(((JsVarIntUnsigned)da) >> db));
- case LEX_EQUAL: return jsvNewFromBool(da==db);
- case LEX_NEQUAL: return jsvNewFromBool(da!=db);
- case '<': return jsvNewFromBool(da<db);
- case LEX_LEQUAL: return jsvNewFromBool(da<=db);
- case '>': return jsvNewFromBool(da>db);
- case LEX_GEQUAL: return jsvNewFromBool(da>=db);
- default: return jsvMathsOpError(op, "Integer");
- }
- } else {
- // use doubles
- JsVarFloat da = jsvGetFloat(a);
- JsVarFloat db = jsvGetFloat(b);
- switch (op) {
- case '+': return jsvNewFromFloat(da+db);
- case '-': return jsvNewFromFloat(da-db);
- case '*': return jsvNewFromFloat(da*db);
- case '/': return jsvNewFromFloat(da/db);
- case LEX_EQUAL: return jsvNewFromBool(da==db);
- case LEX_NEQUAL: return jsvNewFromBool(da!=db);
- case '<': return jsvNewFromBool(da<db);
- case LEX_LEQUAL: return jsvNewFromBool(da<=db);
- case '>': return jsvNewFromBool(da>db);
- case LEX_GEQUAL: return jsvNewFromBool(da>=db);
- default: return jsvMathsOpError(op, "Double");
- }
- }
- } else if ((jsvIsArray(a) || jsvIsObject(a) ||
- jsvIsArray(b) || jsvIsObject(b)) &&
- (op == LEX_EQUAL || op==LEX_NEQUAL)) {
- bool isArray = jsvIsArray(a);
- /* Just check pointers */
- switch (op) {
- case LEX_EQUAL: return jsvNewFromBool(a==b);
- case LEX_NEQUAL: return jsvNewFromBool(a!=b);
- default: return jsvMathsOpError(op, isArray?"Array":"Object");
- }
- } else {
- JsVar *da = jsvAsString(a, false);
- JsVar *db = jsvAsString(b, false);
- if (!da || !db) { // out of memory
- jsvUnLock(da);
- jsvUnLock(db);
- return 0;
- }
- if (op=='+') {
- JsVar *v = jsvCopy(da);
- // TODO: can we be fancy and not copy da if we know it isn't reffed? what about locks?
- if (v) // could be out of memory
- jsvAppendStringVarComplete(v, db);
- jsvUnLock(da);
- jsvUnLock(db);
- return v;
- }
- int cmp = jsvCompareString(da,db,0,0,false);
- jsvUnLock(da);
- jsvUnLock(db);
- // use strings
- switch (op) {
- case LEX_EQUAL: return jsvNewFromBool(cmp==0);
- case LEX_NEQUAL: return jsvNewFromBool(cmp!=0);
- case '<': return jsvNewFromBool(cmp<0);
- case LEX_LEQUAL: return jsvNewFromBool(cmp<=0);
- case '>': return jsvNewFromBool(cmp>0);
- case LEX_GEQUAL: return jsvNewFromBool(cmp>=0);
- default: return jsvMathsOpError(op, "String");
- }
- }
- }
- JsVar *jsvNegateAndUnLock(JsVar *v) {
- JsVar *zero = jsvNewFromInteger(0);
- JsVar *res = jsvMathsOpSkipNames(zero, v, '-');
- jsvUnLock(zero);
- jsvUnLock(v);
- return res;
- }
- void jsvTraceLockInfo(JsVar *v) {
- jsiConsolePrintf("#%d[r%d,l%d] ",jsvGetRef(v),v->refs,jsvGetLocks(v)-1);
- }
- /** Get the lowest level at which searchRef appears */
- int _jsvTraceGetLowestLevel(JsVarRef ref, JsVarRef searchRef) {
- if (ref == searchRef) return 0;
- int found = -1;
- JsVar *var = jsvLock(ref);
- // Use IS_RECURSING flag to stop recursion
- if (var->flags & JSV_IS_RECURSING) {
- jsvUnLock(var);
- return -1;
- }
- var->flags |= JSV_IS_RECURSING;
- if (jsvHasSingleChild(var) && var->firstChild) {
- int f = _jsvTraceGetLowestLevel(var->firstChild, searchRef);
- if (f>=0 && (found<0 || f<found)) found=f+1;
- }
- if (jsvHasChildren(var)) {
- JsVarRef childRef = var->firstChild;
- while (childRef) {
- int f = _jsvTraceGetLowestLevel(childRef, searchRef);
- if (f>=0 && (found<0 || f<found)) found=f+1;
- JsVar *child = jsvLock(childRef);
- childRef = child->nextSibling;
- jsvUnLock(child);
- }
- }
- var->flags &= ~JSV_IS_RECURSING;
- jsvUnLock(var);
- return found; // searchRef not found
- }
- void _jsvTrace(JsVarRef ref, int indent, JsVarRef baseRef, int level) {
- #ifdef SAVE_ON_FLASH
- jsiConsolePrint("Trace unimplemented in this version.\n");
- #else
- int i;
- for (i=0;i<indent;i++) jsiConsolePrint(" ");
- if (!ref) {
- jsiConsolePrint("undefined\n");
- return;
- }
- /*jsiConsolePrint("<");
- jsiConsolePrintInt(level);
- jsiConsolePrint(":");
- jsiConsolePrintInt(_jsvTraceGetLowestLevel(baseRef, ref));
- jsiConsolePrint("> ");*/
- JsVar *var = jsvLock(ref);
- jsvTraceLockInfo(var);
- if (jsvIsName(var)) {
- if (jsvIsFunctionParameter(var))
- jsiConsolePrint("Param ");
- JsVar *str = jsvAsString(var, false);
- if (jsvIsInt(var)) {
- jsiConsolePrintf("Name: int %v ", str);
- } else if (jsvIsFloat(var)) {
- jsiConsolePrintf("Name: flt %v ", str);
- } else if (jsvIsString(var) || jsvIsFunctionParameter(var)) {
- jsiConsolePrintf("Name: '%v' ", str);
- } else if (jsvIsArrayBufferName(var)) {
- jsiConsolePrintf("ArrayBufferName[%d] ", jsvGetInteger(var));
- } else {
- assert(0);
- }
- jsvUnLock(str);
- // go to what the name points to
- ref = var->firstChild;
- jsvUnLock(var);
- if (ref) {
- level++;
- int lowestLevel = _jsvTraceGetLowestLevel(baseRef, ref);
- /*jsiConsolePrint("<");
- jsiConsolePrintInt(level);
- jsiConsolePrint(":");
- jsiConsolePrintInt(lowestLevel);
- jsiConsolePrint("> ");*/
- var = jsvLock(ref);
- jsvTraceLockInfo(var);
- if (lowestLevel < level) {
- // If this data is available elsewhere in the tree (but nearer the root)
- // then don't print it. This makes the dump significantly more readable!
- // It also stops us getting in recursive loops ...
- jsiConsolePrint("...\n");
- jsvUnLock(var);
- return;
- }
- } else {
- jsiConsolePrint("undefined\n");
- return;
- }
- }
- if (jsvIsName(var)) {
- jsiConsolePrint("\n");
- _jsvTrace(jsvGetRef(var), indent+2, baseRef, level+1);
- jsvUnLock(var);
- return;
- }
- if (jsvIsObject(var)) jsiConsolePrint("Object {");
- else if (jsvIsArray(var)) jsiConsolePrint("Array [");
- else if (jsvIsPin(var)) jsiConsolePrint("Pin ");
- else if (jsvIsInt(var)) jsiConsolePrint("Integer ");
- else if (jsvIsBoolean(var)) jsiConsolePrint("Bool ");
- else if (jsvIsFloat(var)) jsiConsolePrint("Double ");
- else if (jsvIsString(var)) jsiConsolePrint("String ");
- else if (jsvIsArrayBuffer(var)) {
- jsiConsolePrintf("%s ", jswGetBasicObjectName(var)); // way to get nice name
- _jsvTrace(var->firstChild, indent+1, baseRef, level+1);
- jsvUnLock(var);
- return;
- } else if (jsvIsFunction(var)) jsiConsolePrint("Function {");
- else {
- jsiConsolePrintf("Flags %d\n", var->flags & (JsVarFlags)~(JSV_LOCK_MASK));
- }
- if (!jsvIsObject(var) && !jsvIsArray(var) && !jsvIsFunction(var)) {
- JsVar *str = jsvAsString(var, false);
- if (str) {
- JsvStringIterator it;
- jsvStringIteratorNew(&it, str, 0);
- while (jsvStringIteratorHasChar(&it)) {
- char ch = jsvStringIteratorGetChar(&it);
- jsiConsolePrint(escapeCharacter(ch));
- jsvStringIteratorNext(&it);
- }
- jsvStringIteratorFree(&it);
- jsvUnLock(str);
- }
- }
- if (jsvHasStringExt(var)) {
- if (!jsvIsStringExt(var) && var->firstChild) { // stringext don't have children (the use them for chars)
- jsiConsolePrint("( Multi-block string ");
- JsVarRef child = var->firstChild;
- while (child) {
- JsVar *childVar = jsvLock(child);
- jsvTraceLockInfo(childVar);
- child = childVar->firstChild;
- jsvUnLock(childVar);
- }
- jsiConsolePrint(")\n");
- } else
- jsiConsolePrint("\n");
- } else {
- JsVarRef child = var->firstChild;
- jsiConsolePrint("\n");
- // dump children
- while (child) {
- JsVar *childVar;
- _jsvTrace(child, indent+2, baseRef, level+1);
- childVar = jsvLock(child);
- child = childVar->nextSibling;
- jsvUnLock(childVar);
- }
- }
- if (jsvIsObject(var) || jsvIsFunction(var) || jsvIsArray(var)) {
- int i;
- for (i=0;i<indent;i++) jsiConsolePrint(" ");
- jsiConsolePrint(jsvIsArray(var) ? "]\n" : "}\n");
- }
- jsvUnLock(var);
- #endif
- }
- /** Write debug info for this Var out to the console */
- void jsvTrace(JsVarRef ref, int indent) {
- _jsvTrace(ref,indent,ref,0);
- }
- /** Recursively mark the variable */
- static void jsvGarbageCollectMarkUsed(JsVar *var) {
- var->flags &= (JsVarFlags)~JSV_GARBAGE_COLLECT;
- if (jsvHasCharacterData(var)) {
- if (var->lastChild) {
- JsVar *childVar = jsvGetAddressOf(var->lastChild);
- if (childVar->flags & JSV_GARBAGE_COLLECT)
- jsvGarbageCollectMarkUsed(childVar);
- }
- }
- // intentionally no else
- if (jsvHasSingleChild(var)) {
- if (var->firstChild) {
- JsVar *childVar = jsvGetAddressOf(var->firstChild);
- if (childVar->flags & JSV_GARBAGE_COLLECT)
- jsvGarbageCollectMarkUsed(childVar);
- }
- } else if (jsvHasChildren(var)) {
- JsVarRef child = var->firstChild;
- while (child) {
- JsVar *childVar;
- childVar = jsvGetAddressOf(child);
- if (childVar->flags & JSV_GARBAGE_COLLECT)
- jsvGarbageCollectMarkUsed(childVar);
- child = childVar->nextSibling;
- }
- }
- }
- /** Run a garbage collection sweep - return true if things have been freed */
- bool jsvGarbageCollect() {
- JsVarRef i;
- // clear garbage collect flags
- for (i=1;i<=jsVarsSize;i++) {
- JsVar *var = jsvGetAddressOf(i);
- if ((var->flags&JSV_VARTYPEMASK) != JSV_UNUSED) // if it is not unused
- var->flags |= (JsVarFlags)JSV_GARBAGE_COLLECT;
- }
- // recursively add 'native' vars
- for (i=1;i<=jsVarsSize;i++) {
- JsVar *var = jsvGetAddressOf(i);
- if ((var->flags & JSV_GARBAGE_COLLECT) && // not already GC'd
- jsvGetLocks(var)>0) // or it is locked
- jsvGarbageCollectMarkUsed(var);
- }
- // now sweep for things that we can GC!
- bool freedSomething = false;
- for (i=1;i<=jsVarsSize;i++) {
- JsVar *var = jsvGetAddressOf(i);
- if (var->flags & JSV_GARBAGE_COLLECT) {
- freedSomething = true;
- // free!
- var->flags = JSV_UNUSED;
- // add this to our free list
- var->nextSibling = jsVarFirstEmpty;
- jsVarFirstEmpty = jsvGetRef(var);
- }
- }
- return freedSomething;
- }
- /** Remove whitespace to the right of a string - on MULTIPLE LINES */
- JsVar *jsvStringTrimRight(JsVar *srcString) {
- JsvStringIterator src, dst;
- JsVar *dstString = jsvNewFromEmptyString();
- jsvStringIteratorNew(&src, srcString, 0);
- jsvStringIteratorNew(&dst, dstString, 0);
- int spaces = 0;
- while (jsvStringIteratorHasChar(&src)) {
- char ch = jsvStringIteratorGetChar(&src);
- jsvStringIteratorNext(&src);
- if (ch==' ') spaces++;
- else if (ch=='\n') {
- spaces = 0;
- jsvStringIteratorAppend(&dst, ch);
- } else {
- for (;spaces>0;spaces--)
- jsvStringIteratorAppend(&dst, ' ');
- jsvStringIteratorAppend(&dst, ch);
- }
- }
- jsvStringIteratorFree(&src);
- jsvStringIteratorFree(&dst);
- return dstString;
- }
- /// If v is the key of a function, return true if it is internal and shouldn't be visible to the user
- bool jsvIsInternalFunctionKey(JsVar *v) {
- return (jsvIsString(v) && (
- v->varData.str[0]==JS_HIDDEN_CHAR)
- ) ||
- jsvIsFunctionParameter(v);
- }
- /// If v is the key of an object, return true if it is internal and shouldn't be visible to the user
- bool jsvIsInternalObjectKey(JsVar *v) {
- return (jsvIsString(v) && (
- v->varData.str[0]==JS_HIDDEN_CHAR ||
- jsvIsStringEqual(v, JSPARSE_INHERITS_VAR) ||
- jsvIsStringEqual(v, JSPARSE_CONSTRUCTOR_VAR)
- ));
- }
- // --------------------------------------------------------------------------------------------
- void jsvStringIteratorNew(JsvStringIterator *it, JsVar *str, int startIdx) {
- assert(jsvHasCharacterData(str));
- it->var = jsvLockAgain(str);
- it->charsInVar = jsvGetCharactersInVar(str);
- it->charIdx = (size_t)startIdx;
- it->index = (size_t)startIdx;
- while (it->charIdx>0 && it->charIdx >= it->charsInVar) {
- it->charIdx -= it->charsInVar;
- if (it->var) {
- if (it->var->lastChild) {
- JsVar *next = jsvLock(it->var->lastChild);
- jsvUnLock(it->var);
- it->var = next;
- it->charsInVar = jsvGetCharactersInVar(it->var);
- } else {
- jsvUnLock(it->var);
- it->var = 0;
- it->charsInVar = 0;
- return; // get out of loop
- }
- }
- }
- }
- void jsvStringIteratorNext(JsvStringIterator *it) {
- jsvStringIteratorNextInline(it);
- }
- void jsvStringIteratorGotoEnd(JsvStringIterator *it) {
- assert(it->var);
- while (it->var->lastChild) {
- it->index += it->charsInVar;
- JsVar *next = jsvLock(it->var->lastChild);
- jsvUnLock(it->var);
- it->var = next;
- it->charsInVar = jsvGetCharactersInVar(it->var);
- }
- if (it->charsInVar) it->charIdx = it->charsInVar-1;
- else it->charIdx = 0;
- }
- void jsvStringIteratorAppend(JsvStringIterator *it, char ch) {
- if (!it->var) return;
- if (it->charsInVar>0) {
- assert(it->charIdx+1 == it->charsInVar /* check at end */);
- it->charIdx++;
- it->index++;
- } else
- assert(it->charIdx == 0);
- if (it->charIdx >= jsvGetMaxCharactersInVar(it->var)) {
- assert(!it->var->lastChild);
- JsVar *next = jsvNewWithFlags(JSV_STRING_EXT);
- if (!next) return; // out of memory
- // we don't ref, because StringExts are never reffed as they only have one owner (and ALWAYS have an owner)
- it->var->lastChild = jsvGetRef(next);
- jsvUnLock(it->var);
- it->var = next;
- it->charIdx = 0; // it's new, so empty
- }
- it->var->varData.str[it->charIdx] = ch;
- it->charsInVar = it->charIdx+1;
- jsvSetCharactersInVar(it->var, it->charsInVar);
- }
- // --------------------------------------------------------------------------------------------
- void jsvArrayBufferIteratorNew(JsvArrayBufferIterator *it, JsVar *arrayBuffer, JsVarInt index) {
- assert(jsvIsArrayBuffer(arrayBuffer));
- it->index = index;
- it->type = arrayBuffer->varData.arraybuffer.type;
- it->byteLength = arrayBuffer->varData.arraybuffer.length * JSV_ARRAYBUFFER_GET_SIZE(it->type);
- it->byteOffset = arrayBuffer->varData.arraybuffer.byteOffset;
- JsVar *arrayBufferData = jsvLock(arrayBuffer->firstChild);
- while (jsvIsArrayBuffer(arrayBufferData)) {
- JsVar *s = jsvLock(arrayBufferData->firstChild);
- jsvUnLock(arrayBufferData);
- arrayBufferData = s;
- }
- assert(jsvIsString(arrayBufferData));
- it->byteLength += it->byteOffset; // because we'll check if we have more bytes using this
- it->byteOffset = it->byteOffset + index*JSV_ARRAYBUFFER_GET_SIZE(it->type);
- if (it->byteOffset<0 || (it->byteLength>=0 && it->byteOffset>=(it->byteLength+1-JSV_ARRAYBUFFER_GET_SIZE(it->type)))) {
- jsvUnLock(arrayBufferData);
- it->type = ARRAYBUFFERVIEW_UNDEFINED;
- return;
- }
- jsvStringIteratorNew(&it->it, arrayBufferData, (int)it->byteOffset);
- jsvUnLock(arrayBufferData);
- it->hasAccessedElement = false;
- }
- static void jsvArrayBufferIteratorGetValueData(JsvArrayBufferIterator *it, char *data) {
- if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return;
- assert(!it->hasAccessedElement); // we just haven't implemented this case yet
- unsigned int i,dataLen = JSV_ARRAYBUFFER_GET_SIZE(it->type);
- for (i=0;i<dataLen;i++) {
- data[i] = jsvStringIteratorGetChar(&it->it);
- if (dataLen!=1) jsvStringIteratorNext(&it->it);
- }
- if (dataLen!=1) it->hasAccessedElement = true;
- }
- static JsVarInt jsvArrayBufferIteratorDataToInt(JsvArrayBufferIterator *it, char *data) {
- unsigned int dataLen = JSV_ARRAYBUFFER_GET_SIZE(it->type);
- JsVarInt v = 0;
- if (dataLen==1) v = *(char*)data;
- else if (dataLen==2) v = *(short*)data;
- else if (dataLen==4) v = *(int*)data;
- else if (dataLen==8) v = *(long long*)data;
- else assert(0);
- if ((!JSV_ARRAYBUFFER_IS_SIGNED(it->type)) && v<0)
- v += 1 << (8*dataLen);
- return v;
- }
- static JsVarFloat jsvArrayBufferIteratorDataToFloat(JsvArrayBufferIterator *it, char *data) {
- unsigned int dataLen = JSV_ARRAYBUFFER_GET_SIZE(it->type);
- JsVarFloat v = 0;
- if (dataLen==4) v = *(float*)data;
- else if (dataLen==8) v = *(double*)data;
- else assert(0);
- return v;
- }
- JsVar *jsvArrayBufferIteratorGetValue(JsvArrayBufferIterator *it) {
- if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return 0;
- char data[8];
- jsvArrayBufferIteratorGetValueData(it, data);
- if (JSV_ARRAYBUFFER_IS_FLOAT(it->type)) {
- return jsvNewFromFloat(jsvArrayBufferIteratorDataToFloat(it, data));
- } else {
- return jsvNewFromInteger(jsvArrayBufferIteratorDataToInt(it, data));
- }
- }
- JsVarInt jsvArrayBufferIteratorGetIntegerValue(JsvArrayBufferIterator *it) {
- if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return 0;
- char data[8];
- jsvArrayBufferIteratorGetValueData(it, data);
- if (JSV_ARRAYBUFFER_IS_FLOAT(it->type)) {
- return (JsVarInt)jsvArrayBufferIteratorDataToFloat(it, data);
- } else {
- return jsvArrayBufferIteratorDataToInt(it, data);
- }
- }
- JsVarFloat jsvArrayBufferIteratorGetFloatValue(JsvArrayBufferIterator *it) {
- if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return 0;
- char data[8];
- jsvArrayBufferIteratorGetValueData(it, data);
- if (JSV_ARRAYBUFFER_IS_FLOAT(it->type)) {
- return jsvArrayBufferIteratorDataToFloat(it, data);
- } else {
- return (JsVarFloat)jsvArrayBufferIteratorDataToInt(it, data);
- }
- }
- void jsvArrayBufferIteratorSetValue(JsvArrayBufferIterator *it, JsVar *value) {
- if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return;
- assert(!it->hasAccessedElement); // we just haven't implemented this case yet
- char data[8];
- unsigned int i,dataLen = JSV_ARRAYBUFFER_GET_SIZE(it->type);
- if (JSV_ARRAYBUFFER_IS_FLOAT(it->type)) {
- JsVarFloat v = jsvGetFloat(value); ;
- if (dataLen==4) { float f = (float)v; memcpy(data,&f,dataLen); }
- else if (dataLen==8) { double f = (double)v; memcpy(data,&f,dataLen); }
- else assert(0);
- } else {
- JsVarInt v = jsvGetInteger(value);
- // we don't care about sign when writing - as it gets truncated
- if (dataLen==1) { char c = (char)v; memcpy(data,&c,dataLen); }
- else if (dataLen==2) { short c = (short)v; memcpy(data,&c,dataLen); }
- else if (dataLen==4) { int c = (int)v; memcpy(data,&c,dataLen); }
- else if (dataLen==8) { long long c = (long long)v; memcpy(data,&c,dataLen); }
- else assert(0);
- }
- for (i=0;i<dataLen;i++) {
- jsvStringIteratorSetChar(&it->it, data[i]);
- if (dataLen!=1) jsvStringIteratorNext(&it->it);
- }
- if (dataLen!=1) it->hasAccessedElement = true;
- }
- void jsvArrayBufferIteratorSetIntegerValue(JsvArrayBufferIterator *it, JsVarInt value) {
- // FIXME: Do this without the allocation!
- JsVar *val = jsvNewFromInteger(value);
- jsvArrayBufferIteratorSetValue(it, val);
- jsvUnLock(val);
- }
- JsVar* jsvArrayBufferIteratorGetIndex(JsvArrayBufferIterator *it) {
- return jsvNewFromInteger(it->index);
- }
- bool jsvArrayBufferIteratorHasElement(JsvArrayBufferIterator *it) {
- if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return false;
- if (it->hasAccessedElement) return true;
- if (it->byteLength>=0)
- return it->byteOffset <= (it->byteLength-JSV_ARRAYBUFFER_GET_SIZE(it->type));
- else
- return jsvStringIteratorHasChar(&it->it);
- }
- void jsvArrayBufferIteratorNext(JsvArrayBufferIterator *it) {
- it->index++;
- it->byteOffset += JSV_ARRAYBUFFER_GET_SIZE(it->type);
- if (!it->hasAccessedElement) {
- unsigned int i,dataLen = JSV_ARRAYBUFFER_GET_SIZE(it->type);
- for (i=0;i<dataLen;i++)
- jsvStringIteratorNext(&it->it);
- } else
- it->hasAccessedElement = false;
- }
- void jsvArrayBufferIteratorFree(JsvArrayBufferIterator *it) {
- if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return;
- jsvStringIteratorFree(&it->it);
- }
- // --------------------------------------------------------------------------------------------
- /* General Purpose iterator, for Strings, Arrays, Objects, Typed Arrays */
- void jsvIteratorNew(JsvIterator *it, JsVar *obj) {
- if (jsvIsArray(obj)) {
- it->type = JSVI_ARRAY;
- jsvArrayIteratorNew(&it->it.arr, obj);
- } else if (jsvIsObject(obj) || jsvIsFunction(obj)) {
- it->type = JSVI_OBJECT;
- jsvObjectIteratorNew(&it->it.obj, obj);
- } else if (jsvIsArrayBuffer(obj)) {
- it->type = JSVI_ARRAYBUFFER;
- jsvArrayBufferIteratorNew(&it->it.buf, obj, 0);
- } else if (jsvHasCharacterData(obj)) {
- it->type = JSVI_STRING;
- jsvStringIteratorNew(&it->it.str, obj, 0);
- } else assert(0);
- }
- JsVar *jsvIteratorGetKey(JsvIterator *it) {
- switch (it->type) {
- case JSVI_ARRAY : return jsvArrayIteratorGetIndex(&it->it.arr);
- case JSVI_OBJECT : return jsvObjectIteratorGetKey(&it->it.obj);
- case JSVI_STRING : return jsvNewFromInteger((JsVarInt)jsvStringIteratorGetIndex(&it->it.str));
- case JSVI_ARRAYBUFFER : return jsvArrayBufferIteratorGetIndex(&it->it.buf);
- default: assert(0); return 0;
- }
- }
- JsVar *jsvIteratorGetValue(JsvIterator *it) {
- switch (it->type) {
- case JSVI_ARRAY : return jsvArrayIteratorGetElement(&it->it.arr);
- case JSVI_OBJECT : return jsvObjectIteratorGetValue(&it->it.obj);
- case JSVI_STRING : { char buf[2] = {jsvStringIteratorGetChar(&it->it.str),0}; return jsvNewFromString(buf); }
- case JSVI_ARRAYBUFFER : return jsvArrayBufferIteratorGetValue(&it->it.buf);
- default: assert(0); return 0;
- }
- }
- JsVarInt jsvIteratorGetIntegerValue(JsvIterator *it) {
- switch (it->type) {
- case JSVI_ARRAY : return jsvGetIntegerAndUnLock(jsvArrayIteratorGetElement(&it->it.arr));
- case JSVI_OBJECT : return jsvGetIntegerAndUnLock(jsvObjectIteratorGetValue(&it->it.obj));
- case JSVI_STRING : return (JsVarInt)jsvStringIteratorGetChar(&it->it.str);
- case JSVI_ARRAYBUFFER : return jsvArrayBufferIteratorGetIntegerValue(&it->it.buf);
- default: assert(0); return 0;
- }
- }
- bool jsvIteratorHasElement(JsvIterator *it) {
- switch (it->type) {
- case JSVI_ARRAY : return jsvArrayIteratorHasElement(&it->it.arr);
- case JSVI_OBJECT : return jsvObjectIteratorHasElement(&it->it.obj);
- case JSVI_STRING : return jsvStringIteratorHasChar(&it->it.str);
- case JSVI_ARRAYBUFFER : return jsvArrayBufferIteratorHasElement(&it->it.buf);
- default: assert(0); return 0;
- }
- }
- void jsvIteratorNext(JsvIterator *it) {
- switch (it->type) {
- case JSVI_ARRAY : jsvArrayIteratorNext(&it->it.arr); break;
- case JSVI_OBJECT : jsvObjectIteratorNext(&it->it.obj); break;
- case JSVI_STRING : jsvStringIteratorNext(&it->it.str); break;
- case JSVI_ARRAYBUFFER : jsvArrayBufferIteratorNext(&it->it.buf); break;
- default: assert(0); break;
- }
- }
- void jsvIteratorFree(JsvIterator *it) {
- switch (it->type) {
- case JSVI_ARRAY : jsvArrayIteratorFree(&it->it.arr); break;
- case JSVI_OBJECT : jsvObjectIteratorFree(&it->it.obj); break;
- case JSVI_STRING : jsvStringIteratorFree(&it->it.str); break;
- case JSVI_ARRAYBUFFER : jsvArrayBufferIteratorFree(&it->it.buf); break;
- default: assert(0); break;
- }
- }
|