jsvar.h 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  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. #ifndef JSVAR_H_
  15. #define JSVAR_H_
  16. #include "jsutils.h"
  17. typedef void (*JsCallback)(JsVarRef var)
  18. #ifdef SDCC
  19. __reentrant
  20. #endif
  21. ;
  22. /** To avoid confusion - JsVarRefCounter should be big enough
  23. * to store as many refs as can possibly be created - so it's
  24. * safe just to set it to the same size as JsVarRef. However
  25. * it is NOT a reference itself.
  26. */
  27. typedef JsVarRef JsVarRefCounter;
  28. typedef enum {
  29. ARRAYBUFFERVIEW_UNDEFINED = 0,
  30. ARRAYBUFFERVIEW_ARRAYBUFFER = 1 | 64, ///< Basic ArrayBuffer type
  31. ARRAYBUFFERVIEW_MASK_SIZE = 15,
  32. ARRAYBUFFERVIEW_SIGNED = 16,
  33. ARRAYBUFFERVIEW_FLOAT = 32,
  34. ARRAYBUFFERVIEW_UINT8 = 1,
  35. ARRAYBUFFERVIEW_INT8 = 1 | ARRAYBUFFERVIEW_SIGNED,
  36. ARRAYBUFFERVIEW_UINT16 = 2,
  37. ARRAYBUFFERVIEW_INT16 = 2 | ARRAYBUFFERVIEW_SIGNED,
  38. ARRAYBUFFERVIEW_UINT32 = 4,
  39. ARRAYBUFFERVIEW_INT32 = 4 | ARRAYBUFFERVIEW_SIGNED,
  40. ARRAYBUFFERVIEW_FLOAT32 = 4 | ARRAYBUFFERVIEW_FLOAT,
  41. ARRAYBUFFERVIEW_FLOAT64 = 8 | ARRAYBUFFERVIEW_FLOAT,
  42. } PACKED_FLAGS JsVarDataArrayBufferViewType;
  43. #define JSV_ARRAYBUFFER_GET_SIZE(T) ((T)&ARRAYBUFFERVIEW_MASK_SIZE)
  44. #define JSV_ARRAYBUFFER_IS_SIGNED(T) (((T)&ARRAYBUFFERVIEW_SIGNED)!=0)
  45. #define JSV_ARRAYBUFFER_IS_FLOAT(T) (((T)&ARRAYBUFFERVIEW_FLOAT)!=0)
  46. #define JSV_ARRAYBUFFER_MAX_LENGTH 65535
  47. typedef struct {
  48. unsigned short byteOffset;
  49. unsigned short length;
  50. JsVarDataArrayBufferViewType type;
  51. } PACKED_FLAGS JsVarDataArrayBufferView;
  52. typedef union {
  53. char str[JSVAR_DATA_STRING_LEN]; ///< The contents of this variable if it is a string
  54. /* NOTE: For str above, we INTENTIONALLY OVERFLOW str (and hence data) in the case of STRING_EXTS
  55. * to overwrite 3 references in order to grab another 6 bytes worth of string data */
  56. // TODO do some magic with union/structs in order to make sure we don't intentionally write off the end of arrays
  57. JsVarInt integer; ///< The contents of this variable if it is an int
  58. JsVarFloat floating; ///< The contents of this variable if it is a double
  59. JsCallback callback; ///< Callback for native functions, or 0
  60. JsVarDataArrayBufferView arraybuffer; ///< information for array buffer views.
  61. } PACKED_FLAGS JsVarData;
  62. typedef struct {
  63. #ifdef LARGE_MEM
  64. JsVarRef this; ///< The reference of this variable itself (so we can get back)
  65. #endif
  66. JsVarFlags flags; ///< the flags determine the type of the variable - int/double/string/etc.
  67. JsVarData varData;
  68. /* NOTE: WE INTENTIONALLY OVERFLOW data in the case of STRING_EXTS
  69. * to overwrite the following 3 references in order to grab another
  70. * 6 bytes worth of string data */
  71. /* For Variable NAMES (e.g. Object/Array keys) these store actual next/previous pointers for a linked list
  72. * For STRING_EXT - extra characters
  73. * Not used for other stuff
  74. */
  75. JsVarRef nextSibling;
  76. JsVarRef prevSibling;
  77. JsVarRefCounter refs; ///< The number of references held to this - used for automatic garbage collection. NOT USED for STRINGEXT though (it is just extra characters)
  78. /**
  79. * For OBJECT/ARRAY/FUNCTION - this is the first child
  80. * For NAMES and REF - this is a link to the variable it points to
  81. * For STRING_EXT - extra character data (NOT a link)
  82. * For ARRAYBUFFER - a link to a string containing the data for the array buffer *
  83. */
  84. JsVarRef firstChild;
  85. /**
  86. * For OBJECT/ARRAY/FUNCTION - this is the last child
  87. * For STRINGS/STRING_EXT/NAME+STRING - this is a link to more string data if it is needed
  88. * For REF - this is the 'parent' that the firstChild is a member of
  89. */
  90. JsVarRef lastChild;
  91. } PACKED_FLAGS JsVar;
  92. /* We have a few different types:
  93. *
  94. * OBJECT/ARRAY - uses firstChild/lastChild to link to NAMEs.
  95. * BUILT-IN OBJECT - as above, but we use varData to store the name as well. This means built in object names must be LESS THAN 8 CHARACTERS
  96. * FUNCTION - uses firstChild/lastChild to link to NAMEs, and callback is used
  97. * NAME - use nextSibling/prevSibling linking to other NAMEs, and firstChild to link to a Variable of some kind
  98. * STRING - use firstChild to link to other STRINGs if String value is too long
  99. * INT/DOUBLE - firstChild never used
  100. */
  101. static inline unsigned char jsvGetLocks(JsVar *v) { return (unsigned char)((v->flags>>JSV_LOCK_SHIFT) & JSV_LOCK_MAX); }
  102. // For debugging/testing ONLY - maximum # of vars we are allowed to use
  103. void jsvSetMaxVarsUsed(unsigned int size);
  104. // Init/kill vars as a whole
  105. void jsvInit();
  106. void jsvKill();
  107. void jsvSoftInit(); ///< called when loading from flash
  108. void jsvSoftKill(); ///< called when saving to flash
  109. JsVar *jsvFindOrCreateRoot(); ///< Find or create the ROOT variable item - used mainly if recovering from a saved state.
  110. unsigned int jsvGetMemoryUsage(); ///< Get number of memory records (JsVars) used
  111. unsigned int jsvGetMemoryTotal(); ///< Get total amount of memory records
  112. bool jsvIsMemoryFull(); ///< Get whether memory is full or not
  113. void jsvShowAllocated(); ///< Show what is still allocated, for debugging memory problems
  114. /// Try and allocate more memory - only works if RESIZABLE_JSVARS is defined
  115. void jsvSetMemoryTotal(unsigned int jsNewVarCount);
  116. // Note that jsvNew* don't REF a variable for you, but the do LOCK it
  117. JsVar *jsvNew(); ///< Create a new variable
  118. JsVar *jsvNewWithFlags(JsVarFlags flags);
  119. JsVar *jsvNewFromString(const char *str); ///< Create a new string
  120. JsVar *jsvNewStringOfLength(unsigned int byteLength); ///< Create a new string of the given length - full of 0s
  121. static inline JsVar *jsvNewFromEmptyString() { return jsvNewWithFlags(JSV_STRING); } ;///< Create a new empty string
  122. JsVar *jsvNewFromLexer(struct JsLex *lex, JslCharPos charFrom, JslCharPos charTo); // Create a new STRING from part of the lexer
  123. JsVar *jsvNewFromInteger(JsVarInt value);
  124. JsVar *jsvNewFromBool(bool value);
  125. JsVar *jsvNewFromFloat(JsVarFloat value);
  126. // Turns var into a Variable name that links to the given value... No locking so no need to unlock var
  127. JsVar *jsvMakeIntoVariableName(JsVar *var, JsVar *valueOrZero);
  128. JsVar *jsvNewFromPin(int pin);
  129. /// DO NOT CALL THIS DIRECTLY - this frees an unreffed/locked var
  130. void jsvFreePtr(JsVar *var);
  131. /// Get a reference from a var - SAFE for null vars
  132. JsVarRef jsvGetRef(JsVar *var);
  133. /// SCARY - only to be used for vital stuff like load/save
  134. JsVar *_jsvGetAddressOf(JsVarRef ref);
  135. /// Lock this reference and return a pointer - UNSAFE for null refs
  136. JsVar *jsvLock(JsVarRef ref);
  137. /// Lock this pointer and return a pointer - UNSAFE for null pointer
  138. JsVar *jsvLockAgain(JsVar *var);
  139. /// Unlock this variable - this is SAFE for null variables
  140. void jsvUnLock(JsVar *var);
  141. /// Reference - set this variable as used by something
  142. JsVar *jsvRef(JsVar *v);
  143. /// Unreference - set this variable as not used by anything
  144. void jsvUnRef(JsVar *var);
  145. /// Helper fn, Reference - set this variable as used by something
  146. JsVarRef jsvRefRef(JsVarRef ref);
  147. /// Helper fn, Unreference - set this variable as not used by anything
  148. JsVarRef jsvUnRefRef(JsVarRef ref);
  149. static inline bool jsvIsRoot(const JsVar *v) { return v && (v->flags&JSV_VARTYPEMASK)==JSV_ROOT; }
  150. static inline bool jsvIsPin(const JsVar *v) { return v && (v->flags&JSV_VARTYPEMASK)==JSV_PIN; }
  151. static inline bool jsvIsInt(const JsVar *v) { return v && ((v->flags&JSV_VARTYPEMASK)==JSV_INTEGER || (v->flags&JSV_VARTYPEMASK)==JSV_PIN); }
  152. static inline bool jsvIsFloat(const JsVar *v) { return v && (v->flags&JSV_VARTYPEMASK)==JSV_FLOAT; }
  153. static inline bool jsvIsBoolean(const JsVar *v) { return v && (v->flags&JSV_VARTYPEMASK)==JSV_BOOLEAN; }
  154. static inline bool jsvIsString(const JsVar *v) { return v && (v->flags&JSV_VARTYPEMASK)>=JSV_STRING_0 && (v->flags&JSV_VARTYPEMASK)<=JSV_STRING_MAX; }
  155. static inline bool jsvIsStringExt(const JsVar *v) { return v && (v->flags&JSV_VARTYPEMASK)>=JSV_STRING_EXT_0 && (v->flags&JSV_VARTYPEMASK)<=JSV_STRING_EXT_MAX; } ///< The extra bits dumped onto the end of a string to store more data
  156. static inline bool jsvIsNumeric(const JsVar *v) { return v && (v->flags&JSV_VARTYPEMASK)>=JSV_NUMERICSTART && (v->flags&JSV_VARTYPEMASK)<=JSV_NUMERICEND; }
  157. static inline bool jsvIsFunction(const JsVar *v) { return v && (v->flags&JSV_VARTYPEMASK)==JSV_FUNCTION; }
  158. static inline bool jsvIsFunctionParameter(const JsVar *v) { return v && (v->flags&JSV_FUNCTION_PARAMETER) == JSV_FUNCTION_PARAMETER; }
  159. static inline bool jsvIsObject(const JsVar *v) { return v && (((v->flags&JSV_VARTYPEMASK)==JSV_OBJECT) || ((v->flags&JSV_VARTYPEMASK)==JSV_ROOT)); }
  160. static inline bool jsvIsArray(const JsVar *v) { return v && (v->flags&JSV_VARTYPEMASK)==JSV_ARRAY; }
  161. static inline bool jsvIsArrayBuffer(const JsVar *v) { return v && (v->flags&JSV_VARTYPEMASK)==JSV_ARRAYBUFFER; }
  162. static inline bool jsvIsArrayBufferName(const JsVar *v) { return v && (v->flags&(JSV_VARTYPEMASK|JSV_NAME))==JSV_ARRAYBUFFERNAME; }
  163. static inline bool jsvIsNative(const JsVar *v) { return v && (v->flags&JSV_NATIVE)!=0; }
  164. static inline bool jsvIsUndefined(const JsVar *v) { return v==0; }
  165. static inline bool jsvIsNull(const JsVar *v) { return v && (v->flags&JSV_VARTYPEMASK)==JSV_NULL; }
  166. static inline bool jsvIsBasic(const JsVar *v) { return jsvIsNumeric(v) || jsvIsString(v);} ///< Is this *not* an array/object/etc
  167. static inline bool jsvIsName(const JsVar *v) { return v && (v->flags & JSV_NAME)!=0; } ///< NAMEs are what's used to name a variable (it is not the data itself)
  168. static inline bool jsvIsIterable(const JsVar *v) {
  169. return jsvIsArray(v) || jsvIsObject(v) || jsvIsFunction(v) ||
  170. jsvIsString(v) || jsvIsArrayBuffer(v);
  171. }
  172. /** Does this string contain only Numeric characters? */
  173. bool jsvIsStringNumeric(const JsVar *var);
  174. /** Does this string contain only Numeric characters? This is for arrays
  175. * and makes the assertion that int_to_string(string_to_int(var))==var */
  176. bool jsvIsStringNumericStrict(const JsVar *var);
  177. // TODO: maybe isName shouldn't include ArrayBufferName?
  178. bool jsvHasCharacterData(const JsVar *v); ///< does the v->data union contain character data?
  179. bool jsvHasStringExt(const JsVar *v);
  180. /// Does this variable use firstChild/lastChild to point to multiple childrem
  181. bool jsvHasChildren(const JsVar *v);
  182. /// Is this variable a type that uses firstChild to point to a single Variable (ie. it doesn't have multiple children)
  183. bool jsvHasSingleChild(const JsVar *v);
  184. /// Does this variable have a 'ref' argument? Stringexts use it for extra character data
  185. static inline bool jsvHasRef(const JsVar *v) { return !jsvIsStringExt(v); }
  186. /// This is the number of characters a JsVar can contain, NOT string length
  187. static inline size_t jsvGetMaxCharactersInVar(const JsVar *v) {
  188. // see jsvCopy - we need to know about this in there too
  189. if (jsvIsStringExt(v)) return JSVAR_DATA_STRING_MAX_LEN;
  190. assert(jsvHasCharacterData(v));
  191. return JSVAR_DATA_STRING_LEN;
  192. }
  193. /// This is the number of characters a JsVar can contain, NOT string length
  194. static inline size_t jsvGetCharactersInVar(const JsVar *v) {
  195. assert(jsvIsString(v) || jsvIsStringExt(v));
  196. int f = v->flags&JSV_VARTYPEMASK;
  197. return (size_t)(f - ((f < JSV_STRING_EXT_0) ? JSV_STRING_0 : JSV_STRING_EXT_0));
  198. }
  199. /// This is the number of characters a JsVar can contain, NOT string length
  200. static inline void jsvSetCharactersInVar(JsVar *v, size_t chars) {
  201. assert(jsvIsString(v) || jsvIsStringExt(v));
  202. if (jsvIsString(v)) { assert(chars <= JSVAR_DATA_STRING_LEN); }
  203. if (jsvIsStringExt(v)) { assert(chars <= JSVAR_DATA_STRING_MAX_LEN); }
  204. int f = v->flags&JSV_VARTYPEMASK;
  205. v->flags = (JsVarFlags)((v->flags&(~JSV_VARTYPEMASK)) | (((f < JSV_STRING_EXT_0) ? JSV_STRING_0 : JSV_STRING_EXT_0) + (int)chars));
  206. }
  207. /** Check if two Basic Variables are equal (this IGNORES the value that is pointed to,
  208. * so 'a=5'=='a=7' but 'a=5'!='b=5')
  209. */
  210. bool jsvIsBasicVarEqual(JsVar *a, JsVar *b);
  211. /** Check if two things are equal. Basic vars are done by value,
  212. * for anything else the reference/pointer must be equal */
  213. bool jsvIsEqual(JsVar *a, JsVar *b);
  214. const char *jsvGetConstString(const JsVar *v); ///< Get a const string representing this variable - if we can. Otherwise return 0
  215. const char *jsvGetTypeOf(const JsVar *v); ///< Return the 'type' of the JS variable (eg. JS's typeof operator)
  216. size_t jsvGetString(const JsVar *v, char *str, size_t len); ///< Save this var as a string to the given buffer, and return how long it was (return val doesn't include terminating 0)
  217. void jsvSetString(JsVar *v, char *str, size_t len); ///< Set the Data in this string. This must JUST overwrite - not extend or shrink
  218. JsVar *jsvAsString(JsVar *var, bool unlockVar); ///< If var is a string, lock and return it, else create a new string
  219. size_t jsvGetStringLength(JsVar *v); ///< Get the length of this string, IF it is a string
  220. int jsvGetLinesInString(JsVar *v); ///< IN A STRING get the number of lines in the string (min=1)
  221. int jsvGetCharsOnLine(JsVar *v, int line); ///< IN A STRING Get the number of characters on a line - lines start at 1
  222. void jsvGetLineAndCol(JsVar *v, int charIdx, int* line, int *col); ///< IN A STRING, get the line and column of the given character. Both values must be non-null
  223. int jsvGetIndexFromLineAndCol(JsVar *v, int line, int col); ///< IN A STRING, get a character index from a line and column
  224. bool jsvIsStringEqual(JsVar *var, const char *str);
  225. int jsvCompareString(JsVar *va, JsVar *vb, int starta, int startb, bool equalAtEndOfString); ///< Compare 2 strings, starting from the given character positions
  226. int jsvCompareInteger(JsVar *va, JsVar *vb); ///< Compare 2 integers, >0 if va>vb, <0 if va<vb. If compared with a non-integer, that gets put later
  227. void jsvAppendString(JsVar *var, const char *str); ///< Append the given string to this one
  228. void jsvAppendStringBuf(JsVar *var, const char *str, int length); ///< Append the given string to this one - but does not use null-terminated strings
  229. void jsvAppendPrintf(JsVar *var, const char *fmt, ...); ///< Append the formatted string to a variable (see vcbprintf)
  230. static inline void jsvAppendCharacter(JsVar *var, char ch) { jsvAppendStringBuf(var, &ch, 1); }; ///< Append the given character to this string
  231. #define JSVAPPENDSTRINGVAR_MAXLENGTH (0x7FFFFFFF)
  232. void jsvAppendStringVar(JsVar *var, const JsVar *str, int stridx, int maxLength); ///< Append str to var. Both must be strings. stridx = start char or str, maxLength = max number of characters (can be JSVAPPENDSTRINGVAR_MAXLENGTH). stridx can be negative to go from end of string
  233. void jsvAppendStringVarComplete(JsVar *var, const JsVar *str); ///< Append all of str to var. Both must be strings.
  234. char jsvGetCharInString(JsVar *v, int idx);
  235. JsVarInt jsvGetInteger(const JsVar *v);
  236. void jsvSetInteger(JsVar *v, JsVarInt value); ///< Set an integer value (use carefully!)
  237. JsVarFloat jsvGetFloat(const JsVar *v); ///< Get the floating point representation of this var
  238. bool jsvGetBool(const JsVar *v);
  239. JsVar *jsvAsNumber(JsVar *var); ///< Convert the given variable to a number
  240. static inline JsVarInt _jsvGetIntegerAndUnLock(JsVar *v) { JsVarInt i = jsvGetInteger(v); jsvUnLock(v); return i; }
  241. static inline JsVarFloat _jsvGetFloatAndUnLock(JsVar *v) { JsVarFloat f = jsvGetFloat(v); jsvUnLock(v); return f; }
  242. static inline bool _jsvGetBoolAndUnLock(JsVar *v) { bool b = jsvGetBool(v); jsvUnLock(v); return b; }
  243. #ifdef SAVE_ON_FLASH
  244. JsVarInt jsvGetIntegerAndUnLock(JsVar *v);
  245. JsVarFloat jsvGetFloatAndUnLock(JsVar *v);
  246. bool jsvGetBoolAndUnLock(JsVar *v);
  247. #else
  248. #define jsvGetIntegerAndUnLock _jsvGetIntegerAndUnLock
  249. #define jsvGetFloatAndUnLock _jsvGetFloatAndUnLock
  250. #define jsvGetBoolAndUnLock _jsvGetBoolAndUnLock
  251. #endif
  252. /** Get the item at the given location in the array buffer and return the result */
  253. size_t jsvGetArrayBufferLength(JsVar *arrayBuffer);
  254. /** Get the item at the given location in the array buffer and return the result */
  255. JsVar *jsvArrayBufferGet(JsVar *arrayBuffer, JsVarInt index);
  256. /** Set the item at the given location in the array buffer */
  257. void jsvArrayBufferSet(JsVar *arrayBuffer, JsVarInt index, JsVar *value);
  258. /** Given an integer name that points to an arraybuffer or an arraybufferview, evaluate it and return the result */
  259. JsVar *jsvArrayBufferGetFromName(JsVar *name);
  260. /** If a is a name skip it and go to what it points to - and so on.
  261. * ALWAYS locks - so must unlock what it returns. It MAY
  262. * return 0. */
  263. JsVar *jsvSkipName(JsVar *a);
  264. /** If a is a name skip it and go to what it points to.
  265. * ALWAYS locks - so must unlock what it returns. It MAY
  266. * return 0. */
  267. JsVar *jsvSkipOneName(JsVar *a);
  268. /** If a is a's child is a name skip it and go to what it points to.
  269. * ALWAYS locks - so must unlock what it returns. */
  270. JsVar *jsvSkipToLastName(JsVar *a);
  271. /** Same as jsvSkipName, but ensures that 'a' is unlocked if it was
  272. * a name, so it can be used INLINE_FUNC */
  273. static inline JsVar *jsvSkipNameAndUnLock(JsVar *a) {
  274. JsVar *b = jsvSkipName(a);
  275. jsvUnLock(a);
  276. return b;
  277. }
  278. /** Same as jsvSkipOneName, but ensures that 'a' is unlocked if it was
  279. * a name, so it can be used INLINE_FUNC */
  280. static inline JsVar *jsvSkipOneNameAndUnLock(JsVar *a) {
  281. JsVar *b = jsvSkipOneName(a);
  282. jsvUnLock(a);
  283. return b;
  284. }
  285. /// MATHS!
  286. JsVar *jsvMathsOpSkipNames(JsVar *a, JsVar *b, int op);
  287. JsVar *jsvMathsOp(JsVar *a, JsVar *b, int op);
  288. /// Negates an integer/double value
  289. JsVar *jsvNegateAndUnLock(JsVar *v);
  290. /// Copy this variable and return the locked copy
  291. JsVar *jsvCopy(JsVar *src);
  292. /** Copy only a name, not what it points to. ALTHOUGH the link to what it points to is maintained unless linkChildren=false.
  293. If keepAsName==false, this will be converted into a normal variable */
  294. JsVar *jsvCopyNameOnly(JsVar *src, bool linkChildren, bool keepAsName);
  295. /// Tree related stuff
  296. void jsvAddName(JsVar *parent, JsVar *nameChild); // Add a child, which is itself a name
  297. JsVar *jsvAddNamedChild(JsVar *parent, JsVar *child, const char *name); // Add a child, and create a name for it. Returns a LOCKED var. DOES NOT CHECK FOR DUPLICATES
  298. JsVar *jsvSetNamedChild(JsVar *parent, JsVar *child, const char *name); // Add a child, and create a name for it. Returns a LOCKED name var. CHECKS FOR DUPLICATES
  299. JsVar *jsvSetValueOfName(JsVar *name, JsVar *src); // Set the value of a child created with jsvAddName,jsvAddNamedChild. Returns the UNLOCKED name argument
  300. JsVar *jsvFindChildFromString(JsVar *parent, const char *name, bool createIfNotFound); // Non-recursive finding of child with name. Returns a LOCKED var
  301. JsVar *jsvFindChildFromVar(JsVar *parent, JsVar *childName, bool addIfNotFound); // Non-recursive finding of child with name. Returns a LOCKED var
  302. static inline JsVar *jsvFindChildFromStringRef(JsVarRef parentref, const char *name, bool addIfNotFound) { // Non-recursive finding of child with name. Returns a LOCKED var
  303. JsVar *p = jsvLock(parentref);
  304. JsVar *v = jsvFindChildFromString(p, name, addIfNotFound);
  305. jsvUnLock(p);
  306. return v;
  307. }
  308. static inline JsVar *jsvFindChildFromVarRef(JsVarRef parentref, JsVar *childName, bool addIfNotFound) { // Non-recursive finding of child with name. Returns a LOCKED var
  309. JsVar *p = jsvLock(parentref);
  310. JsVar *v = jsvFindChildFromVar(p, childName, addIfNotFound);
  311. jsvUnLock(p);
  312. return v;
  313. }
  314. /// Remove a child - note that the child MUST ACTUALLY BE A CHILD!
  315. void jsvRemoveChild(JsVar *parent, JsVar *child);
  316. void jsvRemoveAllChildren(JsVar *parent);
  317. static inline void jsvRemoveNamedChild(JsVar *parent, const char *name) {
  318. JsVar *child = jsvFindChildFromString(parent, name, false);
  319. if (child) {
  320. jsvRemoveChild(parent, child);
  321. jsvUnLock(child);
  322. }
  323. }
  324. /// Get the named child of an object. If createChild!=0 then create the child
  325. JsVar *jsvObjectGetChild(JsVar *obj, const char *name, JsVarFlags createChild);
  326. /// Set the named child of an object, and return the child (so you can choose to unlock it if you want)
  327. JsVar *jsvObjectSetChild(JsVar *obj, const char *name, JsVar *child);
  328. int jsvGetChildren(JsVar *v); ///< number of children of a variable. also see jsvGetArrayLength and jsvGetLength
  329. JsVarInt jsvGetArrayLength(JsVar *arr); ///< Not the same as GetChildren, as it can be a sparse array
  330. JsVarInt jsvGetLength(JsVar *src); ///< General purpose length function. Does the 'right' thing
  331. size_t jsvCountJsVarsUsed(JsVar *v); ///< Count the amount of JsVars used. Mostly useful for debugging
  332. JsVar *jsvGetArrayItem(JsVar *arr, int index); ///< Get an item at the specified index in the array (and lock it)
  333. JsVar *jsvGetArrayIndexOf(JsVar *arr, JsVar *value, bool matchExact); ///< Get the index of the value in the array (matchExact==use pointer, not equality check)
  334. JsVarInt jsvArrayPushWithInitialSize(JsVar *arr, JsVar *value, JsVarInt initialValue); ///< 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.
  335. JsVarInt jsvArrayPush(JsVar *arr, JsVar *value); ///< Adds a new element to the end of an array, and returns the new length
  336. JsVarInt jsvArrayPushAndUnLock(JsVar *arr, JsVar *value); ///< Adds a new element to the end of an array, unlocks it, and returns the new length
  337. JsVar *jsvArrayPop(JsVar *arr); ///< Removes the last element of an array, and returns that element (or 0 if empty). includes the NAME
  338. JsVar *jsvArrayPopFirst(JsVar *arr); ///< Removes the first element of an array, and returns that element (or 0 if empty) includes the NAME
  339. JsVar *jsvArrayGetLast(JsVar *arr); ///< Get the last element of an array (does not remove, unlike jsvArrayPop), and returns that element (or 0 if empty) includes the NAME
  340. JsVar *jsvArrayJoin(JsVar *arr, JsVar *filler); ///< Join all elements of an array together into a string
  341. void jsvArrayInsertBefore(JsVar *arr, JsVar *beforeIndex, JsVar *element); ///< Insert a new element before beforeIndex, DOES NOT UPDATE INDICES
  342. static inline bool jsvArrayIsEmpty(JsVar *arr) { assert(jsvIsArray(arr)); return !arr->firstChild; } ///< Return true is array is empty
  343. /** Write debug info for this Var out to the console */
  344. void jsvTrace(JsVarRef ref, int indent);
  345. /** Run a garbage collection sweep - return true if things have been freed */
  346. bool jsvGarbageCollect();
  347. /** Remove whitespace to the right of a string - on MULTIPLE LINES */
  348. JsVar *jsvStringTrimRight(JsVar *srcString);
  349. /** If v is the key of a function, return true if it is internal and shouldn't be visible to the user */
  350. bool jsvIsInternalFunctionKey(JsVar *v);
  351. /// If v is the key of an object, return true if it is internal and shouldn't be visible to the user
  352. bool jsvIsInternalObjectKey(JsVar *v);
  353. // --------------------------------------------------------------------------------------------
  354. typedef struct JsvStringIterator {
  355. size_t charIdx; ///< index of character in var
  356. size_t charsInVar; ///< total characters in var
  357. size_t index; ///< index in string
  358. JsVar *var; ///< current StringExt we're looking at
  359. } JsvStringIterator;
  360. // slight hack to enure we can use string iterator with const JsVars
  361. #define jsvStringIteratorNewConst(it,str,startIdx) jsvStringIteratorNew(it,(JsVar*)str,startIdx)
  362. void jsvStringIteratorNew(JsvStringIterator *it, JsVar *str, int startIdx);
  363. /// Gets the current character (or 0)
  364. static inline char jsvStringIteratorGetChar(JsvStringIterator *it) {
  365. if (!it->var) return 0;
  366. return it->var->varData.str[it->charIdx];
  367. }
  368. /// Gets the current (>=0) character (or -1)
  369. static inline int jsvStringIteratorGetCharOrMinusOne(JsvStringIterator *it) {
  370. if (!it->var) return -1;
  371. return (int)(unsigned char)it->var->varData.str[it->charIdx];
  372. }
  373. /// Do we have a character, or are we at the end?
  374. static inline bool jsvStringIteratorHasChar(JsvStringIterator *it) {
  375. return it->charIdx < it->charsInVar;
  376. }
  377. /// Sets a character (will not extend the string - just overwrites)
  378. static inline void jsvStringIteratorSetChar(JsvStringIterator *it, char c) {
  379. if (jsvStringIteratorHasChar(it))
  380. it->var->varData.str[it->charIdx] = c;
  381. }
  382. /// Gets the current index in the string
  383. static inline size_t jsvStringIteratorGetIndex(JsvStringIterator *it) {
  384. return it->index;
  385. }
  386. /// Move to next character
  387. void jsvStringIteratorNext(JsvStringIterator *it);
  388. /// Move to next character (this one is inlined where speed is needed)
  389. static inline void jsvStringIteratorNextInline(JsvStringIterator *it) {
  390. it->charIdx++;
  391. it->index++;
  392. if (it->charIdx >= it->charsInVar) {
  393. it->charIdx -= it->charsInVar;
  394. if (it->var && it->var->lastChild) {
  395. JsVar *next = jsvLock(it->var->lastChild);
  396. jsvUnLock(it->var);
  397. it->var = next;
  398. it->charsInVar = jsvGetCharactersInVar(it->var);
  399. } else {
  400. jsvUnLock(it->var);
  401. it->var = 0;
  402. it->charsInVar = 0;
  403. }
  404. }
  405. }
  406. /// Go to the end of the string iterator - for use with jsvStringIteratorAppend
  407. void jsvStringIteratorGotoEnd(JsvStringIterator *it);
  408. /// Append a character TO THE END of a string iterator
  409. void jsvStringIteratorAppend(JsvStringIterator *it, char ch);
  410. static inline void jsvStringIteratorFree(JsvStringIterator *it) {
  411. jsvUnLock(it->var);
  412. }
  413. // --------------------------------------------------------------------------------------------
  414. typedef struct JsArrayIterator {
  415. JsVar *var;
  416. } JsArrayIterator;
  417. static inline void jsvArrayIteratorNew(JsArrayIterator *it, JsVar *arr) {
  418. assert(jsvIsArray(arr));
  419. it->var = arr->firstChild ? jsvLock(arr->firstChild) : 0;
  420. }
  421. /// Gets the current array element (or 0)
  422. static inline JsVar *jsvArrayIteratorGetElement(JsArrayIterator *it) {
  423. if (!it->var) return 0; // end of array
  424. return it->var->firstChild ? jsvLock(it->var->firstChild) : 0; // might even be undefined
  425. }
  426. /// Gets the current array element index (or 0)
  427. static inline JsVar *jsvArrayIteratorGetIndex(JsArrayIterator *it) {
  428. if (!it->var) return 0;
  429. return jsvLockAgain(it->var);
  430. }
  431. /// Do we have a character, or are we at the end?
  432. static inline bool jsvArrayIteratorHasElement(JsArrayIterator *it) {
  433. return it->var != 0;
  434. }
  435. /// Move to next character
  436. static inline void jsvArrayIteratorNext(JsArrayIterator *it) {
  437. if (it->var) {
  438. JsVarRef next = it->var->nextSibling;
  439. jsvUnLock(it->var);
  440. it->var = next ? jsvLock(next) : 0;
  441. }
  442. }
  443. static inline void jsvArrayIteratorFree(JsArrayIterator *it) {
  444. jsvUnLock(it->var);
  445. }
  446. // --------------------------------------------------------------------------------------------
  447. typedef struct JsObjectIterator {
  448. JsVar *var;
  449. } JsObjectIterator;
  450. static inline void jsvObjectIteratorNew(JsObjectIterator *it, JsVar *obj) {
  451. assert(jsvIsObject(obj) || jsvIsFunction(obj));
  452. it->var = obj->firstChild ? jsvLock(obj->firstChild) : 0;
  453. }
  454. /// Gets the current object element key (or 0)
  455. static inline JsVar *jsvObjectIteratorGetKey(JsObjectIterator *it) {
  456. if (!it->var) return 0; // end of object
  457. return jsvLockAgain(it->var);
  458. }
  459. /// Gets the current object element value (or 0)
  460. static inline JsVar *jsvObjectIteratorGetValue(JsObjectIterator *it) {
  461. if (!it->var) return 0; // end of object
  462. return it->var->firstChild ? jsvLock(it->var->firstChild) : 0; // might even be undefined
  463. }
  464. /// Do we have a key, or are we at the end?
  465. static inline bool jsvObjectIteratorHasElement(JsObjectIterator *it) {
  466. return it->var != 0;
  467. }
  468. /// Move to next character
  469. static inline void jsvObjectIteratorNext(JsObjectIterator *it) {
  470. if (it->var) {
  471. JsVarRef next = it->var->nextSibling;
  472. jsvUnLock(it->var);
  473. it->var = next ? jsvLock(next) : 0;
  474. }
  475. }
  476. static inline void jsvObjectIteratorFree(JsObjectIterator *it) {
  477. jsvUnLock(it->var);
  478. }
  479. // --------------------------------------------------------------------------------------------
  480. typedef struct JsvArrayBufferIterator {
  481. JsvStringIterator it;
  482. JsVarDataArrayBufferViewType type;
  483. JsVarInt byteLength;
  484. JsVarInt byteOffset;
  485. JsVarInt index;
  486. bool hasAccessedElement;
  487. } JsvArrayBufferIterator;
  488. void jsvArrayBufferIteratorNew(JsvArrayBufferIterator *it, JsVar *arrayBuffer, JsVarInt index);
  489. JsVar *jsvArrayBufferIteratorGetValue(JsvArrayBufferIterator *it);
  490. JsVarInt jsvArrayBufferIteratorGetIntegerValue(JsvArrayBufferIterator *it);
  491. JsVarFloat jsvArrayBufferIteratorGetFloatValue(JsvArrayBufferIterator *it);
  492. void jsvArrayBufferIteratorSetValue(JsvArrayBufferIterator *it, JsVar *value);
  493. void jsvArrayBufferIteratorSetIntegerValue(JsvArrayBufferIterator *it, JsVarInt value);
  494. JsVar* jsvArrayBufferIteratorGetIndex(JsvArrayBufferIterator *it);
  495. bool jsvArrayBufferIteratorHasElement(JsvArrayBufferIterator *it);
  496. void jsvArrayBufferIteratorNext(JsvArrayBufferIterator *it);
  497. void jsvArrayBufferIteratorFree(JsvArrayBufferIterator *it);
  498. // --------------------------------------------------------------------------------------------
  499. union JsvIteratorUnion {
  500. JsvStringIterator str;
  501. JsObjectIterator obj;
  502. JsArrayIterator arr;
  503. JsvArrayBufferIterator buf;
  504. };
  505. /** General Purpose iterator, for Strings, Arrays, Objects, Typed Arrays */
  506. typedef struct JsvIterator {
  507. enum {JSVI_STRING, JSVI_ARRAY, JSVI_OBJECT, JSVI_ARRAYBUFFER } type;
  508. union JsvIteratorUnion it;
  509. } JsvIterator;
  510. void jsvIteratorNew(JsvIterator *it, JsVar *obj);
  511. JsVar *jsvIteratorGetKey(JsvIterator *it);
  512. JsVar *jsvIteratorGetValue(JsvIterator *it);
  513. JsVarInt jsvIteratorGetIntegerValue(JsvIterator *it);
  514. bool jsvIteratorHasElement(JsvIterator *it);
  515. void jsvIteratorNext(JsvIterator *it);
  516. void jsvIteratorFree(JsvIterator *it);
  517. #endif /* JSVAR_H_ */