jsvar.c 79 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542
  1. /*
  2. * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
  3. *
  4. * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
  5. *
  6. * This Source Code Form is subject to the terms of the Mozilla Public
  7. * License, v. 2.0. If a copy of the MPL was not distributed with this
  8. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  9. *
  10. * ----------------------------------------------------------------------------
  11. * Variables
  12. * ----------------------------------------------------------------------------
  13. */
  14. #include "jsvar.h"
  15. #include "jslex.h"
  16. #include "jsparse.h"
  17. #include "jswrap_json.h"
  18. #include "jsinteractive.h"
  19. #include "jswrapper.h"
  20. /** Basically, JsVars are stored in one big array, so save the need for
  21. * lots of memory allocation. On Linux, the arrays are in blocks, so that
  22. * more blocks can be allocated. We can't use realloc on one big block as
  23. * this may change the address of vars that are already locked!
  24. *
  25. */
  26. #ifdef RESIZABLE_JSVARS
  27. JsVar **jsVarBlocks = 0;
  28. unsigned int jsVarsSize = 0;
  29. #define JSVAR_BLOCK_SIZE 1024
  30. #define JSVAR_BLOCK_SHIFT 10
  31. #else
  32. JsVar jsVars[JSVAR_CACHE_SIZE];
  33. unsigned int jsVarsSize = JSVAR_CACHE_SIZE;
  34. #endif
  35. JsVarRef jsVarFirstEmpty; ///< reference of first unused variable (variables are in a linked list)
  36. /** Return a pointer - UNSAFE for null refs.
  37. * This is effectively a Lock without locking! */
  38. static inline JsVar *jsvGetAddressOf(JsVarRef ref) {
  39. assert(ref);
  40. #ifdef RESIZABLE_JSVARS
  41. JsVarRef t = ref-1;
  42. return &jsVarBlocks[t>>JSVAR_BLOCK_SHIFT][t&(JSVAR_BLOCK_SIZE-1)];
  43. #else
  44. return &jsVars[ref-1];
  45. #endif
  46. }
  47. JsVar *_jsvGetAddressOf(JsVarRef ref) {
  48. return jsvGetAddressOf(ref);
  49. }
  50. // For debugging/testing ONLY - maximum # of vars we are allowed to use
  51. void jsvSetMaxVarsUsed(unsigned int size) {
  52. #ifdef RESIZABLE_JSVARS
  53. assert(size < JSVAR_BLOCK_SIZE); // remember - this is only for DEBUGGING - as such it doesn't use multiple blocks
  54. #else
  55. assert(size < JSVAR_CACHE_SIZE);
  56. #endif
  57. jsVarsSize = size;
  58. }
  59. // maps the empty variables in...
  60. void jsvSoftInit() {
  61. jsVarFirstEmpty = 0;
  62. JsVar *lastEmpty = 0;
  63. JsVarRef i;
  64. for (i=1;i<=jsVarsSize;i++) {
  65. if ((jsvGetAddressOf(i)->flags&JSV_VARTYPEMASK) == JSV_UNUSED) {
  66. jsvGetAddressOf(i)->nextSibling = 0;
  67. if (lastEmpty)
  68. lastEmpty->nextSibling = i;
  69. else
  70. jsVarFirstEmpty = i;
  71. lastEmpty = jsvGetAddressOf(i);
  72. }
  73. }
  74. }
  75. void jsvSoftKill() {
  76. }
  77. /** This links all JsVars together, so we can have our nice
  78. * linked list of free JsVars. It returns the ref of the first
  79. * item - that we should set jsVarFirstEmpty to (if it is 0) */
  80. static JsVarRef jsvInitJsVars(JsVarRef start, unsigned int count) {
  81. JsVarRef i;
  82. for (i=start;i<start+count;i++) {
  83. JsVar *v = jsvGetAddressOf(i);
  84. v->flags = JSV_UNUSED;
  85. #ifdef LARGE_MEM
  86. v->this = i;
  87. #endif
  88. // v->locks = 0; // locks is 0 anyway because it is stored in flags
  89. v->nextSibling = (JsVarRef)(i+1); // link to next
  90. }
  91. jsvGetAddressOf((JsVarRef)(start+count-1))->nextSibling = (JsVarRef)0; // set the final one to 0
  92. return start;
  93. }
  94. void jsvInit() {
  95. #ifdef RESIZABLE_JSVARS
  96. jsVarsSize = JSVAR_BLOCK_SIZE;
  97. jsVarBlocks = malloc(sizeof(JsVar*)); // just 1
  98. jsVarBlocks[0] = malloc(sizeof(JsVar) * JSVAR_BLOCK_SIZE);
  99. #endif
  100. jsVarFirstEmpty = jsvInitJsVars(1/*first*/, jsVarsSize);
  101. jsvSoftInit();
  102. }
  103. void jsvKill() {
  104. #ifdef RESIZABLE_JSVARS
  105. unsigned int i;
  106. for (i=0;i<jsVarsSize>>JSVAR_BLOCK_SHIFT;i++)
  107. free(jsVarBlocks[i]);
  108. free(jsVarBlocks);
  109. jsVarBlocks = 0;
  110. jsVarsSize = 0;
  111. #endif
  112. }
  113. /** Find or create the ROOT variable item - used mainly
  114. * if recovering from a saved state. */
  115. JsVar *jsvFindOrCreateRoot() {
  116. JsVarRef i;
  117. for (i=1;i<=jsVarsSize;i++)
  118. if (jsvIsRoot(jsvGetAddressOf(i)))
  119. return jsvLock(i);
  120. return jsvRef(jsvNewWithFlags(JSV_ROOT));
  121. }
  122. /// Get number of memory records (JsVars) used
  123. unsigned int jsvGetMemoryUsage() {
  124. unsigned int usage = 0;
  125. unsigned int i;
  126. for (i=1;i<=jsVarsSize;i++) {
  127. JsVar *v = jsvGetAddressOf((JsVarRef)i);
  128. if ((v->flags&JSV_VARTYPEMASK) != JSV_UNUSED)
  129. usage++;
  130. }
  131. return usage;
  132. }
  133. /// Get total amount of memory records
  134. unsigned int jsvGetMemoryTotal() {
  135. return jsVarsSize;
  136. }
  137. /// Try and allocate more memory - only works if RESIZABLE_JSVARS is defined
  138. void jsvSetMemoryTotal(unsigned int jsNewVarCount) {
  139. #ifdef RESIZABLE_JSVARS
  140. if (jsNewVarCount <= jsVarsSize) return; // never allow us to have less!
  141. // When resizing, we just allocate a bunch more
  142. unsigned int oldSize = jsVarsSize;
  143. unsigned int oldBlockCount = jsVarsSize >> JSVAR_BLOCK_SHIFT;
  144. unsigned int newBlockCount = (jsNewVarCount+JSVAR_BLOCK_SIZE-1) >> JSVAR_BLOCK_SHIFT;
  145. jsVarsSize = newBlockCount << JSVAR_BLOCK_SHIFT;
  146. // resize block table
  147. jsVarBlocks = realloc(jsVarBlocks, sizeof(JsVar*)*newBlockCount);
  148. // allocate more blocks
  149. unsigned int i;
  150. for (i=oldBlockCount;i<newBlockCount;i++)
  151. jsVarBlocks[i] = malloc(sizeof(JsVar) * JSVAR_BLOCK_SIZE);
  152. /** and now reset all the newly allocated vars. We know jsVarFirstEmpty
  153. * is 0 (because jsiFreeMoreMemory returned 0) so we can just assign it. */
  154. assert(!jsVarFirstEmpty);
  155. jsVarFirstEmpty = jsvInitJsVars(oldSize+1, jsVarsSize-oldSize);
  156. // jsiConsolePrintf("Resized memory from %d blocks to %d\n", oldBlockCount, newBlockCount);
  157. #else
  158. NOT_USED(jsNewVarCount);
  159. assert(0);
  160. #endif
  161. }
  162. /// Get whether memory is full or not
  163. bool jsvIsMemoryFull() {
  164. return !jsVarFirstEmpty;
  165. }
  166. // Show what is still allocated, for debugging memory problems
  167. void jsvShowAllocated() {
  168. JsVarRef i;
  169. for (i=1;i<=jsVarsSize;i++) {
  170. if ((jsvGetAddressOf(i)->flags&JSV_VARTYPEMASK) != JSV_UNUSED) {
  171. jsiConsolePrintf("USED VAR #%d:",i);
  172. jsvTrace(i, 2);
  173. }
  174. }
  175. }
  176. bool jsvHasCharacterData(const JsVar *v) {
  177. return jsvIsString(v) || jsvIsStringExt(v);
  178. }
  179. bool jsvHasStringExt(const JsVar *v) {
  180. return jsvIsString(v) || jsvIsStringExt(v);
  181. }
  182. bool jsvHasChildren(const JsVar *v) {
  183. return jsvIsFunction(v) || jsvIsObject(v) || jsvIsArray(v) || jsvIsRoot(v);
  184. }
  185. /// Is this variable a type that uses firstChild to point to a single Variable (ie. it doesn't have multiple children)
  186. bool jsvHasSingleChild(const JsVar *v) {
  187. return jsvIsName(v) || jsvIsArrayBuffer(v) || jsvIsArrayBufferName(v);
  188. }
  189. JsVar *jsvNew() {
  190. if (jsVarFirstEmpty!=0) {
  191. JsVar *v = jsvLock(jsVarFirstEmpty);
  192. jsVarFirstEmpty = v->nextSibling; // move our reference to the next in the free list
  193. assert((v->flags&JSV_VARTYPEMASK) == JSV_UNUSED);
  194. // reset it
  195. v->refs = 0;
  196. //v->locks = 1;
  197. v->flags = JSV_LOCK_ONE;
  198. v->varData.callback = 0;
  199. v->firstChild = 0;
  200. v->lastChild = 0;
  201. v->prevSibling = 0;
  202. v->nextSibling = 0;
  203. // return pointer
  204. return v;
  205. }
  206. /* we don't have memort - second last hope - run garbage collector */
  207. if (jsvGarbageCollect())
  208. return jsvNew(); // if it freed something, continue
  209. /* we don't have memory - last hope - ask jsInteractive to try and free some it
  210. may have kicking around */
  211. if (jsiFreeMoreMemory())
  212. return jsvNew();
  213. /* We couldn't claim any more memory by Garbage collecting... */
  214. #ifdef RESIZABLE_JSVARS
  215. jsvSetMemoryTotal(jsVarsSize*2);
  216. return jsvNew();
  217. #else
  218. // On a micro, we're screwed.
  219. jsError("Out of Memory!");
  220. jspSetInterrupted(true);
  221. return 0;
  222. #endif
  223. }
  224. void jsvFreePtr(JsVar *var) {
  225. /* To be here, we're not supposed to be part of anything else. If
  226. * we were, we'd have been freed by jsvGarbageCollect */
  227. assert(jsvIsStringExt(var) || (!var->nextSibling && !var->prevSibling));
  228. // Names that Link to other things
  229. if (jsvHasSingleChild(var)) {
  230. if (var->firstChild) {
  231. JsVar *child = jsvLock(var->firstChild);
  232. jsvUnRef(child); var->firstChild = 0; // unlink the child
  233. jsvUnLock(child); // unlock should trigger a free
  234. }
  235. }
  236. /* No else, because a String Name may have a single child, but
  237. * also StringExts */
  238. /* Now, free children - see jsvar.h comments for how! */
  239. if (jsvHasStringExt(var)) {
  240. // TODO: make string free this non-recursive
  241. JsVarRef stringDataRef = var->lastChild;
  242. var->lastChild = 0;
  243. if (stringDataRef) {
  244. JsVar *child = jsvLock(stringDataRef);
  245. assert(jsvIsStringExt(child));
  246. jsvFreePtr(child);
  247. jsvUnLock(child);
  248. }
  249. } else if (jsvHasChildren(var)) {
  250. JsVarRef childref = var->firstChild;
  251. var->firstChild = 0;
  252. var->lastChild = 0;
  253. while (childref) {
  254. JsVar *child = jsvLock(childref);
  255. assert(jsvIsName(child));
  256. childref = child->nextSibling;
  257. child->prevSibling = 0;
  258. child->nextSibling = 0;
  259. jsvUnRef(child);
  260. jsvUnLock(child);
  261. }
  262. } else {
  263. assert(!var->firstChild);
  264. assert(!var->lastChild);
  265. }
  266. // free!
  267. var->flags = (var->flags & ~JSV_VARTYPEMASK) | JSV_UNUSED;
  268. // add this to our free list
  269. var->nextSibling = jsVarFirstEmpty;
  270. jsVarFirstEmpty = jsvGetRef(var);
  271. }
  272. /// Get a reference from a var - SAFE for null vars
  273. JsVarRef jsvGetRef(JsVar *var) {
  274. if (!var) return 0;
  275. #ifdef LARGE_MEM
  276. return var->this;
  277. #else
  278. #ifdef RESIZABLE_JSVARS
  279. unsigned int i, c = jsVarsSize>>JSVAR_BLOCK_SHIFT;
  280. for (i=0;i<c;i++) {
  281. if (var>=jsVarBlocks[i] && var<&jsVarBlocks[i][JSVAR_BLOCK_SIZE]) {
  282. JsVarRef r = (JsVarRef)(1 + (i<<JSVAR_BLOCK_SHIFT) + (var - jsVarBlocks[i]));
  283. return r;
  284. }
  285. }
  286. return 0;
  287. #else
  288. return (JsVarRef)(1 + (var - jsVars));
  289. #endif
  290. #endif
  291. }
  292. /// Lock this reference and return a pointer - UNSAFE for null refs
  293. JsVar *jsvLock(JsVarRef ref) {
  294. JsVar *var = jsvGetAddressOf(ref);
  295. //var->locks++;
  296. assert(jsvGetLocks(var) < JSV_LOCK_MAX);
  297. var->flags += JSV_LOCK_ONE;
  298. #ifdef DEBUG
  299. if (jsvGetLocks(var)==0) {
  300. jsError("Too many locks to Variable!");
  301. //jsPrint("Var #");jsPrintInt(ref);jsPrint("\n");
  302. }
  303. #endif
  304. return var;
  305. }
  306. /// Lock this pointer and return a pointer - UNSAFE for null pointer
  307. JsVar *jsvLockAgain(JsVar *var) {
  308. assert(var);
  309. assert(jsvGetLocks(var) < JSV_LOCK_MAX);
  310. var->flags += JSV_LOCK_ONE;
  311. #ifdef DEBUG
  312. if (var->locks==0) {
  313. jsError("Too many locks to Variable!");
  314. //jsPrint("Var #");jsPrintInt(ref);jsPrint("\n");
  315. }
  316. #endif
  317. return var;
  318. }
  319. /// Unlock this variable - this is SAFE for null variables
  320. void jsvUnLock(JsVar *var) {
  321. if (!var) return;
  322. assert(jsvGetLocks(var)>0);
  323. var->flags -= JSV_LOCK_ONE;
  324. /* if we know we're free, then we can just free
  325. * this variable right now. Loops of variables
  326. * are handled by the Garbage Collector.
  327. * Note: we check var->refs first as it is fastest and most likely to be false */
  328. if (var->refs == 0 && jsvHasRef(var) && jsvGetLocks(var) == 0 && (var->flags&JSV_VARTYPEMASK)!=JSV_UNUSED) {
  329. jsvFreePtr(var);
  330. }
  331. }
  332. /// Reference - set this variable as used by something
  333. JsVar *jsvRef(JsVar *v) {
  334. assert(v && jsvHasRef(v));
  335. v->refs++;
  336. return v;
  337. }
  338. /// Unreference - set this variable as not used by anything
  339. void jsvUnRef(JsVar *var) {
  340. assert(var && var->refs>0 && jsvHasRef(var));
  341. var->refs--;
  342. // locks are never 0 here, so why bother checking!
  343. assert(jsvGetLocks(var)>0);
  344. }
  345. /// Helper fn, Reference - set this variable as used by something
  346. JsVarRef jsvRefRef(JsVarRef ref) {
  347. JsVar *v;
  348. assert(ref);
  349. v = jsvLock(ref);
  350. assert(!jsvIsStringExt(v));
  351. jsvRef(v);
  352. jsvUnLock(v);
  353. return ref;
  354. }
  355. /// Helper fn, Unreference - set this variable as not used by anything
  356. JsVarRef jsvUnRefRef(JsVarRef ref) {
  357. JsVar *v;
  358. assert(ref);
  359. v = jsvLock(ref);
  360. assert(!jsvIsStringExt(v));
  361. jsvUnRef(v);
  362. jsvUnLock(v);
  363. return 0;
  364. }
  365. JsVar *jsvNewFromString(const char *str) {
  366. // Create a var
  367. JsVar *first = jsvNewWithFlags(JSV_STRING);
  368. if (!first) {
  369. jsWarn("Unable to create string as not enough memory");
  370. return 0;
  371. }
  372. // Now we copy the string, but keep creating new jsVars if we go
  373. // over the end
  374. JsVar *var = jsvLockAgain(first);
  375. while (*str) {
  376. // copy data in
  377. size_t i, l = jsvGetMaxCharactersInVar(var);
  378. for (i=0;i<l && *str;i++)
  379. var->varData.str[i] = *(str++);
  380. // might as well shove a zero terminator on it if we can
  381. if (i<l) var->varData.str[i]=0;
  382. // we've stopped if the string was empty
  383. jsvSetCharactersInVar(var, i);
  384. // if there is still some left, it's because we filled up our var...
  385. // make a new one, link it in, and unlock the old one.
  386. if (*str) {
  387. JsVar *next = jsvNewWithFlags(JSV_STRING_EXT);
  388. if (!next) {
  389. jsWarn("Truncating string as not enough memory");
  390. jsvUnLock(var);
  391. return first;
  392. }
  393. // we don't ref, because StringExts are never reffed as they only have one owner (and ALWAYS have an owner)
  394. var->lastChild = jsvGetRef(next);
  395. jsvUnLock(var);
  396. var = next;
  397. }
  398. }
  399. jsvUnLock(var);
  400. // return
  401. return first;
  402. }
  403. JsVar *jsvNewStringOfLength(unsigned int byteLength) {
  404. // Create a var
  405. JsVar *first = jsvNewWithFlags(JSV_STRING);
  406. if (!first) {
  407. jsWarn("Unable to create string as not enough memory");
  408. return 0;
  409. }
  410. // Now zero the string, but keep creating new jsVars if we go
  411. // over the end
  412. JsVar *var = jsvLockAgain(first);
  413. while (byteLength>0) {
  414. // copy data in
  415. size_t i, l = jsvGetMaxCharactersInVar(var);
  416. for (i=0;i<l && byteLength>0;i++,byteLength--)
  417. var->varData.str[i] = 0;
  418. // might as well shove a zero terminator on it if we can
  419. if (i<l) var->varData.str[i]=0;
  420. // we've stopped if the string was empty
  421. jsvSetCharactersInVar(var, i);
  422. // if there is still some left, it's because we filled up our var...
  423. // make a new one, link it in, and unlock the old one.
  424. if (byteLength>0) {
  425. JsVar *next = jsvNewWithFlags(JSV_STRING_EXT);
  426. if (!next) {
  427. jsWarn("Truncating string as not enough memory");
  428. jsvUnLock(var);
  429. return first;
  430. }
  431. // we don't ref, because StringExts are never reffed as they only have one owner (and ALWAYS have an owner)
  432. var->lastChild = jsvGetRef(next);
  433. jsvUnLock(var);
  434. var = next;
  435. }
  436. }
  437. jsvUnLock(var);
  438. // return
  439. return first;
  440. }
  441. JsVar *jsvNewFromLexer(struct JsLex *lex, JslCharPos charFrom, JslCharPos charTo) {
  442. // Create a var
  443. JsVar *var = jsvNewFromEmptyString();
  444. if (!var) { // out of memory
  445. return 0;
  446. }
  447. jsvAppendStringVar(var, lex->sourceVar, charFrom, (JslCharPos)charTo-charFrom);
  448. return var;
  449. }
  450. JsVar *jsvNewWithFlags(JsVarFlags flags) {
  451. JsVar *var = jsvNew();
  452. if (!var) return 0; // no memory
  453. var->flags = (var->flags&(JsVarFlags)(~JSV_VARTYPEMASK)) | (flags&(JsVarFlags)(~JSV_LOCK_MASK));
  454. return var;
  455. }
  456. JsVar *jsvNewFromInteger(JsVarInt value) {
  457. JsVar *var = jsvNewWithFlags(JSV_INTEGER);
  458. if (!var) return 0; // no memory
  459. var->varData.integer = value;
  460. return var;
  461. }
  462. JsVar *jsvNewFromBool(bool value) {
  463. JsVar *var = jsvNewWithFlags(JSV_BOOLEAN);
  464. if (!var) return 0; // no memory
  465. var->varData.integer = value ? 1 : 0;
  466. return var;
  467. }
  468. JsVar *jsvNewFromFloat(JsVarFloat value) {
  469. JsVar *var = jsvNewWithFlags(JSV_FLOAT);
  470. if (!var) return 0; // no memory
  471. var->varData.floating = value;
  472. return var;
  473. }
  474. JsVar *jsvMakeIntoVariableName(JsVar *var, JsVar *valueOrZero) {
  475. if (!var) return 0;
  476. assert(var->refs==0); // make sure it's unused
  477. var->flags |= JSV_NAME;
  478. if (valueOrZero)
  479. var->firstChild = jsvGetRef(jsvRef(valueOrZero));
  480. return var;
  481. }
  482. JsVar *jsvNewFromPin(int pin) {
  483. JsVar *v = jsvNewFromInteger((JsVarInt)pin);
  484. if (v) {
  485. v->flags = (JsVarFlags)((v->flags & ~JSV_VARTYPEMASK) | JSV_PIN);
  486. }
  487. return v;
  488. }
  489. bool jsvIsBasicVarEqual(JsVar *a, JsVar *b) {
  490. // quick checks
  491. if (a==b) return true;
  492. if (!a || !b) return false; // one of them is undefined
  493. // OPT: would this be useful as compare instead?
  494. assert(jsvIsBasic(a) && jsvIsBasic(b));
  495. if (jsvIsNumeric(a) && jsvIsNumeric(b)) {
  496. if (jsvIsInt(a)) {
  497. if (jsvIsInt(b)) {
  498. return a->varData.integer == b->varData.integer;
  499. } else {
  500. assert(jsvIsFloat(b));
  501. return a->varData.integer == b->varData.floating;
  502. }
  503. } else {
  504. assert(jsvIsFloat(a));
  505. if (jsvIsInt(b)) {
  506. return a->varData.floating == b->varData.integer;
  507. } else {
  508. assert(jsvIsFloat(b));
  509. return a->varData.floating == b->varData.floating;
  510. }
  511. }
  512. } else if (jsvIsString(a) && jsvIsString(b)) {
  513. JsvStringIterator ita, itb;
  514. jsvStringIteratorNew(&ita, a, 0);
  515. jsvStringIteratorNew(&itb, b, 0);
  516. while (true) {
  517. char a = jsvStringIteratorGetChar(&ita);
  518. char b = jsvStringIteratorGetChar(&itb);
  519. if (a != b) {
  520. jsvStringIteratorFree(&ita);
  521. jsvStringIteratorFree(&itb);
  522. return false;
  523. }
  524. if (!a) { // equal, but end of string
  525. jsvStringIteratorFree(&ita);
  526. jsvStringIteratorFree(&itb);
  527. return true;
  528. }
  529. jsvStringIteratorNext(&ita);
  530. jsvStringIteratorNext(&itb);
  531. }
  532. // we never get here
  533. return false; // make compiler happy
  534. } else {
  535. //TODO: are there any other combinations we should check here?? String v int?
  536. return false;
  537. }
  538. }
  539. bool jsvIsEqual(JsVar *a, JsVar *b) {
  540. if (jsvIsBasic(a) && jsvIsBasic(b))
  541. return jsvIsBasicVarEqual(a,b);
  542. return jsvGetRef(a)==jsvGetRef(b);
  543. }
  544. /// Get a const string representing this variable - if we can. Otherwise return 0
  545. const char *jsvGetConstString(const JsVar *v) {
  546. if (jsvIsUndefined(v)) {
  547. return "undefined";
  548. } else if (jsvIsNull(v)) {
  549. return "null";
  550. } else if (jsvIsBoolean(v)) {
  551. return jsvGetBool(v) ? "true" : "false";
  552. } else if (jsvIsRoot(v)) {
  553. return "[object Hardware]";
  554. } else if (jsvIsObject(v)) {
  555. return "[object Object]";
  556. }
  557. return 0;
  558. }
  559. /// Return the 'type' of the JS variable (eg. JS's typeof operator)
  560. const char *jsvGetTypeOf(const JsVar *v) {
  561. if (jsvIsNull(v)) return "object";
  562. if (jsvIsUndefined(v)) return "undefined";
  563. if (jsvIsFunction(v)) return "function";
  564. if (jsvIsObject(v) || jsvIsArray(v)) return "object";
  565. if (jsvIsString(v)) return "string";
  566. if (jsvIsBoolean(v)) return "boolean";
  567. if (jsvIsNumeric(v)) return "number";
  568. return "?";
  569. }
  570. /// Save this var as a string to the given buffer, and return how long it was (return val doesn't include terminating 0)
  571. size_t jsvGetString(const JsVar *v, char *str, size_t len) {
  572. const char *s = jsvGetConstString(v);
  573. if (s) {
  574. strncpy(str, s, len);
  575. return strlen(s);
  576. } else if (jsvIsInt(v)) {
  577. itoa(v->varData.integer, str, 10);
  578. return strlen(str);
  579. } else if (jsvIsFloat(v)) {
  580. ftoa(v->varData.floating, str);
  581. return strlen(str);
  582. } else if (jsvHasCharacterData(v)) {
  583. if (jsvIsStringExt(v))
  584. jsErrorInternal("Calling jsvGetString on a JSV_STRING_EXT");
  585. size_t l = len;
  586. JsvStringIterator it;
  587. jsvStringIteratorNewConst(&it, v, 0);
  588. while (jsvStringIteratorHasChar(&it)) {
  589. if (l--<=1) {
  590. *str = 0;
  591. jsWarn("jsvGetString overflowed\n");
  592. jsvStringIteratorFree(&it);
  593. return len;
  594. }
  595. *(str++) = jsvStringIteratorGetChar(&it);
  596. jsvStringIteratorNext(&it);
  597. }
  598. jsvStringIteratorFree(&it);
  599. *str = 0;
  600. return len-l;
  601. } else {
  602. // Try and get as a JsVar string, and try again
  603. JsVar *stringVar = jsvAsString((JsVar*)v, false); // we know we're casting to non-const here
  604. if (stringVar) {
  605. size_t l = jsvGetString(stringVar, str, len); // call again - but this time with converted var
  606. jsvUnLock(stringVar);
  607. return l;
  608. } else {
  609. strncpy(str, "", len);
  610. jsErrorInternal("Variable type cannot be converted to string");
  611. return 0;
  612. }
  613. }
  614. }
  615. /// Set the Data in this string. This must JUST overwrite - not extend or shrink
  616. void jsvSetString(JsVar *v, char *str, size_t len) {
  617. assert(jsvHasCharacterData(v));
  618. assert(len == jsvGetStringLength(v));
  619. JsvStringIterator it;
  620. jsvStringIteratorNew(&it, v, 0);
  621. size_t i;
  622. for (i=0;i<len;i++) {
  623. jsvStringIteratorSetChar(&it, str[i]);
  624. jsvStringIteratorNext(&it);
  625. }
  626. jsvStringIteratorFree(&it);
  627. }
  628. /** If var is a string, lock and return it, else
  629. * create a new string. unlockVar means this will auto-unlock 'var' */
  630. JsVar *jsvAsString(JsVar *v, bool unlockVar) {
  631. JsVar *str = 0;
  632. // If it is string-ish, but not quite a string, copy it
  633. if (jsvHasCharacterData(v) && jsvIsName(v)) {
  634. str = jsvNewFromEmptyString();
  635. if (str) jsvAppendStringVarComplete(str,v);
  636. } else if (jsvIsString(v)) { // If it is a string - just return a reference
  637. str = jsvLockAgain(v);
  638. } else {
  639. const char *constChar = jsvGetConstString(v);
  640. if (constChar) {
  641. // if we could get this as a simple const char, do that..
  642. str = jsvNewFromString(constChar);
  643. } else if (jsvIsPin(v)) {
  644. char buf[8];
  645. jshGetPinString(buf, (Pin)v->varData.integer);
  646. str = jsvNewFromString(buf);
  647. } else if (jsvIsInt(v)) {
  648. char buf[JS_NUMBER_BUFFER_SIZE];
  649. itoa(v->varData.integer, buf, 10);
  650. str = jsvNewFromString(buf);
  651. } else if (jsvIsFloat(v)) {
  652. char buf[JS_NUMBER_BUFFER_SIZE];
  653. ftoa(v->varData.floating, buf);
  654. str = jsvNewFromString(buf);
  655. } else if (jsvIsArray(v) || jsvIsArrayBuffer(v)) {
  656. JsVar *filler = jsvNewFromString(",");
  657. str = jsvArrayJoin(v, filler);
  658. jsvUnLock(filler);
  659. } else if (jsvIsFunction(v)) {
  660. str = jsvNewFromEmptyString();
  661. if (str) jsfGetJSON(v, str);
  662. } else {
  663. jsErrorInternal("Variable type cannot be converted to string");
  664. str = 0;
  665. }
  666. }
  667. if (unlockVar) jsvUnLock(v);
  668. return str;
  669. }
  670. size_t jsvGetStringLength(JsVar *v) {
  671. size_t strLength = 0;
  672. JsVar *var = v;
  673. JsVarRef ref = 0;
  674. if (!jsvHasCharacterData(v)) return 0;
  675. while (var) {
  676. JsVarRef refNext = var->lastChild;
  677. strLength += jsvGetCharactersInVar(var);
  678. // Go to next
  679. if (ref) jsvUnLock(var); // note use of if (ref), not var
  680. ref = refNext;
  681. var = ref ? jsvLock(ref) : 0;
  682. }
  683. if (ref) jsvUnLock(var); // note use of if (ref), not var
  684. return strLength;
  685. }
  686. // IN A STRING get the number of lines in the string (min=1)
  687. int jsvGetLinesInString(JsVar *v) {
  688. int lines = 1;
  689. JsvStringIterator it;
  690. jsvStringIteratorNew(&it, v, 0);
  691. while (jsvStringIteratorHasChar(&it)) {
  692. if (jsvStringIteratorGetChar(&it)=='\n') lines++;
  693. jsvStringIteratorNext(&it);
  694. }
  695. jsvStringIteratorFree(&it);
  696. return lines;
  697. }
  698. // IN A STRING Get the number of characters on a line - lines start at 1
  699. int jsvGetCharsOnLine(JsVar *v, int line) {
  700. int currentLine = 1;
  701. int chars = 0;
  702. JsvStringIterator it;
  703. jsvStringIteratorNew(&it, v, 0);
  704. while (jsvStringIteratorHasChar(&it)) {
  705. if (jsvStringIteratorGetChar(&it)=='\n') {
  706. currentLine++;
  707. if (currentLine > line) break;
  708. } else if (currentLine==line) chars++;
  709. jsvStringIteratorNext(&it);
  710. }
  711. jsvStringIteratorFree(&it);
  712. return chars;
  713. }
  714. // IN A STRING, get the line and column of the given character. Both values must be non-null
  715. void jsvGetLineAndCol(JsVar *v, int charIdx, int* line, int *col) {
  716. int x = 1;
  717. int y = 1;
  718. int n = 0;
  719. assert(line && col);
  720. JsvStringIterator it;
  721. jsvStringIteratorNew(&it, v, 0);
  722. while (jsvStringIteratorHasChar(&it)) {
  723. char ch = jsvStringIteratorGetChar(&it);
  724. if (n==charIdx) {
  725. jsvStringIteratorFree(&it);
  726. *line = y;
  727. *col = x;
  728. return;
  729. }
  730. x++;
  731. if (ch=='\n') {
  732. x=1; y++;
  733. }
  734. n++;
  735. jsvStringIteratorNext(&it);
  736. }
  737. jsvStringIteratorFree(&it);
  738. // uh-oh - not found
  739. *line = y;
  740. *col = x;
  741. }
  742. // IN A STRING, get a character index from a line and column
  743. int jsvGetIndexFromLineAndCol(JsVar *v, int line, int col) {
  744. int x = 1;
  745. int y = 1;
  746. int n = 0;
  747. JsvStringIterator it;
  748. jsvStringIteratorNew(&it, v, 0);
  749. while (jsvStringIteratorHasChar(&it)) {
  750. char ch = jsvStringIteratorGetChar(&it);
  751. if ((y==line && x>=col) || y>line) {
  752. jsvStringIteratorFree(&it);
  753. return (y>line) ? (n-1) : n;
  754. }
  755. x++;
  756. if (ch=='\n') {
  757. x=1; y++;
  758. }
  759. n++;
  760. jsvStringIteratorNext(&it);
  761. }
  762. jsvStringIteratorFree(&it);
  763. return n;
  764. }
  765. void jsvAppendString(JsVar *var, const char *str) {
  766. assert(jsvIsString(var));
  767. JsVar *block = jsvLockAgain(var);
  768. // Find the block at end of the string...
  769. while (block->lastChild) {
  770. JsVarRef next = block->lastChild;
  771. jsvUnLock(block);
  772. block = jsvLock(next);
  773. }
  774. // find how full the block is
  775. size_t blockChars = jsvGetCharactersInVar(block);
  776. // now start appending
  777. while (*str) {
  778. // copy data in
  779. size_t i, l=jsvGetMaxCharactersInVar(block);
  780. for (i=blockChars;i<l && *str;i++) {
  781. block->varData.str[i] = *(str++);
  782. }
  783. jsvSetCharactersInVar(block, i);
  784. // if there is still some left, it's because we filled up our var...
  785. // make a new one, link it in, and unlock the old one.
  786. if (*str) {
  787. JsVar *next = jsvNewWithFlags(JSV_STRING_EXT);
  788. if (!next) break;
  789. // we don't ref, because StringExts are never reffed as they only have one owner (and ALWAYS have an owner)
  790. block->lastChild = jsvGetRef(next);
  791. jsvUnLock(block);
  792. block = next;
  793. blockChars=0; // it's new, so empty
  794. }
  795. }
  796. jsvUnLock(block);
  797. }
  798. void jsvAppendStringBuf(JsVar *var, const char *str, int length) {
  799. assert(jsvIsString(var));
  800. JsVar *block = jsvLockAgain(var);
  801. // Find the block at end of the string...
  802. while (block->lastChild) {
  803. JsVarRef next = block->lastChild;
  804. jsvUnLock(block);
  805. block = jsvLock(next);
  806. }
  807. // find how full the block is
  808. size_t blockChars = jsvGetCharactersInVar(block);
  809. // now start appending
  810. while (length) {
  811. // copy data in
  812. size_t i, l=jsvGetMaxCharactersInVar(block);
  813. for (i=blockChars;i<l && length;i++) {
  814. block->varData.str[i] = *(str++);
  815. length--;
  816. }
  817. jsvSetCharactersInVar(block, i);
  818. // if there is still some left, it's because we filled up our var...
  819. // make a new one, link it in, and unlock the old one.
  820. if (length) {
  821. JsVar *next = jsvNewWithFlags(JSV_STRING_EXT);
  822. if (!next) break;
  823. // we don't ref, because StringExts are never reffed as they only have one owner (and ALWAYS have an owner)
  824. block->lastChild = jsvGetRef(next);
  825. jsvUnLock(block);
  826. block = next;
  827. blockChars=0; // it's new, so empty
  828. }
  829. }
  830. jsvUnLock(block);
  831. }
  832. static void _jsvAppendPrintf(const char *str, void *user_data) {
  833. while (*str)
  834. jsvStringIteratorAppend((JsvStringIterator *)user_data, *(str++));
  835. }
  836. void jsvAppendPrintf(JsVar *var, const char *fmt, ...) {
  837. JsvStringIterator it;
  838. jsvStringIteratorNew(&it, var, 0);
  839. jsvStringIteratorGotoEnd(&it);
  840. va_list argp;
  841. va_start(argp, fmt);
  842. vcbprintf((vcbprintf_callback)&_jsvAppendPrintf,&it, fmt, argp);
  843. va_end(argp);
  844. jsvStringIteratorFree(&it);
  845. }
  846. /** Append str to var. Both must be strings. stridx = start char or str, maxLength = max number of characters (can be JSVAPPENDSTRINGVAR_MAXLENGTH).
  847. * stridx can be negative to go from end of string */
  848. void jsvAppendStringVar(JsVar *var, const JsVar *str, int stridx, int maxLength) {
  849. JsVar *block = jsvLockAgain(var);
  850. assert(jsvIsString(var));
  851. // Find the block at end of the string...
  852. while (block->lastChild) {
  853. JsVarRef next = block->lastChild;
  854. jsvUnLock(block);
  855. block = jsvLock(next);
  856. }
  857. // find how full the block is
  858. size_t blockChars = jsvGetCharactersInVar(block);
  859. // now start appending
  860. JsvStringIterator it;
  861. jsvStringIteratorNewConst(&it, str, stridx);
  862. while (jsvStringIteratorHasChar(&it) && (maxLength-->0)) {
  863. char ch = jsvStringIteratorGetChar(&it);
  864. if (blockChars >= jsvGetMaxCharactersInVar(block)) {
  865. jsvSetCharactersInVar(block, blockChars);
  866. JsVar *next = jsvNewWithFlags(JSV_STRING_EXT);
  867. if (!next) break; // out of memory
  868. // we don't ref, because StringExts are never reffed as they only have one owner (and ALWAYS have an owner)
  869. block->lastChild = jsvGetRef(next);
  870. jsvUnLock(block);
  871. block = next;
  872. blockChars=0; // it's new, so empty
  873. }
  874. block->varData.str[blockChars++] = ch;
  875. jsvStringIteratorNext(&it);
  876. }
  877. jsvStringIteratorFree(&it);
  878. jsvSetCharactersInVar(block, blockChars);
  879. jsvUnLock(block);
  880. }
  881. /** Append all of str to var. Both must be strings. */
  882. void jsvAppendStringVarComplete(JsVar *var, const JsVar *str) {
  883. jsvAppendStringVar(var, str, 0, JSVAPPENDSTRINGVAR_MAXLENGTH);
  884. }
  885. char jsvGetCharInString(JsVar *v, int idx) {
  886. if (!jsvIsString(v)) return 0;
  887. if (idx<0) idx += (int)jsvGetStringLength(v); // <0 goes from end of string
  888. if (idx<0) return 0;
  889. JsvStringIterator it;
  890. jsvStringIteratorNew(&it, v, idx);
  891. char ch = jsvStringIteratorGetChar(&it);
  892. jsvStringIteratorFree(&it);
  893. return ch;
  894. }
  895. /** Does this string contain only Numeric characters? */
  896. bool jsvIsStringNumeric(const JsVar *var) {
  897. assert(jsvIsString(var));
  898. JsvStringIterator it;
  899. jsvStringIteratorNewConst(&it, var, 0); // we know it's non const
  900. int chars = 0;
  901. while (jsvStringIteratorHasChar(&it)) {
  902. chars++;
  903. char ch = jsvStringIteratorGetChar(&it);
  904. if (!isNumeric(ch)) { // FIXME: should check for non-integer values (floating point?)
  905. jsvStringIteratorFree(&it);
  906. return false;
  907. }
  908. jsvStringIteratorNext(&it);
  909. }
  910. jsvStringIteratorFree(&it);
  911. return chars>0;
  912. }
  913. /** Does this string contain only Numeric characters? This is for arrays
  914. * and makes the assertion that int_to_string(string_to_int(var))==var */
  915. bool jsvIsStringNumericStrict(const JsVar *var) {
  916. assert(jsvIsString(var));
  917. JsvStringIterator it;
  918. jsvStringIteratorNewConst(&it, var, 0); // we know it's non const
  919. bool hadNonZero = false;
  920. bool hasLeadingZero = false;
  921. int chars = 0;
  922. while (jsvStringIteratorHasChar(&it)) {
  923. chars++;
  924. char ch = jsvStringIteratorGetChar(&it);
  925. if (!isNumeric(ch)) {
  926. // test for leading zero ensures int_to_string(string_to_int(var))==var
  927. jsvStringIteratorFree(&it);
  928. return false;
  929. }
  930. if (!hadNonZero && ch=='0') hasLeadingZero=true;
  931. if (ch!='0') hadNonZero=true;
  932. jsvStringIteratorNext(&it);
  933. }
  934. jsvStringIteratorFree(&it);
  935. return chars>0 && (!hasLeadingZero || chars==1);
  936. }
  937. JsVarInt jsvGetInteger(const JsVar *v) {
  938. if (!v) return 0; // undefined
  939. /* strtol understands about hex and octal */
  940. if (jsvIsInt(v) || jsvIsBoolean(v) || jsvIsPin(v) || jsvIsArrayBufferName(v)) return v->varData.integer;
  941. if (jsvIsNull(v)) return 0;
  942. if (jsvIsUndefined(v)) return 0;
  943. if (jsvIsFloat(v)) return (JsVarInt)v->varData.floating;
  944. if (jsvIsString(v) && jsvIsStringNumeric(v)) {
  945. char buf[32];
  946. jsvGetString(v, buf, sizeof(buf));
  947. return stringToInt(buf);
  948. }
  949. return 0;
  950. }
  951. void jsvSetInteger(JsVar *v, JsVarInt value) {
  952. assert(jsvIsInt(v));
  953. v->varData.integer = value;
  954. }
  955. bool jsvGetBool(const JsVar *v) {
  956. return jsvGetInteger(v)!=0;
  957. }
  958. JsVarFloat jsvGetFloat(const JsVar *v) {
  959. if (!v) return NAN; // undefined
  960. if (jsvIsFloat(v)) return v->varData.floating;
  961. if (jsvIsInt(v)) return (JsVarFloat)v->varData.integer;
  962. if (jsvIsNull(v)) return 0;
  963. if (jsvIsString(v) && jsvIsStringNumeric(v)) {
  964. char buf[32];
  965. jsvGetString(v, buf, sizeof(buf));
  966. return stringToFloat(buf);
  967. }
  968. return NAN;
  969. }
  970. /// Convert the given variable to a number
  971. JsVar *jsvAsNumber(JsVar *var) {
  972. if (jsvIsInt(var) || jsvIsFloat(var)) return jsvLockAgain(var);
  973. if (jsvIsBoolean(var) || jsvIsPin(var)) return jsvNewFromInteger(var->varData.integer);
  974. return jsvNewFromFloat(jsvGetFloat(var));
  975. }
  976. #ifdef SAVE_ON_FLASH
  977. JsVarInt jsvGetIntegerAndUnLock(JsVar *v) { return _jsvGetIntegerAndUnLock(v); }
  978. JsVarFloat jsvGetFloatAndUnLock(JsVar *v) { return _jsvGetFloatAndUnLock(v); }
  979. bool jsvGetBoolAndUnLock(JsVar *v) { return _jsvGetBoolAndUnLock(v); }
  980. #endif
  981. /** Get the item at the given location in the array buffer and return the result */
  982. size_t jsvGetArrayBufferLength(JsVar *arrayBuffer) {
  983. assert(jsvIsArrayBuffer(arrayBuffer));
  984. return arrayBuffer->varData.arraybuffer.length;
  985. }
  986. /** Get the item at the given location in the array buffer and return the result */
  987. JsVar *jsvArrayBufferGet(JsVar *arrayBuffer, JsVarInt idx) {
  988. JsvArrayBufferIterator it;
  989. jsvArrayBufferIteratorNew(&it, arrayBuffer, idx);
  990. JsVar *v = jsvArrayBufferIteratorGetValue(&it);
  991. jsvArrayBufferIteratorFree(&it);
  992. return v;
  993. }
  994. /** Set the item at the given location in the array buffer */
  995. void jsvArrayBufferSet(JsVar *arrayBuffer, JsVarInt idx, JsVar *value) {
  996. JsvArrayBufferIterator it;
  997. jsvArrayBufferIteratorNew(&it, arrayBuffer, idx);
  998. jsvArrayBufferIteratorSetValue(&it, value);
  999. jsvArrayBufferIteratorFree(&it);
  1000. }
  1001. /** Given an integer name that points to an arraybuffer or an arraybufferview, evaluate it and return the result */
  1002. JsVar *jsvArrayBufferGetFromName(JsVar *name) {
  1003. assert(jsvIsArrayBufferName(name));
  1004. JsVarInt idx = jsvGetInteger(name);
  1005. JsVar *arrayBuffer = jsvLock(name->firstChild);
  1006. JsVar *value = jsvArrayBufferGet(arrayBuffer, idx);
  1007. jsvUnLock(arrayBuffer);
  1008. return value;
  1009. }
  1010. /** If a is a name skip it and go to what it points to - and so on.
  1011. * ALWAYS locks - so must unlock what it returns. It MAY
  1012. * return 0. */
  1013. JsVar *jsvSkipName(JsVar *a) {
  1014. JsVar *pa = a;
  1015. if (!a) return 0;
  1016. if (jsvIsArrayBufferName(pa)) return jsvArrayBufferGetFromName(pa);
  1017. while (jsvIsName(pa)) {
  1018. JsVarRef n = pa->firstChild;
  1019. if (pa!=a) jsvUnLock(pa);
  1020. if (!n) return 0;
  1021. pa = jsvLock(n);
  1022. }
  1023. if (pa==a) jsvLockAgain(pa);
  1024. return pa;
  1025. }
  1026. /** If a is a name skip it and go to what it points to.
  1027. * ALWAYS locks - so must unlock what it returns. It MAY
  1028. * return 0. */
  1029. JsVar *jsvSkipOneName(JsVar *a) {
  1030. JsVar *pa = a;
  1031. if (!a) return 0;
  1032. if (jsvIsArrayBufferName(pa)) return jsvArrayBufferGetFromName(pa);
  1033. if (jsvIsName(pa)) {
  1034. JsVarRef n = pa->firstChild;
  1035. if (pa!=a) jsvUnLock(pa);
  1036. if (!n) return 0;
  1037. pa = jsvLock(n);
  1038. }
  1039. if (pa==a) jsvLockAgain(pa);
  1040. return pa;
  1041. }
  1042. /** If a is a's child is a name skip it and go to what it points to.
  1043. * ALWAYS locks - so must unlock what it returns. */
  1044. JsVar *jsvSkipToLastName(JsVar *a) {
  1045. assert(jsvIsName(a));
  1046. a = jsvLockAgain(a);
  1047. while (true) {
  1048. if (!a->firstChild) return a;
  1049. JsVar *child = jsvLock(a->firstChild);
  1050. if (jsvIsName(child)) {
  1051. jsvUnLock(a);
  1052. a = child;
  1053. } else {
  1054. jsvUnLock(child);
  1055. return a;
  1056. }
  1057. }
  1058. return 0; // not called
  1059. }
  1060. // Also see jsvIsBasicVarEqual
  1061. bool jsvIsStringEqual(JsVar *var, const char *str) {
  1062. if (!jsvHasCharacterData(var)) {
  1063. assert(jsvIsBasic(var));
  1064. return 0; // not a string so not equal!
  1065. }
  1066. JsvStringIterator it;
  1067. jsvStringIteratorNew(&it, var, 0);
  1068. while (jsvStringIteratorHasChar(&it) && *str) {
  1069. if (jsvStringIteratorGetChar(&it) != *str) {
  1070. jsvStringIteratorFree(&it);
  1071. return false;
  1072. }
  1073. str++;
  1074. jsvStringIteratorNext(&it);
  1075. }
  1076. bool eq = jsvStringIteratorGetChar(&it)==*str; // should both be 0 if equal
  1077. jsvStringIteratorFree(&it);
  1078. return eq;
  1079. }
  1080. /** Compare 2 strings, starting from the given character positions. equalAtEndOfString means that
  1081. * if one of the strings ends, we treat them as equal.
  1082. * For a basic strcmp, do: jsvCompareString(a,b,0,0,false)
  1083. * */
  1084. int jsvCompareString(JsVar *va, JsVar *vb, int starta, int startb, bool equalAtEndOfString) {
  1085. JsvStringIterator ita, itb;
  1086. jsvStringIteratorNew(&ita, va, starta);
  1087. jsvStringIteratorNew(&itb, vb, startb);
  1088. // step to first positions
  1089. while (true) {
  1090. int ca = jsvStringIteratorGetCharOrMinusOne(&ita);
  1091. int cb = jsvStringIteratorGetCharOrMinusOne(&itb);
  1092. if (ca != cb) {
  1093. jsvStringIteratorFree(&ita);
  1094. jsvStringIteratorFree(&itb);
  1095. if ((ca<0 || cb<0) && equalAtEndOfString) return 0;
  1096. return ca - cb;
  1097. }
  1098. if (ca < 0) { // both equal, but end of string
  1099. jsvStringIteratorFree(&ita);
  1100. jsvStringIteratorFree(&itb);
  1101. return 0;
  1102. }
  1103. jsvStringIteratorNext(&ita);
  1104. jsvStringIteratorNext(&itb);
  1105. }
  1106. // never get here, but the compiler warns...
  1107. return true;
  1108. }
  1109. /** Compare 2 integers, >0 if va>vb, <0 if va<vb. If compared with a non-integer, that gets put later */
  1110. int jsvCompareInteger(JsVar *va, JsVar *vb) {
  1111. if (jsvIsInt(va) && jsvIsInt(vb))
  1112. return (int)(jsvGetInteger(va) - jsvGetInteger(vb));
  1113. else if (jsvIsInt(va))
  1114. return -1;
  1115. else if (jsvIsInt(vb))
  1116. return 1;
  1117. else
  1118. return 0;
  1119. }
  1120. /** Copy only a name, not what it points to. ALTHOUGH the link to what it points to is maintained unless linkChildren=false
  1121. If keepAsName==false, this will be converted into a normal variable */
  1122. JsVar *jsvCopyNameOnly(JsVar *src, bool linkChildren, bool keepAsName) {
  1123. assert(jsvIsName(src));
  1124. JsVarFlags flags = src->flags;
  1125. if (!keepAsName) flags &= (JsVarFlags)~JSV_NAME; // make sure this is NOT a name
  1126. JsVar *dst = jsvNewWithFlags(flags);
  1127. if (!dst) return 0; // out of memory
  1128. memcpy(&dst->varData, &src->varData, sizeof(JsVarData));
  1129. dst->lastChild = 0;
  1130. dst->firstChild = 0;
  1131. dst->prevSibling = 0;
  1132. dst->nextSibling = 0;
  1133. // Copy LINK of what it points to
  1134. if (linkChildren && src->firstChild) {
  1135. dst->firstChild = jsvRefRef(src->firstChild);
  1136. }
  1137. // Copy extra string data if there was any
  1138. if (jsvHasStringExt(src)) {
  1139. // copy extra bits of string if there were any
  1140. if (src->lastChild) {
  1141. JsVar *child = jsvLock(src->lastChild);
  1142. JsVar *childCopy = jsvCopy(child);
  1143. if (childCopy) { // could be out of memory
  1144. dst->lastChild = jsvGetRef(childCopy); // no ref for stringext
  1145. jsvUnLock(childCopy);
  1146. }
  1147. jsvUnLock(child);
  1148. }
  1149. } else {
  1150. assert(jsvIsBasic(src)); // in case we missed something!
  1151. }
  1152. return dst;
  1153. }
  1154. JsVar *jsvCopy(JsVar *src) {
  1155. JsVar *dst = jsvNewWithFlags(src->flags);
  1156. if (!dst) return 0; // out of memory
  1157. if (!jsvIsStringExt(src)) {
  1158. memcpy(&dst->varData, &src->varData, sizeof(JsVarData));
  1159. dst->lastChild = 0;
  1160. dst->firstChild = 0;
  1161. dst->prevSibling = 0;
  1162. dst->nextSibling = 0;
  1163. } else {
  1164. // stringexts use the extra pointers after varData to store characters
  1165. // see jsvGetMaxCharactersInVar
  1166. memcpy(&dst->varData, &src->varData, JSVAR_DATA_STRING_MAX_LEN);
  1167. dst->lastChild = 0;
  1168. }
  1169. // Copy what names point to
  1170. if (jsvIsName(src)) {
  1171. if (src->firstChild) {
  1172. JsVar *child = jsvLock(src->firstChild);
  1173. JsVar *childCopy = jsvRef(jsvCopy(child));
  1174. jsvUnLock(child);
  1175. if (childCopy) { // could have been out of memory
  1176. dst->firstChild = jsvGetRef(childCopy);
  1177. jsvUnLock(childCopy);
  1178. }
  1179. }
  1180. }
  1181. if (jsvHasStringExt(src)) {
  1182. // copy extra bits of string if there were any
  1183. if (src->lastChild) {
  1184. JsVar *child = jsvLock(src->lastChild);
  1185. JsVar *childCopy = jsvCopy(child);
  1186. if (childCopy) {// could be out of memory
  1187. dst->lastChild = jsvGetRef(childCopy); // no ref for stringext
  1188. jsvUnLock(childCopy);
  1189. }
  1190. jsvUnLock(child);
  1191. }
  1192. } else if (jsvHasChildren(src)) {
  1193. // Copy children..
  1194. JsVarRef vr;
  1195. vr = src->firstChild;
  1196. while (vr) {
  1197. JsVar *name = jsvLock(vr);
  1198. JsVar *child = jsvCopyNameOnly(name, true/*link children*/, true/*keep as name*/); // NO DEEP COPY!
  1199. if (child) { // could have been out of memory
  1200. jsvAddName(dst, child);
  1201. jsvUnLock(child);
  1202. }
  1203. vr = name->nextSibling;
  1204. jsvUnLock(name);
  1205. }
  1206. } else {
  1207. assert(jsvIsBasic(src)); // in case we missed something!
  1208. }
  1209. return dst;
  1210. }
  1211. void jsvAddName(JsVar *parent, JsVar *namedChild) {
  1212. namedChild = jsvRef(namedChild); // ref here VERY important as adding to structure!
  1213. assert(jsvIsName(namedChild));
  1214. if (parent->lastChild) { // we have children already
  1215. JsVar *insertAfter = jsvLock(parent->lastChild);
  1216. if (jsvIsArray(parent)) {
  1217. // we must insert in order - so step back until we get the right place
  1218. while (insertAfter && jsvCompareInteger(namedChild, insertAfter)<0) {
  1219. JsVarRef prev = insertAfter->prevSibling;
  1220. jsvUnLock(insertAfter);
  1221. insertAfter = prev ? jsvLock(prev) : 0;
  1222. }
  1223. }
  1224. if (insertAfter) {
  1225. if (insertAfter->nextSibling) {
  1226. // great, we're in the middle...
  1227. JsVar *insertBefore = jsvLock(insertAfter->nextSibling);
  1228. insertBefore->prevSibling = jsvGetRef(namedChild);
  1229. namedChild->nextSibling = jsvGetRef(insertBefore);
  1230. jsvUnLock(insertBefore);
  1231. } else {
  1232. // We're at the end - just set up the parent
  1233. parent->lastChild = jsvGetRef(namedChild);
  1234. }
  1235. insertAfter->nextSibling = jsvGetRef(namedChild);
  1236. namedChild->prevSibling = jsvGetRef(insertAfter);
  1237. jsvUnLock(insertAfter);
  1238. } else { // Insert right at the beginning of the array
  1239. // Link 2 children together
  1240. JsVar *firstChild = jsvLock(parent->firstChild);
  1241. firstChild->prevSibling = jsvGetRef(namedChild);
  1242. jsvUnLock(firstChild);
  1243. namedChild->nextSibling = parent->firstChild;
  1244. // finally set the new child as the first one
  1245. parent->firstChild = jsvGetRef(namedChild);
  1246. }
  1247. } else { // we have no children - just add it
  1248. parent->firstChild = parent->lastChild = jsvGetRef(namedChild);
  1249. }
  1250. }
  1251. JsVar *jsvAddNamedChild(JsVar *parent, JsVar *child, const char *name) {
  1252. JsVar *namedChild = jsvMakeIntoVariableName(jsvNewFromString(name), child);
  1253. if (!namedChild) return 0; // Out of memory
  1254. jsvAddName(parent, namedChild);
  1255. return namedChild;
  1256. }
  1257. JsVar *jsvSetNamedChild(JsVar *parent, JsVar *child, const char *name) {
  1258. JsVar *namedChild = jsvFindChildFromString(parent, name, true);
  1259. if (namedChild) // could be out of memory
  1260. return jsvSetValueOfName(namedChild, child);
  1261. return 0;
  1262. }
  1263. JsVar *jsvSetValueOfName(JsVar *name, JsVar *src) {
  1264. assert(name && jsvIsName(name));
  1265. assert(name!=src); // no infinite loops!
  1266. // all is fine, so replace the existing child...
  1267. /* Existing child may be null in the case of Z = 0 where
  1268. * we create 'Z' and pass it down to '=' to have the value
  1269. * filled in (or it may be undefined). */
  1270. if (name->firstChild) jsvUnRefRef(name->firstChild); // free existing
  1271. if (src) {
  1272. // we can link to a name if we want (so can remove the assert!)
  1273. name->firstChild = jsvGetRef(jsvRef(src));
  1274. } else
  1275. name->firstChild = 0;
  1276. return name;
  1277. }
  1278. JsVar *jsvFindChildFromString(JsVar *parent, const char *name, bool addIfNotFound) {
  1279. /* Pull out first 4 bytes, and ensure that everything
  1280. * is 0 padded so that we can do a nice speedy check. */
  1281. char fastCheck[4];
  1282. fastCheck[0] = name[0];
  1283. if (name[0]) {
  1284. fastCheck[1] = name[1];
  1285. if (name[1]) {
  1286. fastCheck[2] = name[2];
  1287. if (name[2]) {
  1288. fastCheck[3] = name[3];
  1289. } else {
  1290. fastCheck[3] = 0;
  1291. }
  1292. } else {
  1293. fastCheck[2] = 0;
  1294. fastCheck[3] = 0;
  1295. }
  1296. } else {
  1297. fastCheck[1] = 0;
  1298. fastCheck[2] = 0;
  1299. fastCheck[3] = 0;
  1300. }
  1301. assert(jsvHasChildren(parent));
  1302. JsVarRef childref = parent->firstChild;
  1303. while (childref) {
  1304. // Don't Lock here, just use GetAddressOf - to try and speed up the finding
  1305. // TODO: We can do this now, but when/if we move to cacheing vars, it'll break
  1306. JsVar *child = jsvGetAddressOf(childref);
  1307. if (*(int*)fastCheck==*(int*)child->varData.str && // speedy check of first 4 bytes
  1308. jsvIsStringEqual(child, name)) {
  1309. // found it! unlock parent but leave child locked
  1310. return jsvLockAgain(child);
  1311. }
  1312. childref = child->nextSibling;
  1313. }
  1314. JsVar *child = 0;
  1315. if (addIfNotFound) {
  1316. child = jsvMakeIntoVariableName(jsvNewFromString(name), 0);
  1317. if (child) // could be out of memory
  1318. jsvAddName(parent, child);
  1319. }
  1320. return child;
  1321. }
  1322. /** Non-recursive finding */
  1323. JsVar *jsvFindChildFromVar(JsVar *parent, JsVar *childName, bool addIfNotFound) {
  1324. JsVar *child;
  1325. JsVarRef childref = parent->firstChild;
  1326. while (childref) {
  1327. child = jsvLock(childref);
  1328. if (jsvIsBasicVarEqual(child, childName)) {
  1329. // found it! unlock parent but leave child locked
  1330. return child;
  1331. }
  1332. childref = child->nextSibling;
  1333. jsvUnLock(child);
  1334. }
  1335. child = 0;
  1336. if (addIfNotFound && childName) {
  1337. if (childName->refs == 0) {
  1338. // Not reffed - great! let's just use it
  1339. if (!jsvIsName(childName))
  1340. childName = jsvMakeIntoVariableName(childName, 0);
  1341. child = jsvLockAgain(childName);
  1342. } else { // it was reffed, we must add a new one
  1343. child = jsvMakeIntoVariableName(jsvCopy(childName), 0);
  1344. }
  1345. jsvAddName(parent, child);
  1346. }
  1347. return child;
  1348. }
  1349. void jsvRemoveChild(JsVar *parent, JsVar *child) {
  1350. assert(jsvHasChildren(parent));
  1351. JsVarRef childref = jsvGetRef(child);
  1352. // unlink from parent
  1353. if (parent->firstChild == childref)
  1354. parent->firstChild = child->nextSibling;
  1355. if (parent->lastChild == childref)
  1356. parent->lastChild = child->prevSibling;
  1357. // unlink from child list
  1358. if (child->prevSibling) {
  1359. JsVar *v = jsvLock(child->prevSibling);
  1360. v->nextSibling = child->nextSibling;
  1361. jsvUnLock(v);
  1362. }
  1363. if (child->nextSibling) {
  1364. JsVar *v = jsvLock(child->nextSibling);
  1365. v->prevSibling = child->prevSibling;
  1366. jsvUnLock(v);
  1367. }
  1368. child->prevSibling = 0;
  1369. child->nextSibling = 0;
  1370. jsvUnRef(child);
  1371. }
  1372. void jsvRemoveAllChildren(JsVar *parent) {
  1373. assert(jsvHasChildren(parent));
  1374. while (parent->firstChild) {
  1375. JsVar *v = jsvLock(parent->firstChild);
  1376. jsvRemoveChild(parent, v);
  1377. jsvUnLock(v);
  1378. }
  1379. }
  1380. /// Get the named child of an object. If createChild!=0 then create the child
  1381. JsVar *jsvObjectGetChild(JsVar *obj, const char *name, JsVarFlags createChild) {
  1382. if (!obj) return 0;
  1383. assert(jsvHasChildren(obj));
  1384. JsVar *childName = jsvFindChildFromString(obj, name, createChild);
  1385. if (!childName && createChild) {
  1386. JsVar *child = jsvNewWithFlags(createChild);
  1387. jsvSetValueOfName(childName, child);
  1388. jsvUnLock(childName);
  1389. return child;
  1390. }
  1391. if (childName)
  1392. return jsvSkipNameAndUnLock(childName);
  1393. return 0;
  1394. }
  1395. /// Set the named child of an object, and return the child (so you can choose to unlock it if you want)
  1396. JsVar *jsvObjectSetChild(JsVar *obj, const char *name, JsVar *child) {
  1397. assert(jsvHasChildren(obj));
  1398. // child can actually be a name (for instance if it is a named function)
  1399. JsVar *childName = jsvFindChildFromString(obj, name, true);
  1400. if (!childName) return 0; // out of memory
  1401. jsvSetValueOfName(childName, child);
  1402. jsvUnLock(childName);
  1403. return child;
  1404. }
  1405. int jsvGetChildren(JsVar *v) {
  1406. //OPT: could length be stored as the value of the array?
  1407. int children = 0;
  1408. JsVarRef childref = v->firstChild;
  1409. while (childref) {
  1410. JsVar *child = jsvLock(childref);
  1411. children++;
  1412. childref = child->nextSibling;
  1413. jsvUnLock(child);
  1414. }
  1415. return children;
  1416. }
  1417. JsVarInt jsvGetArrayLength(JsVar *arr) {
  1418. JsVarRef childref = arr->lastChild;
  1419. // Just look at last non-string element!
  1420. while (childref) {
  1421. JsVar *child = jsvLock(childref);
  1422. if (jsvIsInt(child)) {
  1423. JsVarInt lastIdx = jsvGetInteger(child);
  1424. jsvUnLock(child);
  1425. return lastIdx+1;
  1426. }
  1427. // if not an int, keep going
  1428. childref = child->prevSibling;
  1429. jsvUnLock(child);
  1430. }
  1431. return 0;
  1432. }
  1433. JsVarInt jsvGetLength(JsVar *src) {
  1434. if (jsvIsArray(src)) {
  1435. return jsvGetArrayLength(src);
  1436. } else if (jsvIsArrayBuffer(src)) {
  1437. return (JsVarInt)jsvGetArrayBufferLength(src);
  1438. } else if (jsvIsString(src)) {
  1439. return (JsVarInt)jsvGetStringLength(src);
  1440. } else if (jsvIsObject(src) || jsvIsFunction(src)) {
  1441. return jsvGetChildren(src);
  1442. } else {
  1443. return 1;
  1444. }
  1445. }
  1446. /** Count the amount of JsVars used. Mostly useful for debugging */
  1447. size_t jsvCountJsVarsUsed(JsVar *v) {
  1448. size_t count = 1;
  1449. if (jsvHasChildren(v)) {
  1450. JsVarRef childref = v->firstChild;
  1451. while (childref) {
  1452. JsVar *child = jsvLock(childref);
  1453. count += jsvCountJsVarsUsed(child);
  1454. childref = child->nextSibling;
  1455. jsvUnLock(child);
  1456. }
  1457. }
  1458. if (jsvHasCharacterData(v)) {
  1459. size_t count = 0;
  1460. JsVarRef childref = v->lastChild;
  1461. while (childref) {
  1462. JsVar *child = jsvLock(childref);
  1463. count++;
  1464. childref = child->lastChild;
  1465. jsvUnLock(child);
  1466. }
  1467. }
  1468. if (jsvIsName(v) && v->firstChild) {
  1469. JsVar *child = jsvLock(v->firstChild);
  1470. count += jsvCountJsVarsUsed(child);
  1471. jsvUnLock(child);
  1472. }
  1473. return count;
  1474. }
  1475. JsVar *jsvGetArrayItem(JsVar *arr, int index) {
  1476. JsVarRef childref = arr->firstChild;
  1477. while (childref) {
  1478. JsVarInt childIndex;
  1479. JsVar *child = jsvLock(childref);
  1480. assert(jsvIsInt(child));
  1481. childIndex = jsvGetInteger(child);
  1482. if (childIndex == index) {
  1483. JsVar *item = child->firstChild ? jsvLock(child->firstChild) : 0;
  1484. jsvUnLock(child);
  1485. return item;
  1486. }
  1487. childref = child->nextSibling;
  1488. jsvUnLock(child);
  1489. }
  1490. return 0; // undefined
  1491. }
  1492. /// Get the index of the value in the array (matchExact==use pointer, not equality check)
  1493. JsVar *jsvGetArrayIndexOf(JsVar *arr, JsVar *value, bool matchExact) {
  1494. JsVarRef indexref;
  1495. assert(jsvIsArray(arr) || jsvIsObject(arr));
  1496. indexref = arr->firstChild;
  1497. while (indexref) {
  1498. JsVar *childIndex = jsvLock(indexref);
  1499. assert(jsvIsName(childIndex))
  1500. if (childIndex->firstChild) {
  1501. JsVar *childValue = jsvLock(childIndex->firstChild);
  1502. if ((matchExact && childValue==value) ||
  1503. (!matchExact && jsvIsBasicVarEqual(childValue, value))) {
  1504. jsvUnLock(childValue);
  1505. return childIndex;
  1506. }
  1507. jsvUnLock(childValue);
  1508. } else if (jsvIsUndefined(value))
  1509. return childIndex; // both are undefined, so we return the index
  1510. indexref = childIndex->nextSibling;
  1511. jsvUnLock(childIndex);
  1512. }
  1513. return 0; // undefined
  1514. }
  1515. /// 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.
  1516. JsVarInt jsvArrayPushWithInitialSize(JsVar *arr, JsVar *value, JsVarInt initialValue) {
  1517. assert(jsvIsArray(arr));
  1518. JsVarInt index = jsvGetArrayLength(arr);
  1519. if (index==0) index=initialValue;
  1520. JsVar *idx = jsvMakeIntoVariableName(jsvNewFromInteger(index), value);
  1521. if (!idx) {
  1522. jsWarn("Out of memory while appending to array");
  1523. return 0;
  1524. }
  1525. jsvAddName(arr, idx);
  1526. jsvUnLock(idx);
  1527. return index+1; // new size
  1528. }
  1529. /// Adds new elements to the end of an array, and returns the new length
  1530. JsVarInt jsvArrayPush(JsVar *arr, JsVar *value) {
  1531. return jsvArrayPushWithInitialSize(arr, value, 0);
  1532. }
  1533. /// Adds a new element to the end of an array, unlocks it, and returns the new length
  1534. JsVarInt jsvArrayPushAndUnLock(JsVar *arr, JsVar *value) {
  1535. JsVarInt l = jsvArrayPushWithInitialSize(arr, value, 0);
  1536. jsvUnLock(value);
  1537. return l;
  1538. }
  1539. /// Removes the last element of an array, and returns that element (or 0 if empty). includes the NAME
  1540. JsVar *jsvArrayPop(JsVar *arr) {
  1541. assert(jsvIsArray(arr));
  1542. if (arr->lastChild) {
  1543. JsVar *child = jsvLock(arr->lastChild);
  1544. if (arr->firstChild == arr->lastChild)
  1545. arr->firstChild = 0; // if 1 item in array
  1546. arr->lastChild = child->prevSibling; // unlink from end of array
  1547. jsvUnRef(child); // as no longer in array
  1548. if (child->prevSibling) {
  1549. JsVar *v = jsvLock(child->prevSibling);
  1550. v->nextSibling = 0;
  1551. jsvUnLock(v);
  1552. }
  1553. child->prevSibling = 0;
  1554. return child; // and return it
  1555. } else {
  1556. // no children!
  1557. return 0;
  1558. }
  1559. }
  1560. /// Removes the first element of an array, and returns that element (or 0 if empty).
  1561. JsVar *jsvArrayPopFirst(JsVar *arr) {
  1562. assert(jsvIsArray(arr));
  1563. if (arr->firstChild) {
  1564. JsVar *child = jsvLock(arr->firstChild);
  1565. if (arr->firstChild == arr->lastChild)
  1566. arr->lastChild = 0; // if 1 item in array
  1567. arr->firstChild = child->nextSibling; // unlink from end of array
  1568. jsvUnRef(child); // as no longer in array
  1569. if (child->nextSibling) {
  1570. JsVar *v = jsvLock(child->nextSibling);
  1571. v->prevSibling = 0;
  1572. jsvUnLock(v);
  1573. }
  1574. child->nextSibling = 0;
  1575. return child; // and return it
  1576. } else {
  1577. // no children!
  1578. return 0;
  1579. }
  1580. }
  1581. /// Get the last element of an array (does not remove, unlike jsvArrayPop), and returns that element (or 0 if empty) includes the NAME
  1582. JsVar *jsvArrayGetLast(JsVar *arr) {
  1583. assert(jsvIsArray(arr));
  1584. if (arr->lastChild) {
  1585. return jsvLock(arr->lastChild);
  1586. } else { // no children!
  1587. return 0;
  1588. }
  1589. }
  1590. /// Join all elements of an array together into a string
  1591. JsVar *jsvArrayJoin(JsVar *arr, JsVar *filler) {
  1592. JsVar *str = jsvNewFromEmptyString();
  1593. if (!str) return 0; // out of memory
  1594. JsVarInt index = 0;
  1595. JsvIterator it;
  1596. jsvIteratorNew(&it, arr);
  1597. while (jsvIteratorHasElement(&it)) {
  1598. JsVar *key = jsvIteratorGetKey(&it);
  1599. if (jsvIsInt(key)) {
  1600. JsVarInt thisIndex = jsvGetInteger(key);
  1601. // add the filler
  1602. if (filler) {
  1603. while (index<thisIndex) {
  1604. index++;
  1605. jsvAppendStringVarComplete(str, filler);
  1606. }
  1607. }
  1608. // add the value
  1609. JsVar *value = jsvIteratorGetValue(&it);
  1610. if (value) {
  1611. JsVar *valueStr = jsvAsString(value, true /* UNLOCK */);
  1612. if (valueStr) { // could be out of memory
  1613. jsvAppendStringVarComplete(str, valueStr);
  1614. jsvUnLock(valueStr);
  1615. }
  1616. }
  1617. }
  1618. jsvUnLock(key);
  1619. jsvIteratorNext(&it);
  1620. }
  1621. jsvIteratorFree(&it);
  1622. return str;
  1623. }
  1624. /// Insert a new element before beforeIndex, DOES NOT UPDATE INDICES
  1625. void jsvArrayInsertBefore(JsVar *arr, JsVar *beforeIndex, JsVar *element) {
  1626. if (beforeIndex) {
  1627. JsVar *idxVar = jsvMakeIntoVariableName(jsvNewFromInteger(0), element);
  1628. if (!idxVar) return; // out of memory
  1629. JsVarRef idxRef = jsvGetRef(jsvRef(idxVar));
  1630. JsVarRef prev = beforeIndex->prevSibling;
  1631. if (prev) {
  1632. JsVar *prevVar = jsvRef(jsvLock(prev));
  1633. jsvSetInteger(idxVar, jsvGetInteger(prevVar)+1); // update index number
  1634. prevVar->nextSibling = idxRef;
  1635. jsvUnLock(prevVar);
  1636. idxVar->prevSibling = prev;
  1637. } else {
  1638. idxVar->prevSibling = 0;
  1639. arr->firstChild = idxRef;
  1640. }
  1641. beforeIndex->prevSibling = idxRef;
  1642. idxVar->nextSibling = jsvGetRef(jsvRef(beforeIndex));
  1643. jsvUnLock(idxVar);
  1644. } else
  1645. jsvArrayPush(arr, element);
  1646. }
  1647. /** Same as jsvMathsOpPtr, but if a or b are a name, skip them
  1648. * and go to what they point to. */
  1649. JsVar *jsvMathsOpSkipNames(JsVar *a, JsVar *b, int op) {
  1650. JsVar *pa = jsvSkipName(a);
  1651. JsVar *pb = jsvSkipName(b);
  1652. JsVar *res = jsvMathsOp(pa,pb,op);
  1653. jsvUnLock(pa);
  1654. jsvUnLock(pb);
  1655. return res;
  1656. }
  1657. JsVar *jsvMathsOpError(int op, const char *datatype) {
  1658. char buf[JS_ERROR_BUF_SIZE];
  1659. size_t bufpos = 0;
  1660. strncpy(&buf[bufpos], "Operation ", JS_ERROR_BUF_SIZE-bufpos);
  1661. bufpos=strlen(buf);
  1662. jslTokenAsString(op, &buf[bufpos], JS_ERROR_TOKEN_BUF_SIZE-bufpos);
  1663. bufpos=strlen(buf);
  1664. strncat(&buf[bufpos], " not supported on the ", JS_ERROR_BUF_SIZE-bufpos);
  1665. bufpos=strlen(buf);
  1666. strncat(&buf[bufpos], datatype, JS_ERROR_BUF_SIZE-bufpos);
  1667. bufpos=strlen(buf);
  1668. strncat(&buf[bufpos], " datatype", JS_ERROR_BUF_SIZE-bufpos);
  1669. jsError(buf);
  1670. return 0;
  1671. }
  1672. JsVar *jsvMathsOp(JsVar *a, JsVar *b, int op) {
  1673. // Type equality check
  1674. if (op == LEX_TYPEEQUAL || op == LEX_NTYPEEQUAL) {
  1675. // check type first, then call again to check data
  1676. bool eql = (a==0) == (b==0);
  1677. if (a && b) eql = ((a->flags & JSV_VARTYPEMASK) ==
  1678. (b->flags & JSV_VARTYPEMASK));
  1679. if (eql) {
  1680. JsVar *contents = jsvMathsOp(a,b, LEX_EQUAL);
  1681. if (!jsvGetBool(contents)) eql = false;
  1682. jsvUnLock(contents);
  1683. }
  1684. if (op == LEX_TYPEEQUAL)
  1685. return jsvNewFromBool(eql);
  1686. else
  1687. return jsvNewFromBool(!eql);
  1688. }
  1689. bool needsInt = op=='&' || op=='|' || op=='^' || op=='%' || op==LEX_LSHIFT || op==LEX_RSHIFT || op==LEX_RSHIFTUNSIGNED;
  1690. bool needsNumeric = needsInt || op=='*' || op=='/' || op=='%' || op=='-';
  1691. // do maths...
  1692. if (jsvIsUndefined(a) && jsvIsUndefined(b)) {
  1693. if (op == LEX_EQUAL)
  1694. return jsvNewFromBool(true);
  1695. else if (op == LEX_NEQUAL)
  1696. return jsvNewFromBool(false);
  1697. else
  1698. return 0; // undefined
  1699. } else if (needsNumeric ||
  1700. ((jsvIsNumeric(a) || jsvIsUndefined(a) || jsvIsNull(a)) &&
  1701. (jsvIsNumeric(b) || jsvIsUndefined(b) || jsvIsNull(b)))) {
  1702. if (needsInt || !(jsvIsFloat(a) || jsvIsFloat(b))) {
  1703. // use ints
  1704. JsVarInt da = jsvGetInteger(a);
  1705. JsVarInt db = jsvGetInteger(b);
  1706. switch (op) {
  1707. case '+': return jsvNewFromInteger(da+db);
  1708. case '-': return jsvNewFromInteger(da-db);
  1709. case '*': return jsvNewFromInteger(da*db);
  1710. case '/': return jsvNewFromFloat((JsVarFloat)da/(JsVarFloat)db);
  1711. case '&': return jsvNewFromInteger(da&db);
  1712. case '|': return jsvNewFromInteger(da|db);
  1713. case '^': return jsvNewFromInteger(da^db);
  1714. case '%': return jsvNewFromInteger(da%db);
  1715. case LEX_LSHIFT: return jsvNewFromInteger(da << db);
  1716. case LEX_RSHIFT: return jsvNewFromInteger(da >> db);
  1717. case LEX_RSHIFTUNSIGNED: return jsvNewFromInteger((JsVarInt)(((JsVarIntUnsigned)da) >> db));
  1718. case LEX_EQUAL: return jsvNewFromBool(da==db);
  1719. case LEX_NEQUAL: return jsvNewFromBool(da!=db);
  1720. case '<': return jsvNewFromBool(da<db);
  1721. case LEX_LEQUAL: return jsvNewFromBool(da<=db);
  1722. case '>': return jsvNewFromBool(da>db);
  1723. case LEX_GEQUAL: return jsvNewFromBool(da>=db);
  1724. default: return jsvMathsOpError(op, "Integer");
  1725. }
  1726. } else {
  1727. // use doubles
  1728. JsVarFloat da = jsvGetFloat(a);
  1729. JsVarFloat db = jsvGetFloat(b);
  1730. switch (op) {
  1731. case '+': return jsvNewFromFloat(da+db);
  1732. case '-': return jsvNewFromFloat(da-db);
  1733. case '*': return jsvNewFromFloat(da*db);
  1734. case '/': return jsvNewFromFloat(da/db);
  1735. case LEX_EQUAL: return jsvNewFromBool(da==db);
  1736. case LEX_NEQUAL: return jsvNewFromBool(da!=db);
  1737. case '<': return jsvNewFromBool(da<db);
  1738. case LEX_LEQUAL: return jsvNewFromBool(da<=db);
  1739. case '>': return jsvNewFromBool(da>db);
  1740. case LEX_GEQUAL: return jsvNewFromBool(da>=db);
  1741. default: return jsvMathsOpError(op, "Double");
  1742. }
  1743. }
  1744. } else if ((jsvIsArray(a) || jsvIsObject(a) ||
  1745. jsvIsArray(b) || jsvIsObject(b)) &&
  1746. (op == LEX_EQUAL || op==LEX_NEQUAL)) {
  1747. bool isArray = jsvIsArray(a);
  1748. /* Just check pointers */
  1749. switch (op) {
  1750. case LEX_EQUAL: return jsvNewFromBool(a==b);
  1751. case LEX_NEQUAL: return jsvNewFromBool(a!=b);
  1752. default: return jsvMathsOpError(op, isArray?"Array":"Object");
  1753. }
  1754. } else {
  1755. JsVar *da = jsvAsString(a, false);
  1756. JsVar *db = jsvAsString(b, false);
  1757. if (!da || !db) { // out of memory
  1758. jsvUnLock(da);
  1759. jsvUnLock(db);
  1760. return 0;
  1761. }
  1762. if (op=='+') {
  1763. JsVar *v = jsvCopy(da);
  1764. // TODO: can we be fancy and not copy da if we know it isn't reffed? what about locks?
  1765. if (v) // could be out of memory
  1766. jsvAppendStringVarComplete(v, db);
  1767. jsvUnLock(da);
  1768. jsvUnLock(db);
  1769. return v;
  1770. }
  1771. int cmp = jsvCompareString(da,db,0,0,false);
  1772. jsvUnLock(da);
  1773. jsvUnLock(db);
  1774. // use strings
  1775. switch (op) {
  1776. case LEX_EQUAL: return jsvNewFromBool(cmp==0);
  1777. case LEX_NEQUAL: return jsvNewFromBool(cmp!=0);
  1778. case '<': return jsvNewFromBool(cmp<0);
  1779. case LEX_LEQUAL: return jsvNewFromBool(cmp<=0);
  1780. case '>': return jsvNewFromBool(cmp>0);
  1781. case LEX_GEQUAL: return jsvNewFromBool(cmp>=0);
  1782. default: return jsvMathsOpError(op, "String");
  1783. }
  1784. }
  1785. }
  1786. JsVar *jsvNegateAndUnLock(JsVar *v) {
  1787. JsVar *zero = jsvNewFromInteger(0);
  1788. JsVar *res = jsvMathsOpSkipNames(zero, v, '-');
  1789. jsvUnLock(zero);
  1790. jsvUnLock(v);
  1791. return res;
  1792. }
  1793. void jsvTraceLockInfo(JsVar *v) {
  1794. jsiConsolePrintf("#%d[r%d,l%d] ",jsvGetRef(v),v->refs,jsvGetLocks(v)-1);
  1795. }
  1796. /** Get the lowest level at which searchRef appears */
  1797. int _jsvTraceGetLowestLevel(JsVarRef ref, JsVarRef searchRef) {
  1798. if (ref == searchRef) return 0;
  1799. int found = -1;
  1800. JsVar *var = jsvLock(ref);
  1801. // Use IS_RECURSING flag to stop recursion
  1802. if (var->flags & JSV_IS_RECURSING) {
  1803. jsvUnLock(var);
  1804. return -1;
  1805. }
  1806. var->flags |= JSV_IS_RECURSING;
  1807. if (jsvHasSingleChild(var) && var->firstChild) {
  1808. int f = _jsvTraceGetLowestLevel(var->firstChild, searchRef);
  1809. if (f>=0 && (found<0 || f<found)) found=f+1;
  1810. }
  1811. if (jsvHasChildren(var)) {
  1812. JsVarRef childRef = var->firstChild;
  1813. while (childRef) {
  1814. int f = _jsvTraceGetLowestLevel(childRef, searchRef);
  1815. if (f>=0 && (found<0 || f<found)) found=f+1;
  1816. JsVar *child = jsvLock(childRef);
  1817. childRef = child->nextSibling;
  1818. jsvUnLock(child);
  1819. }
  1820. }
  1821. var->flags &= ~JSV_IS_RECURSING;
  1822. jsvUnLock(var);
  1823. return found; // searchRef not found
  1824. }
  1825. void _jsvTrace(JsVarRef ref, int indent, JsVarRef baseRef, int level) {
  1826. #ifdef SAVE_ON_FLASH
  1827. jsiConsolePrint("Trace unimplemented in this version.\n");
  1828. #else
  1829. int i;
  1830. for (i=0;i<indent;i++) jsiConsolePrint(" ");
  1831. if (!ref) {
  1832. jsiConsolePrint("undefined\n");
  1833. return;
  1834. }
  1835. /*jsiConsolePrint("<");
  1836. jsiConsolePrintInt(level);
  1837. jsiConsolePrint(":");
  1838. jsiConsolePrintInt(_jsvTraceGetLowestLevel(baseRef, ref));
  1839. jsiConsolePrint("> ");*/
  1840. JsVar *var = jsvLock(ref);
  1841. jsvTraceLockInfo(var);
  1842. if (jsvIsName(var)) {
  1843. if (jsvIsFunctionParameter(var))
  1844. jsiConsolePrint("Param ");
  1845. JsVar *str = jsvAsString(var, false);
  1846. if (jsvIsInt(var)) {
  1847. jsiConsolePrintf("Name: int %v ", str);
  1848. } else if (jsvIsFloat(var)) {
  1849. jsiConsolePrintf("Name: flt %v ", str);
  1850. } else if (jsvIsString(var) || jsvIsFunctionParameter(var)) {
  1851. jsiConsolePrintf("Name: '%v' ", str);
  1852. } else if (jsvIsArrayBufferName(var)) {
  1853. jsiConsolePrintf("ArrayBufferName[%d] ", jsvGetInteger(var));
  1854. } else {
  1855. assert(0);
  1856. }
  1857. jsvUnLock(str);
  1858. // go to what the name points to
  1859. ref = var->firstChild;
  1860. jsvUnLock(var);
  1861. if (ref) {
  1862. level++;
  1863. int lowestLevel = _jsvTraceGetLowestLevel(baseRef, ref);
  1864. /*jsiConsolePrint("<");
  1865. jsiConsolePrintInt(level);
  1866. jsiConsolePrint(":");
  1867. jsiConsolePrintInt(lowestLevel);
  1868. jsiConsolePrint("> ");*/
  1869. var = jsvLock(ref);
  1870. jsvTraceLockInfo(var);
  1871. if (lowestLevel < level) {
  1872. // If this data is available elsewhere in the tree (but nearer the root)
  1873. // then don't print it. This makes the dump significantly more readable!
  1874. // It also stops us getting in recursive loops ...
  1875. jsiConsolePrint("...\n");
  1876. jsvUnLock(var);
  1877. return;
  1878. }
  1879. } else {
  1880. jsiConsolePrint("undefined\n");
  1881. return;
  1882. }
  1883. }
  1884. if (jsvIsName(var)) {
  1885. jsiConsolePrint("\n");
  1886. _jsvTrace(jsvGetRef(var), indent+2, baseRef, level+1);
  1887. jsvUnLock(var);
  1888. return;
  1889. }
  1890. if (jsvIsObject(var)) jsiConsolePrint("Object {");
  1891. else if (jsvIsArray(var)) jsiConsolePrint("Array [");
  1892. else if (jsvIsPin(var)) jsiConsolePrint("Pin ");
  1893. else if (jsvIsInt(var)) jsiConsolePrint("Integer ");
  1894. else if (jsvIsBoolean(var)) jsiConsolePrint("Bool ");
  1895. else if (jsvIsFloat(var)) jsiConsolePrint("Double ");
  1896. else if (jsvIsString(var)) jsiConsolePrint("String ");
  1897. else if (jsvIsArrayBuffer(var)) {
  1898. jsiConsolePrintf("%s ", jswGetBasicObjectName(var)); // way to get nice name
  1899. _jsvTrace(var->firstChild, indent+1, baseRef, level+1);
  1900. jsvUnLock(var);
  1901. return;
  1902. } else if (jsvIsFunction(var)) jsiConsolePrint("Function {");
  1903. else {
  1904. jsiConsolePrintf("Flags %d\n", var->flags & (JsVarFlags)~(JSV_LOCK_MASK));
  1905. }
  1906. if (!jsvIsObject(var) && !jsvIsArray(var) && !jsvIsFunction(var)) {
  1907. JsVar *str = jsvAsString(var, false);
  1908. if (str) {
  1909. JsvStringIterator it;
  1910. jsvStringIteratorNew(&it, str, 0);
  1911. while (jsvStringIteratorHasChar(&it)) {
  1912. char ch = jsvStringIteratorGetChar(&it);
  1913. jsiConsolePrint(escapeCharacter(ch));
  1914. jsvStringIteratorNext(&it);
  1915. }
  1916. jsvStringIteratorFree(&it);
  1917. jsvUnLock(str);
  1918. }
  1919. }
  1920. if (jsvHasStringExt(var)) {
  1921. if (!jsvIsStringExt(var) && var->firstChild) { // stringext don't have children (the use them for chars)
  1922. jsiConsolePrint("( Multi-block string ");
  1923. JsVarRef child = var->firstChild;
  1924. while (child) {
  1925. JsVar *childVar = jsvLock(child);
  1926. jsvTraceLockInfo(childVar);
  1927. child = childVar->firstChild;
  1928. jsvUnLock(childVar);
  1929. }
  1930. jsiConsolePrint(")\n");
  1931. } else
  1932. jsiConsolePrint("\n");
  1933. } else {
  1934. JsVarRef child = var->firstChild;
  1935. jsiConsolePrint("\n");
  1936. // dump children
  1937. while (child) {
  1938. JsVar *childVar;
  1939. _jsvTrace(child, indent+2, baseRef, level+1);
  1940. childVar = jsvLock(child);
  1941. child = childVar->nextSibling;
  1942. jsvUnLock(childVar);
  1943. }
  1944. }
  1945. if (jsvIsObject(var) || jsvIsFunction(var) || jsvIsArray(var)) {
  1946. int i;
  1947. for (i=0;i<indent;i++) jsiConsolePrint(" ");
  1948. jsiConsolePrint(jsvIsArray(var) ? "]\n" : "}\n");
  1949. }
  1950. jsvUnLock(var);
  1951. #endif
  1952. }
  1953. /** Write debug info for this Var out to the console */
  1954. void jsvTrace(JsVarRef ref, int indent) {
  1955. _jsvTrace(ref,indent,ref,0);
  1956. }
  1957. /** Recursively mark the variable */
  1958. static void jsvGarbageCollectMarkUsed(JsVar *var) {
  1959. var->flags &= (JsVarFlags)~JSV_GARBAGE_COLLECT;
  1960. if (jsvHasCharacterData(var)) {
  1961. if (var->lastChild) {
  1962. JsVar *childVar = jsvGetAddressOf(var->lastChild);
  1963. if (childVar->flags & JSV_GARBAGE_COLLECT)
  1964. jsvGarbageCollectMarkUsed(childVar);
  1965. }
  1966. }
  1967. // intentionally no else
  1968. if (jsvHasSingleChild(var)) {
  1969. if (var->firstChild) {
  1970. JsVar *childVar = jsvGetAddressOf(var->firstChild);
  1971. if (childVar->flags & JSV_GARBAGE_COLLECT)
  1972. jsvGarbageCollectMarkUsed(childVar);
  1973. }
  1974. } else if (jsvHasChildren(var)) {
  1975. JsVarRef child = var->firstChild;
  1976. while (child) {
  1977. JsVar *childVar;
  1978. childVar = jsvGetAddressOf(child);
  1979. if (childVar->flags & JSV_GARBAGE_COLLECT)
  1980. jsvGarbageCollectMarkUsed(childVar);
  1981. child = childVar->nextSibling;
  1982. }
  1983. }
  1984. }
  1985. /** Run a garbage collection sweep - return true if things have been freed */
  1986. bool jsvGarbageCollect() {
  1987. JsVarRef i;
  1988. // clear garbage collect flags
  1989. for (i=1;i<=jsVarsSize;i++) {
  1990. JsVar *var = jsvGetAddressOf(i);
  1991. if ((var->flags&JSV_VARTYPEMASK) != JSV_UNUSED) // if it is not unused
  1992. var->flags |= (JsVarFlags)JSV_GARBAGE_COLLECT;
  1993. }
  1994. // recursively add 'native' vars
  1995. for (i=1;i<=jsVarsSize;i++) {
  1996. JsVar *var = jsvGetAddressOf(i);
  1997. if ((var->flags & JSV_GARBAGE_COLLECT) && // not already GC'd
  1998. jsvGetLocks(var)>0) // or it is locked
  1999. jsvGarbageCollectMarkUsed(var);
  2000. }
  2001. // now sweep for things that we can GC!
  2002. bool freedSomething = false;
  2003. for (i=1;i<=jsVarsSize;i++) {
  2004. JsVar *var = jsvGetAddressOf(i);
  2005. if (var->flags & JSV_GARBAGE_COLLECT) {
  2006. freedSomething = true;
  2007. // free!
  2008. var->flags = JSV_UNUSED;
  2009. // add this to our free list
  2010. var->nextSibling = jsVarFirstEmpty;
  2011. jsVarFirstEmpty = jsvGetRef(var);
  2012. }
  2013. }
  2014. return freedSomething;
  2015. }
  2016. /** Remove whitespace to the right of a string - on MULTIPLE LINES */
  2017. JsVar *jsvStringTrimRight(JsVar *srcString) {
  2018. JsvStringIterator src, dst;
  2019. JsVar *dstString = jsvNewFromEmptyString();
  2020. jsvStringIteratorNew(&src, srcString, 0);
  2021. jsvStringIteratorNew(&dst, dstString, 0);
  2022. int spaces = 0;
  2023. while (jsvStringIteratorHasChar(&src)) {
  2024. char ch = jsvStringIteratorGetChar(&src);
  2025. jsvStringIteratorNext(&src);
  2026. if (ch==' ') spaces++;
  2027. else if (ch=='\n') {
  2028. spaces = 0;
  2029. jsvStringIteratorAppend(&dst, ch);
  2030. } else {
  2031. for (;spaces>0;spaces--)
  2032. jsvStringIteratorAppend(&dst, ' ');
  2033. jsvStringIteratorAppend(&dst, ch);
  2034. }
  2035. }
  2036. jsvStringIteratorFree(&src);
  2037. jsvStringIteratorFree(&dst);
  2038. return dstString;
  2039. }
  2040. /// If v is the key of a function, return true if it is internal and shouldn't be visible to the user
  2041. bool jsvIsInternalFunctionKey(JsVar *v) {
  2042. return (jsvIsString(v) && (
  2043. v->varData.str[0]==JS_HIDDEN_CHAR)
  2044. ) ||
  2045. jsvIsFunctionParameter(v);
  2046. }
  2047. /// If v is the key of an object, return true if it is internal and shouldn't be visible to the user
  2048. bool jsvIsInternalObjectKey(JsVar *v) {
  2049. return (jsvIsString(v) && (
  2050. v->varData.str[0]==JS_HIDDEN_CHAR ||
  2051. jsvIsStringEqual(v, JSPARSE_INHERITS_VAR) ||
  2052. jsvIsStringEqual(v, JSPARSE_CONSTRUCTOR_VAR)
  2053. ));
  2054. }
  2055. // --------------------------------------------------------------------------------------------
  2056. void jsvStringIteratorNew(JsvStringIterator *it, JsVar *str, int startIdx) {
  2057. assert(jsvHasCharacterData(str));
  2058. it->var = jsvLockAgain(str);
  2059. it->charsInVar = jsvGetCharactersInVar(str);
  2060. it->charIdx = (size_t)startIdx;
  2061. it->index = (size_t)startIdx;
  2062. while (it->charIdx>0 && it->charIdx >= it->charsInVar) {
  2063. it->charIdx -= it->charsInVar;
  2064. if (it->var) {
  2065. if (it->var->lastChild) {
  2066. JsVar *next = jsvLock(it->var->lastChild);
  2067. jsvUnLock(it->var);
  2068. it->var = next;
  2069. it->charsInVar = jsvGetCharactersInVar(it->var);
  2070. } else {
  2071. jsvUnLock(it->var);
  2072. it->var = 0;
  2073. it->charsInVar = 0;
  2074. return; // get out of loop
  2075. }
  2076. }
  2077. }
  2078. }
  2079. void jsvStringIteratorNext(JsvStringIterator *it) {
  2080. jsvStringIteratorNextInline(it);
  2081. }
  2082. void jsvStringIteratorGotoEnd(JsvStringIterator *it) {
  2083. assert(it->var);
  2084. while (it->var->lastChild) {
  2085. it->index += it->charsInVar;
  2086. JsVar *next = jsvLock(it->var->lastChild);
  2087. jsvUnLock(it->var);
  2088. it->var = next;
  2089. it->charsInVar = jsvGetCharactersInVar(it->var);
  2090. }
  2091. if (it->charsInVar) it->charIdx = it->charsInVar-1;
  2092. else it->charIdx = 0;
  2093. }
  2094. void jsvStringIteratorAppend(JsvStringIterator *it, char ch) {
  2095. if (!it->var) return;
  2096. if (it->charsInVar>0) {
  2097. assert(it->charIdx+1 == it->charsInVar /* check at end */);
  2098. it->charIdx++;
  2099. it->index++;
  2100. } else
  2101. assert(it->charIdx == 0);
  2102. if (it->charIdx >= jsvGetMaxCharactersInVar(it->var)) {
  2103. assert(!it->var->lastChild);
  2104. JsVar *next = jsvNewWithFlags(JSV_STRING_EXT);
  2105. if (!next) return; // out of memory
  2106. // we don't ref, because StringExts are never reffed as they only have one owner (and ALWAYS have an owner)
  2107. it->var->lastChild = jsvGetRef(next);
  2108. jsvUnLock(it->var);
  2109. it->var = next;
  2110. it->charIdx = 0; // it's new, so empty
  2111. }
  2112. it->var->varData.str[it->charIdx] = ch;
  2113. it->charsInVar = it->charIdx+1;
  2114. jsvSetCharactersInVar(it->var, it->charsInVar);
  2115. }
  2116. // --------------------------------------------------------------------------------------------
  2117. void jsvArrayBufferIteratorNew(JsvArrayBufferIterator *it, JsVar *arrayBuffer, JsVarInt index) {
  2118. assert(jsvIsArrayBuffer(arrayBuffer));
  2119. it->index = index;
  2120. it->type = arrayBuffer->varData.arraybuffer.type;
  2121. it->byteLength = arrayBuffer->varData.arraybuffer.length * JSV_ARRAYBUFFER_GET_SIZE(it->type);
  2122. it->byteOffset = arrayBuffer->varData.arraybuffer.byteOffset;
  2123. JsVar *arrayBufferData = jsvLock(arrayBuffer->firstChild);
  2124. while (jsvIsArrayBuffer(arrayBufferData)) {
  2125. JsVar *s = jsvLock(arrayBufferData->firstChild);
  2126. jsvUnLock(arrayBufferData);
  2127. arrayBufferData = s;
  2128. }
  2129. assert(jsvIsString(arrayBufferData));
  2130. it->byteLength += it->byteOffset; // because we'll check if we have more bytes using this
  2131. it->byteOffset = it->byteOffset + index*JSV_ARRAYBUFFER_GET_SIZE(it->type);
  2132. if (it->byteOffset<0 || (it->byteLength>=0 && it->byteOffset>=(it->byteLength+1-JSV_ARRAYBUFFER_GET_SIZE(it->type)))) {
  2133. jsvUnLock(arrayBufferData);
  2134. it->type = ARRAYBUFFERVIEW_UNDEFINED;
  2135. return;
  2136. }
  2137. jsvStringIteratorNew(&it->it, arrayBufferData, (int)it->byteOffset);
  2138. jsvUnLock(arrayBufferData);
  2139. it->hasAccessedElement = false;
  2140. }
  2141. static void jsvArrayBufferIteratorGetValueData(JsvArrayBufferIterator *it, char *data) {
  2142. if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return;
  2143. assert(!it->hasAccessedElement); // we just haven't implemented this case yet
  2144. unsigned int i,dataLen = JSV_ARRAYBUFFER_GET_SIZE(it->type);
  2145. for (i=0;i<dataLen;i++) {
  2146. data[i] = jsvStringIteratorGetChar(&it->it);
  2147. if (dataLen!=1) jsvStringIteratorNext(&it->it);
  2148. }
  2149. if (dataLen!=1) it->hasAccessedElement = true;
  2150. }
  2151. static JsVarInt jsvArrayBufferIteratorDataToInt(JsvArrayBufferIterator *it, char *data) {
  2152. unsigned int dataLen = JSV_ARRAYBUFFER_GET_SIZE(it->type);
  2153. JsVarInt v = 0;
  2154. if (dataLen==1) v = *(char*)data;
  2155. else if (dataLen==2) v = *(short*)data;
  2156. else if (dataLen==4) v = *(int*)data;
  2157. else if (dataLen==8) v = *(long long*)data;
  2158. else assert(0);
  2159. if ((!JSV_ARRAYBUFFER_IS_SIGNED(it->type)) && v<0)
  2160. v += 1 << (8*dataLen);
  2161. return v;
  2162. }
  2163. static JsVarFloat jsvArrayBufferIteratorDataToFloat(JsvArrayBufferIterator *it, char *data) {
  2164. unsigned int dataLen = JSV_ARRAYBUFFER_GET_SIZE(it->type);
  2165. JsVarFloat v = 0;
  2166. if (dataLen==4) v = *(float*)data;
  2167. else if (dataLen==8) v = *(double*)data;
  2168. else assert(0);
  2169. return v;
  2170. }
  2171. JsVar *jsvArrayBufferIteratorGetValue(JsvArrayBufferIterator *it) {
  2172. if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return 0;
  2173. char data[8];
  2174. jsvArrayBufferIteratorGetValueData(it, data);
  2175. if (JSV_ARRAYBUFFER_IS_FLOAT(it->type)) {
  2176. return jsvNewFromFloat(jsvArrayBufferIteratorDataToFloat(it, data));
  2177. } else {
  2178. return jsvNewFromInteger(jsvArrayBufferIteratorDataToInt(it, data));
  2179. }
  2180. }
  2181. JsVarInt jsvArrayBufferIteratorGetIntegerValue(JsvArrayBufferIterator *it) {
  2182. if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return 0;
  2183. char data[8];
  2184. jsvArrayBufferIteratorGetValueData(it, data);
  2185. if (JSV_ARRAYBUFFER_IS_FLOAT(it->type)) {
  2186. return (JsVarInt)jsvArrayBufferIteratorDataToFloat(it, data);
  2187. } else {
  2188. return jsvArrayBufferIteratorDataToInt(it, data);
  2189. }
  2190. }
  2191. JsVarFloat jsvArrayBufferIteratorGetFloatValue(JsvArrayBufferIterator *it) {
  2192. if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return 0;
  2193. char data[8];
  2194. jsvArrayBufferIteratorGetValueData(it, data);
  2195. if (JSV_ARRAYBUFFER_IS_FLOAT(it->type)) {
  2196. return jsvArrayBufferIteratorDataToFloat(it, data);
  2197. } else {
  2198. return (JsVarFloat)jsvArrayBufferIteratorDataToInt(it, data);
  2199. }
  2200. }
  2201. void jsvArrayBufferIteratorSetValue(JsvArrayBufferIterator *it, JsVar *value) {
  2202. if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return;
  2203. assert(!it->hasAccessedElement); // we just haven't implemented this case yet
  2204. char data[8];
  2205. unsigned int i,dataLen = JSV_ARRAYBUFFER_GET_SIZE(it->type);
  2206. if (JSV_ARRAYBUFFER_IS_FLOAT(it->type)) {
  2207. JsVarFloat v = jsvGetFloat(value); ;
  2208. if (dataLen==4) { float f = (float)v; memcpy(data,&f,dataLen); }
  2209. else if (dataLen==8) { double f = (double)v; memcpy(data,&f,dataLen); }
  2210. else assert(0);
  2211. } else {
  2212. JsVarInt v = jsvGetInteger(value);
  2213. // we don't care about sign when writing - as it gets truncated
  2214. if (dataLen==1) { char c = (char)v; memcpy(data,&c,dataLen); }
  2215. else if (dataLen==2) { short c = (short)v; memcpy(data,&c,dataLen); }
  2216. else if (dataLen==4) { int c = (int)v; memcpy(data,&c,dataLen); }
  2217. else if (dataLen==8) { long long c = (long long)v; memcpy(data,&c,dataLen); }
  2218. else assert(0);
  2219. }
  2220. for (i=0;i<dataLen;i++) {
  2221. jsvStringIteratorSetChar(&it->it, data[i]);
  2222. if (dataLen!=1) jsvStringIteratorNext(&it->it);
  2223. }
  2224. if (dataLen!=1) it->hasAccessedElement = true;
  2225. }
  2226. void jsvArrayBufferIteratorSetIntegerValue(JsvArrayBufferIterator *it, JsVarInt value) {
  2227. // FIXME: Do this without the allocation!
  2228. JsVar *val = jsvNewFromInteger(value);
  2229. jsvArrayBufferIteratorSetValue(it, val);
  2230. jsvUnLock(val);
  2231. }
  2232. JsVar* jsvArrayBufferIteratorGetIndex(JsvArrayBufferIterator *it) {
  2233. return jsvNewFromInteger(it->index);
  2234. }
  2235. bool jsvArrayBufferIteratorHasElement(JsvArrayBufferIterator *it) {
  2236. if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return false;
  2237. if (it->hasAccessedElement) return true;
  2238. if (it->byteLength>=0)
  2239. return it->byteOffset <= (it->byteLength-JSV_ARRAYBUFFER_GET_SIZE(it->type));
  2240. else
  2241. return jsvStringIteratorHasChar(&it->it);
  2242. }
  2243. void jsvArrayBufferIteratorNext(JsvArrayBufferIterator *it) {
  2244. it->index++;
  2245. it->byteOffset += JSV_ARRAYBUFFER_GET_SIZE(it->type);
  2246. if (!it->hasAccessedElement) {
  2247. unsigned int i,dataLen = JSV_ARRAYBUFFER_GET_SIZE(it->type);
  2248. for (i=0;i<dataLen;i++)
  2249. jsvStringIteratorNext(&it->it);
  2250. } else
  2251. it->hasAccessedElement = false;
  2252. }
  2253. void jsvArrayBufferIteratorFree(JsvArrayBufferIterator *it) {
  2254. if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return;
  2255. jsvStringIteratorFree(&it->it);
  2256. }
  2257. // --------------------------------------------------------------------------------------------
  2258. /* General Purpose iterator, for Strings, Arrays, Objects, Typed Arrays */
  2259. void jsvIteratorNew(JsvIterator *it, JsVar *obj) {
  2260. if (jsvIsArray(obj)) {
  2261. it->type = JSVI_ARRAY;
  2262. jsvArrayIteratorNew(&it->it.arr, obj);
  2263. } else if (jsvIsObject(obj) || jsvIsFunction(obj)) {
  2264. it->type = JSVI_OBJECT;
  2265. jsvObjectIteratorNew(&it->it.obj, obj);
  2266. } else if (jsvIsArrayBuffer(obj)) {
  2267. it->type = JSVI_ARRAYBUFFER;
  2268. jsvArrayBufferIteratorNew(&it->it.buf, obj, 0);
  2269. } else if (jsvHasCharacterData(obj)) {
  2270. it->type = JSVI_STRING;
  2271. jsvStringIteratorNew(&it->it.str, obj, 0);
  2272. } else assert(0);
  2273. }
  2274. JsVar *jsvIteratorGetKey(JsvIterator *it) {
  2275. switch (it->type) {
  2276. case JSVI_ARRAY : return jsvArrayIteratorGetIndex(&it->it.arr);
  2277. case JSVI_OBJECT : return jsvObjectIteratorGetKey(&it->it.obj);
  2278. case JSVI_STRING : return jsvNewFromInteger((JsVarInt)jsvStringIteratorGetIndex(&it->it.str));
  2279. case JSVI_ARRAYBUFFER : return jsvArrayBufferIteratorGetIndex(&it->it.buf);
  2280. default: assert(0); return 0;
  2281. }
  2282. }
  2283. JsVar *jsvIteratorGetValue(JsvIterator *it) {
  2284. switch (it->type) {
  2285. case JSVI_ARRAY : return jsvArrayIteratorGetElement(&it->it.arr);
  2286. case JSVI_OBJECT : return jsvObjectIteratorGetValue(&it->it.obj);
  2287. case JSVI_STRING : { char buf[2] = {jsvStringIteratorGetChar(&it->it.str),0}; return jsvNewFromString(buf); }
  2288. case JSVI_ARRAYBUFFER : return jsvArrayBufferIteratorGetValue(&it->it.buf);
  2289. default: assert(0); return 0;
  2290. }
  2291. }
  2292. JsVarInt jsvIteratorGetIntegerValue(JsvIterator *it) {
  2293. switch (it->type) {
  2294. case JSVI_ARRAY : return jsvGetIntegerAndUnLock(jsvArrayIteratorGetElement(&it->it.arr));
  2295. case JSVI_OBJECT : return jsvGetIntegerAndUnLock(jsvObjectIteratorGetValue(&it->it.obj));
  2296. case JSVI_STRING : return (JsVarInt)jsvStringIteratorGetChar(&it->it.str);
  2297. case JSVI_ARRAYBUFFER : return jsvArrayBufferIteratorGetIntegerValue(&it->it.buf);
  2298. default: assert(0); return 0;
  2299. }
  2300. }
  2301. bool jsvIteratorHasElement(JsvIterator *it) {
  2302. switch (it->type) {
  2303. case JSVI_ARRAY : return jsvArrayIteratorHasElement(&it->it.arr);
  2304. case JSVI_OBJECT : return jsvObjectIteratorHasElement(&it->it.obj);
  2305. case JSVI_STRING : return jsvStringIteratorHasChar(&it->it.str);
  2306. case JSVI_ARRAYBUFFER : return jsvArrayBufferIteratorHasElement(&it->it.buf);
  2307. default: assert(0); return 0;
  2308. }
  2309. }
  2310. void jsvIteratorNext(JsvIterator *it) {
  2311. switch (it->type) {
  2312. case JSVI_ARRAY : jsvArrayIteratorNext(&it->it.arr); break;
  2313. case JSVI_OBJECT : jsvObjectIteratorNext(&it->it.obj); break;
  2314. case JSVI_STRING : jsvStringIteratorNext(&it->it.str); break;
  2315. case JSVI_ARRAYBUFFER : jsvArrayBufferIteratorNext(&it->it.buf); break;
  2316. default: assert(0); break;
  2317. }
  2318. }
  2319. void jsvIteratorFree(JsvIterator *it) {
  2320. switch (it->type) {
  2321. case JSVI_ARRAY : jsvArrayIteratorFree(&it->it.arr); break;
  2322. case JSVI_OBJECT : jsvObjectIteratorFree(&it->it.obj); break;
  2323. case JSVI_STRING : jsvStringIteratorFree(&it->it.str); break;
  2324. case JSVI_ARRAYBUFFER : jsvArrayBufferIteratorFree(&it->it.buf); break;
  2325. default: assert(0); break;
  2326. }
  2327. }