var.h 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949
  1. /*
  2. AutoHotkey
  3. Copyright 2003-2009 Chris Mallett (support@autohotkey.com)
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License
  6. as published by the Free Software Foundation; either version 2
  7. of the License, or (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. */
  13. #ifndef var_h
  14. #define var_h
  15. #include "defines.h"
  16. #include "SimpleHeap.h"
  17. #include "clipboard.h"
  18. #include "util.h" // for strlcpy() & snprintf()
  19. EXTERN_CLIPBOARD;
  20. #define MAX_ALLOC_SIMPLE 64 // Do not decrease this much since it is used for the sizing of some built-in variables.
  21. #define SMALL_STRING_LENGTH (MAX_ALLOC_SIMPLE - 1) // The largest string that can fit in the above.
  22. #define DEREF_BUF_EXPAND_INCREMENT (16 * 1024) // Reduced from 32 to 16 in v1.0.46.07 to reduce the memory utilization of deeply recursive UDFs.
  23. enum AllocMethod {ALLOC_NONE, ALLOC_SIMPLE, ALLOC_MALLOC, ALLOC_DISABLED};
  24. enum VarTypes
  25. {
  26. // The following must all be LOW numbers to avoid any realistic chance of them matching the address of
  27. // any function (namely a BIV_* function).
  28. VAR_ALIAS // VAR_ALIAS must always have a non-NULL mAliasFor. In other ways it's the same as VAR_NORMAL. VAR_ALIAS is never seen because external users call Var::Type(), which automatically resolves ALIAS to some other type.
  29. , VAR_NORMAL // Most variables, such as those created by the user, are this type.
  30. , VAR_CONSTANT // or as I like to say, not variable.
  31. , VAR_VIRTUAL
  32. , VAR_VIRTUAL_OBJ
  33. // If adding to this enum, ensure range checks and VAR_LAST_TYPE remain valid.
  34. , VAR_LAST_TYPE = VAR_VIRTUAL_OBJ
  35. };
  36. // Returns true if the given var type should be evaluated by calling Var::Get(ResultToken&).
  37. inline bool VarTypeIsVirtual(int type) { return type >= VAR_VIRTUAL; }
  38. typedef UCHAR VarTypeType; // UCHAR vs. VarTypes to save memory.
  39. typedef UCHAR AllocMethodType; // UCHAR vs. AllocMethod to save memory.
  40. typedef UCHAR VarAttribType; // Same.
  41. typedef UINT_PTR VarSizeType; // jackieku(2009-10-23): Change this to UINT_PTR to ensure its size is the same with a pointer.
  42. #define VARSIZE_MAX ((VarSizeType) ~0)
  43. #define VARSIZE_ERROR VARSIZE_MAX
  44. class Var; // Forward declaration.
  45. // #pragma pack(4) not used here because although it would currently save 4 bytes per VarBkp struct (28 vs. 32),
  46. // it would probably reduce performance since VarBkp items are stored in contiguous array rather than a
  47. // linked list (which would cause every other struct in the array to have an 8-byte member than stretches
  48. // across two 8-byte regions in memory).
  49. struct VarBkp // This should be kept in sync with any changes to the Var class. See Var for comments.
  50. {
  51. union
  52. {
  53. __int64 mContentsInt64; // 64-bit members kept at the top of the struct to reduce the chance that they'll span 2 vs. 1 64-bit regions.
  54. double mContentsDouble;
  55. IObject *mObject;
  56. };
  57. Var *mVar; // Used to save the target var to which these backed up contents will later be restored.
  58. union
  59. {
  60. char *mByteContents;
  61. TCHAR *mCharContents;
  62. };
  63. union
  64. {
  65. VarSizeType mByteLength;
  66. Var *mAliasFor;
  67. };
  68. VarSizeType mByteCapacity;
  69. AllocMethodType mHowAllocated;
  70. VarAttribType mAttrib;
  71. VarTypeType mType;
  72. // Not needed in the backup:
  73. //bool mIsLocal;
  74. //TCHAR *mName;
  75. void ToToken(ExprTokenType &aValue);
  76. };
  77. #define BIV_DECL_R(name) void name(ResultToken &aResultToken, LPTSTR aVarName)
  78. #define BIV_DECL_W(name) void name(ResultToken &aResultToken, LPTSTR aVarName, ExprTokenType &aValue)
  79. #define BIV_DECL_RW(name) BIV_DECL_R(name); BIV_DECL_W(name##_Set)
  80. struct VirtualVar
  81. {
  82. typedef BIV_DECL_R((* Getter));
  83. typedef BIV_DECL_W((* Setter));
  84. Getter Get;
  85. Setter Set;
  86. };
  87. struct VarEntry
  88. {
  89. LPTSTR name;
  90. VirtualVar type;
  91. };
  92. class VarRef;
  93. #pragma warning(push)
  94. #pragma warning(disable: 4995 4996)
  95. // Concerning "#pragma pack" below:
  96. // Default pack would otherwise be 8, which would cause the 64-bit mContentsInt64 member to increase the size
  97. // of the struct from 20 to 32 (instead of 28). Benchmarking indicates that there's no significant performance
  98. // loss from doing this, perhaps because variables are currently stored in a linked list rather than an
  99. // array. (In an array, having the struct size be a multiple of 8 would prevent every other struct in the array
  100. // from having its 64-bit members span more than one 64-bit region in memory, which might reduce performance.)
  101. #ifdef _WIN64
  102. #pragma pack(push, 8)
  103. #else
  104. #pragma pack(push, 4) // 32-bit vs. 64-bit. See above.
  105. #endif
  106. class Var
  107. {
  108. private:
  109. // Keep VarBkp (above) in sync with any changes made to the members here.
  110. union // 64-bit members kept at the top of the struct to reduce the chance that they'll span 2 64-bit regions.
  111. {
  112. // Although the 8-byte members mContentsInt64 and mContentsDouble could be hung onto the struct
  113. // via a 4-byte-pointer, thus saving 4 bytes for each variable that never uses a binary number,
  114. // it doesn't seem worth it because the percentage of variables in typical scripts that will
  115. // acquire a cached binary number at some point seems likely to be high. A percentage of only
  116. // 50% would be enough to negate the savings because half the variables would consume 12 bytes
  117. // more than the version of AutoHotkey that has no binary-number caching, and the other half
  118. // would consume 4 more (due to the unused/empty pointer). That would be an average of 8 bytes
  119. // extra; i.e. exactly the same as the 8 bytes used by putting the numbers directly into the struct.
  120. // In addition, there are the following advantages:
  121. // 1) Code less complicated, more maintainable, faster.
  122. // 2) Caching of binary numbers works even in recursive script functions. By contrast, if the
  123. // binary number were allocated on demand, recursive functions couldn't use caching because the
  124. // memory from SimpleHeap could never be freed, thus producing a memory leak.
  125. // The main drawback is that some scripts are known to create a million variables or more, so the
  126. // extra 8 bytes per variable would increase memory load by 8+ MB (possibly with a boost in
  127. // performance if those variables are ever numeric).
  128. __int64 mContentsInt64;
  129. double mContentsDouble;
  130. IObject *mObject; // L31
  131. VirtualVar *mVV; // VAR_VIRTUAL
  132. };
  133. union
  134. {
  135. LPTSTR mCharContents = sEmptyString; // Invariant: Anyone setting mByteCapacity to 0 must also set mCharContents to the empty string.
  136. char *mByteContents;
  137. };
  138. union
  139. {
  140. Var *mAliasFor = nullptr; // The variable for which this variable is an alias.
  141. VarSizeType mByteLength; // How much is actually stored in it currently, excluding the zero terminator.
  142. };
  143. VarSizeType mByteCapacity = 0; // In bytes. Includes the space for the zero terminator.
  144. AllocMethodType mHowAllocated = ALLOC_NONE; // Keep adjacent/contiguous with the below to save memory.
  145. #define VAR_ATTRIB_CONTENTS_OUT_OF_DATE 0x01 // Combined with VAR_ATTRIB_IS_INT64/DOUBLE/OBJECT to indicate mContents is not current.
  146. #define VAR_ATTRIB_ALREADY_WARNED 0x01 // Combined with VAR_ATTRIB_UNINITIALIZED to limit VarUnset warnings to 1 MsgBox per var. See WarnUnassignedVar.
  147. #define VAR_ATTRIB_UNINITIALIZED 0x02 // Var requires initialization before use.
  148. #define VAR_ATTRIB_HAS_ASSIGNMENT 0x04 // Used during load time to detect vars that are not assigned anywhere.
  149. #define VAR_ATTRIB_NOT_NUMERIC 0x08 // A prior call to IsNumeric() determined the var's value is PURE_NOT_NUMERIC.
  150. #define VAR_ATTRIB_IS_INT64 0x10 // Var's proper value is in mContentsInt64.
  151. #define VAR_ATTRIB_IS_DOUBLE 0x20 // Var's proper value is in mContentsDouble.
  152. #define VAR_ATTRIB_IS_OBJECT 0x40 // Var's proper value is in mObject.
  153. #define VAR_ATTRIB_VIRTUAL_OPEN 0x80 // Virtual var is open for writing.
  154. #define VAR_ATTRIB_CACHE (VAR_ATTRIB_IS_INT64 | VAR_ATTRIB_IS_DOUBLE | VAR_ATTRIB_NOT_NUMERIC) // These three are mutually exclusive.
  155. #define VAR_ATTRIB_TYPES (VAR_ATTRIB_IS_INT64 | VAR_ATTRIB_IS_DOUBLE | VAR_ATTRIB_IS_OBJECT) // These are mutually exclusive (but NOT_NUMERIC may be combined with OBJECT).
  156. #define VAR_ATTRIB_OFTEN_REMOVED (VAR_ATTRIB_CACHE | VAR_ATTRIB_CONTENTS_OUT_OF_DATE | VAR_ATTRIB_UNINITIALIZED)
  157. VarAttribType mAttrib; // Bitwise combination of the above flags (but many of them may be mutually exclusive).
  158. #define VAR_GLOBAL 0x01
  159. #define VAR_LOCAL 0x02
  160. #define VAR_VARREF 0x04 // This is a VarRef (used to determine whether the ToReturnValue optimization is safe).
  161. #define VAR_DOWNVAR 0x08 // This var is captured by a nested function/closure (it's in Func::mDownVar).
  162. #define VAR_LOCAL_FUNCPARAM 0x10 // Indicates this local var is a function's parameter. VAR_LOCAL_DECLARED should also be set.
  163. #define VAR_LOCAL_STATIC 0x20 // Indicates this local var retains its value between function calls.
  164. #define VAR_DECLARED 0x40 // Indicates this var was declared somehow, not automatic.
  165. UCHAR mScope; // Bitwise combination of the above flags.
  166. VarTypeType mType; // Keep adjacent/contiguous with the above due to struct alignment, to save memory.
  167. // Performance: Rearranging mType and the other byte-sized members with respect to each other didn't seem
  168. // to help performance. However, changing VarTypeType from UCHAR to int did boost performance a few percent,
  169. // but even if it's not a fluke, it doesn't seem worth the increase in memory for scripts with many
  170. // thousands of variables.
  171. #ifdef CONFIG_DEBUGGER
  172. friend class Debugger;
  173. #endif
  174. // Caller has verified mType == VAR_VIRTUAL.
  175. bool HasSetter() { return mVV->Set; }
  176. // Caller has verified VarTypeIsVirtual(mType).
  177. ResultType AssignVirtual(ExprTokenType &aValue);
  178. // Unconditionally accepts new memory, bypassing the usual redirection to Assign() for VAR_VIRTUAL.
  179. void _AcceptNewMem(LPTSTR aNewMem, VarSizeType aLength);
  180. ResultType AssignBinaryNumber(__int64 aNumberAsInt64, VarAttribType aAttrib = VAR_ATTRIB_IS_INT64);
  181. void UpdateContents()
  182. {
  183. ASSERT(mType != VAR_ALIAS); // Caller has already resolved aliases.
  184. if (mAttrib & VAR_ATTRIB_CONTENTS_OUT_OF_DATE)
  185. {
  186. // THE FOLLOWING ISN'T NECESSARY BECAUSE THE ASSIGN() CALLS BELOW DO IT:
  187. //mAttrib &= ~VAR_ATTRIB_CONTENTS_OUT_OF_DATE;
  188. TCHAR value_string[MAX_NUMBER_SIZE];
  189. if (mAttrib & VAR_ATTRIB_IS_INT64)
  190. {
  191. Assign(ITOA64(mContentsInt64, value_string)); // Return value currently not checked for this or the below.
  192. mAttrib |= VAR_ATTRIB_IS_INT64; // Re-enable the cache because Assign() disables it (since all other callers want that).
  193. }
  194. else if (mAttrib & VAR_ATTRIB_IS_DOUBLE)
  195. {
  196. Assign(value_string, FTOA(mContentsDouble, value_string, _countof(value_string)));
  197. mAttrib |= VAR_ATTRIB_IS_DOUBLE; // Re-enable the cache because Assign() disables it (since all other callers want that).
  198. }
  199. //else nothing to update, which shouldn't happen in this block unless there's a flaw or bug somewhere.
  200. }
  201. }
  202. void _SetObject(IObject *aObject)
  203. {
  204. mObject = aObject;
  205. // Mark this variable to indicate it contains an object (objects are never considered numeric).
  206. mAttrib |= VAR_ATTRIB_IS_OBJECT | VAR_ATTRIB_NOT_NUMERIC;
  207. }
  208. VarSizeType _CharLength() { return mByteLength / sizeof(TCHAR); }
  209. VarSizeType _CharCapacity() { return mByteCapacity / sizeof(TCHAR); }
  210. public:
  211. // Testing shows that due to data alignment, keeping mType adjacent to the other less-than-4-size member
  212. // above it reduces size of each object by 4 bytes.
  213. TCHAR *mName; // The name of the var.
  214. // sEmptyString is a special *writable* memory area for empty variables (those with zero capacity).
  215. // Although making it writable does make buffer overflows difficult to detect and analyze (since they
  216. // tend to corrupt the program's static memory pool), the advantages in maintainability and robustness
  217. // seem to far outweigh that. For example, it avoids having to constantly think about whether
  218. // *Contents()='\0' is safe. The sheer number of places that's avoided is a great relief, and it also
  219. // cuts down on code size due to not having to always check Capacity() and/or create more functions to
  220. // protect from writing to read-only strings, which would hurt performance.
  221. // The biggest offender of buffer overflow in sEmptyString is DllCall, which happens most frequently
  222. // when a script forgets to call VarSetStrCapacity before passing a buffer to some function that writes a
  223. // string to it. There is now some code there that tries to detect when that happens.
  224. static TCHAR sEmptyString[1]; // See above.
  225. void Get(ResultToken &aResultToken);
  226. ResultType AssignHWND(HWND aWnd);
  227. ResultType Assign(Var &aVar);
  228. ResultType Assign(ExprTokenType &aToken);
  229. static void AssignVirtualObj(IObject *aObj, ExprTokenType &aValue, ResultToken &aResultToken);
  230. static ResultType GetClipboardAll(void **aData, size_t *aDataSize);
  231. static ResultType SetClipboardAll(void *aData, size_t aDataSize);
  232. // Assign(char *, ...) has been break into four methods below.
  233. // This should prevent some mistakes, as characters and bytes are not interchangeable in the Unicode build.
  234. // Callers must make sure which one is the right method to call.
  235. ResultType AssignString(LPCTSTR aBuf = NULL, VarSizeType aLength = VARSIZE_MAX, bool aExactSize = false);
  236. inline ResultType Assign(LPCTSTR aBuf, VarSizeType aLength = VARSIZE_MAX, bool aExactSize = false)
  237. {
  238. ASSERT(aBuf); // aBuf shouldn't be NULL, use SetCapacity([length in bytes]) or AssignString(NULL, [length in characters]) instead.
  239. return AssignString(aBuf, aLength, aExactSize);
  240. }
  241. inline ResultType Assign()
  242. {
  243. return AssignString();
  244. }
  245. ResultType SetCapacity(VarSizeType aByteLength, bool aExactSize = false)
  246. {
  247. #ifdef UNICODE
  248. return AssignString(NULL, (aByteLength >> 1) + (aByteLength & 1), aExactSize);
  249. #else
  250. return AssignString(NULL, aByteLength, aExactSize);
  251. #endif
  252. }
  253. ResultType AssignStringFromCodePage(LPCSTR aBuf, int aLength = -1, UINT aCodePage = CP_ACP);
  254. ResultType AssignStringToCodePage(LPCWSTR aBuf, int aLength = -1, UINT aCodePage = CP_ACP, DWORD aFlags = WC_NO_BEST_FIT_CHARS, char aDefChar = '?');
  255. inline ResultType AssignStringW(LPCWSTR aBuf, int aLength = -1)
  256. {
  257. #ifdef UNICODE
  258. // Pass aExactSize=true for consistency with AssignStringTo/FromCodePage/UTF8.
  259. return AssignString(aBuf, aLength, true);
  260. #else
  261. return AssignStringToCodePage(aBuf, aLength);
  262. #endif
  263. }
  264. inline ResultType Assign(DWORD aValueToAssign)
  265. {
  266. return AssignBinaryNumber(aValueToAssign, VAR_ATTRIB_CONTENTS_OUT_OF_DATE|VAR_ATTRIB_IS_INT64);
  267. }
  268. inline ResultType Assign(int aValueToAssign)
  269. {
  270. return AssignBinaryNumber(aValueToAssign, VAR_ATTRIB_CONTENTS_OUT_OF_DATE|VAR_ATTRIB_IS_INT64);
  271. }
  272. inline ResultType Assign(__int64 aValueToAssign)
  273. {
  274. return AssignBinaryNumber(aValueToAssign, VAR_ATTRIB_CONTENTS_OUT_OF_DATE|VAR_ATTRIB_IS_INT64);
  275. }
  276. inline ResultType Assign(VarSizeType aValueToAssign)
  277. {
  278. return AssignBinaryNumber(aValueToAssign, VAR_ATTRIB_CONTENTS_OUT_OF_DATE|VAR_ATTRIB_IS_INT64);
  279. }
  280. inline ResultType Assign(double aValueToAssign)
  281. {
  282. // The type-casting below interprets the contents of aValueToAssign as an __int64 without actually
  283. // converting from double to __int64. Although the generated code isn't measurably smaller, hopefully
  284. // the compiler resolves it into something that performs better than a memcpy into a temporary variable.
  285. // Benchmarks show that the performance is at most a few percent worse than having code similar to
  286. // AssignBinaryNumber() in here.
  287. return AssignBinaryNumber(*(__int64 *)&aValueToAssign, VAR_ATTRIB_CONTENTS_OUT_OF_DATE|VAR_ATTRIB_IS_DOUBLE);
  288. }
  289. ResultType AssignSkipAddRef(IObject *aValueToAssign);
  290. ResultType Assign(IObject *aValueToAssign)
  291. {
  292. aValueToAssign->AddRef(); // Must be done before Release() in case the only other reference to this object is already in var. Such a case seems too rare to be worth optimizing by returning early.
  293. return AssignSkipAddRef(aValueToAssign);
  294. }
  295. IObject *Object()
  296. {
  297. Var &var = *ResolveAlias();
  298. return var.mObject;
  299. }
  300. IObject *ToObject()
  301. {
  302. Var &var = *ResolveAlias();
  303. return var.IsObject() ? var.mObject : nullptr;
  304. }
  305. void ReleaseObject()
  306. // Caller has ensured that IsObject() == true, not just HasObject().
  307. {
  308. ASSERT(IsObject());
  309. // Remove the attributes applied by AssignSkipAddRef().
  310. mAttrib &= ~(VAR_ATTRIB_IS_OBJECT | VAR_ATTRIB_NOT_NUMERIC);
  311. // Mark the variable as uninitialized so that any attempt to access it from __Delete
  312. // causes an error (but our caller can assign a new value and remove the attribute).
  313. mAttrib |= VAR_ATTRIB_UNINITIALIZED;
  314. // MUST BE DONE AFTER THE ABOVE IN CASE IT TRIGGERS __Delete:
  315. // Release this variable's object. Setting mObject = NULL is not necessary
  316. // since the value of mObject, mContentsInt64 and mContentsDouble is never used
  317. // unless an attribute is present which indicates which one is valid.
  318. mObject->Release();
  319. }
  320. SymbolType IsNumeric()
  321. {
  322. Var &var = *ResolveAlias();
  323. switch (var.mAttrib & VAR_ATTRIB_CACHE) // This switch() method should squeeze a little more performance out of it compared to doing "&" for every attribute. Only works for attributes that are mutually-exclusive, which these are.
  324. {
  325. case VAR_ATTRIB_IS_INT64: return PURE_INTEGER;
  326. case VAR_ATTRIB_IS_DOUBLE: return PURE_FLOAT;
  327. case VAR_ATTRIB_NOT_NUMERIC: return PURE_NOT_NUMERIC;
  328. }
  329. // Since above didn't return, its numeric status isn't yet known, so determine it. For simplicity
  330. // (and because Length() doesn't support VAR_VIRTUAL), the following doesn't check MAX_NUMBER_LENGTH.
  331. // So any string of digits that is too long to be a legitimate number is still treated as a number
  332. // anyway (overflow). Most of our callers are expressions anyway, in which case any unquoted
  333. // series of digits would've been assigned as a pure number and handled above.
  334. // Below passes FALSE for aUpdateContents because we've already confirmed this var doesn't contain
  335. // a cached number (so can't need updating) and to suppress an "uninitialized variable" warning.
  336. // The majority of our callers will call ToInt64/Double() or Contents() after we return, which would
  337. // trigger a second warning if we didn't suppress ours and StdOut/OutputDebug warn mode is in effect.
  338. // IF-IS is the only caller that wouldn't cause a warning, but in that case ExpandArgs() would have
  339. // already caused one.
  340. SymbolType is_pure_numeric = ::IsNumeric(var.Contents(), true, false, true); // Contents() vs. mContents to support VAR_VIRTUAL lvalue in a pure expression such as "a_clipboard:=1,a_clipboard+=5"
  341. if (is_pure_numeric == PURE_NOT_NUMERIC && !VarTypeIsVirtual(var.mType))
  342. var.mAttrib |= VAR_ATTRIB_NOT_NUMERIC;
  343. return is_pure_numeric;
  344. }
  345. SymbolType IsPureNumeric()
  346. // Unlike IsNumeric(), this is purely based on whether a pure number was assigned to this variable.
  347. // Implicitly supports all types of variables, since these attributes are used only by VAR_NORMAL.
  348. {
  349. Var &var = *ResolveAlias();
  350. switch (var.mAttrib & VAR_ATTRIB_CACHE)
  351. {
  352. case VAR_ATTRIB_IS_INT64: return PURE_INTEGER;
  353. case VAR_ATTRIB_IS_DOUBLE: return PURE_FLOAT;
  354. }
  355. return PURE_NOT_NUMERIC;
  356. }
  357. int IsPureNumericOrObject()
  358. {
  359. Var &var = *ResolveAlias();
  360. return var.mAttrib & (VAR_ATTRIB_IS_INT64 | VAR_ATTRIB_IS_DOUBLE | VAR_ATTRIB_IS_OBJECT);
  361. }
  362. __int64 ToInt64()
  363. {
  364. Var &var = *ResolveAlias();
  365. if (var.mAttrib & VAR_ATTRIB_IS_INT64)
  366. return var.mContentsInt64;
  367. if (var.mAttrib & VAR_ATTRIB_IS_DOUBLE)
  368. // Since mContentsDouble is the true value of this var, cast it to __int64 rather than
  369. // calling ATOI64(Contents()), which might produce a different result in some cases.
  370. return (__int64)var.mContentsDouble;
  371. // Otherwise, this var does not contain a pure number.
  372. return ATOI64(var.Contents()); // Call Contents() vs. using mContents in case of VAR_VIRTUAL or VAR_ATTRIB_IS_DOUBLE, and also for maintainability.
  373. }
  374. double ToDouble()
  375. {
  376. Var &var = *ResolveAlias();
  377. if (var.mAttrib & VAR_ATTRIB_IS_DOUBLE)
  378. return var.mContentsDouble;
  379. if (var.mAttrib & VAR_ATTRIB_IS_INT64)
  380. return (double)var.mContentsInt64; // As expected, testing shows that casting an int64 to a double is at least 100 times faster than calling ATOF() on the text version of that integer.
  381. // Otherwise, this var does not contain a pure number.
  382. return ATOF(var.Contents()); // Call Contents() vs. using mContents in case of VAR_VIRTUAL, and also for maintainability and consistency with ToInt64().
  383. }
  384. ResultType ToDoubleOrInt64(ExprTokenType &aToken)
  385. // aToken.var is the same as the "this" var. Converts var into a number and stores it numerically in aToken.
  386. {
  387. Var &var = *ResolveAlias();
  388. switch (aToken.symbol = var.IsNumeric())
  389. {
  390. case PURE_INTEGER:
  391. aToken.value_int64 = var.ToInt64();
  392. break;
  393. case PURE_FLOAT:
  394. aToken.value_double = var.ToDouble();
  395. break;
  396. default: // Not a pure number.
  397. return FAIL;
  398. }
  399. return OK; // Since above didn't return, indicate success.
  400. }
  401. void ToTokenSkipAddRef(ExprTokenType &aToken)
  402. // See ToDoubleOrInt64 for comments.
  403. {
  404. Var &var = *ResolveAlias();
  405. switch (var.mAttrib & VAR_ATTRIB_TYPES)
  406. {
  407. case VAR_ATTRIB_IS_INT64:
  408. aToken.SetValue(var.mContentsInt64);
  409. return;
  410. case VAR_ATTRIB_IS_DOUBLE:
  411. aToken.SetValue(var.mContentsDouble);
  412. return;
  413. case VAR_ATTRIB_IS_OBJECT:
  414. aToken.SetValue(var.mObject);
  415. return;
  416. default:
  417. if (var.mAttrib & VAR_ATTRIB_UNINITIALIZED)
  418. // This is relied upon by callers of CallEnumerator to permit an unset output arg
  419. // to indicate a missing array item. It should not be translated to a parameter's
  420. // default value, because an unset var in that context would have raised an error.
  421. // Some other callers may use this in place of IsUninitialized().
  422. aToken.symbol = SYM_MISSING;
  423. else
  424. aToken.SetValue(var.Contents(), var.Length());
  425. }
  426. }
  427. void ToToken(ExprTokenType &aToken)
  428. {
  429. ToTokenSkipAddRef(aToken);
  430. if (aToken.symbol == SYM_OBJECT)
  431. aToken.object->AddRef();
  432. }
  433. // Not an enum so that it can be global more easily:
  434. #define VAR_NEVER_FREE 0
  435. #define VAR_ALWAYS_FREE 1
  436. #define VAR_FREE_IF_LARGE 2
  437. #define VAR_CLEAR_ALIASES 4
  438. #define VAR_REQUIRE_INIT 8
  439. void Free(int aWhenToFree = VAR_ALWAYS_FREE);
  440. ResultType Append(LPTSTR aStr, VarSizeType aLength);
  441. ResultType AppendIfRoom(LPTSTR aStr, VarSizeType aLength);
  442. void AcceptNewMem(LPTSTR aNewMem, VarSizeType aLength);
  443. void SetLengthFromContents();
  444. static ResultType BackupFunctionVars(UserFunc &aFunc, VarBkp *&aVarBackup, int &aVarBackupCount);
  445. void Backup(VarBkp &aVarBkp);
  446. void Restore(VarBkp &aVarBkp);
  447. static void FreeAndRestoreFunctionVars(UserFunc &aFunc, VarBkp *&aVarBackup, int &aVarBackupCount);
  448. #define DISPLAY_NO_ERROR 0 // Must be zero.
  449. #define DISPLAY_VAR_ERROR 1
  450. #define DISPLAY_FUNC_ERROR 2
  451. #define DISPLAY_CLASS_ERROR 3
  452. #define DISPLAY_GROUP_ERROR 4
  453. #define DISPLAY_METHOD_ERROR 5
  454. #define VALIDATENAME_SUBJECT_INDEX(n) (n-1)
  455. #define VALIDATENAME_SUBJECTS { _T("variable"), _T("function"), _T("class"), _T("group"), _T("method") }
  456. static ResultType ValidateName(LPCTSTR aName, int aDisplayError = DISPLAY_VAR_ERROR);
  457. LPTSTR ObjectToText(LPTSTR aName, LPTSTR aBuf, int aBufSize);
  458. LPTSTR ToText(LPTSTR aBuf, int aBufSize, bool aAppendNewline)
  459. // Caller must ensure that Type() == VAR_NORMAL.
  460. // aBufSize is an int so that any negative values passed in from caller are not lost.
  461. // Caller has ensured that aBuf isn't NULL.
  462. // Translates this var into its text equivalent, putting the result into aBuf and
  463. // returning the position in aBuf of its new string terminator.
  464. {
  465. Var &var = *ResolveAlias();
  466. // v1.0.44.14: Changed it so that ByRef/Aliases report their own name rather than the target's/caller's
  467. // (it seems more useful and intuitive).
  468. var.Contents(); // Update mContents and mLength for use below.
  469. LPTSTR aBuf_orig = aBuf;
  470. switch (var.IsPureNumericOrObject())
  471. {
  472. case VAR_ATTRIB_IS_INT64:
  473. case VAR_ATTRIB_IS_DOUBLE:
  474. // Dont display [len of cap] since it's not relevant for pure numbers.
  475. // This also makes it obvious that the var contains a pure number:
  476. aBuf += sntprintf(aBuf, aBufSize, _T("%s: %s"), mName, var.mCharContents);
  477. break;
  478. case VAR_ATTRIB_IS_OBJECT:
  479. if (var.mType == VAR_VIRTUAL_OBJ)
  480. {
  481. aBuf += sntprintf(aBuf, aBufSize, _T("%s[%s]: %s"), mName, mObject->Type(), var.mCharContents);
  482. break;
  483. }
  484. aBuf = var.ObjectToText(this->mName, aBuf, aBufSize);
  485. break;
  486. default:
  487. if (var.IsUninitialized())
  488. {
  489. aBuf += sntprintf(aBuf, aBufSize, _T("%s: unset"), mName);
  490. break;
  491. }
  492. aBuf += sntprintf(aBuf, BUF_SPACE_REMAINING, _T("%s[%Iu of %Iu]: %-1.60s%s"), mName // mName not var.mName (see comment above).
  493. , var._CharLength(), var._CharCapacity() ? (var._CharCapacity() - 1) : 0 // Use -1 since it makes more sense to exclude the terminator.
  494. , var.mCharContents, var._CharLength() > 60 ? _T("...") : _T(""));
  495. }
  496. if (aAppendNewline && BUF_SPACE_REMAINING >= 2)
  497. {
  498. *aBuf++ = '\r';
  499. *aBuf++ = '\n';
  500. *aBuf = '\0';
  501. }
  502. return aBuf;
  503. }
  504. VarTypeType Type()
  505. {
  506. Var &var = *ResolveAlias();
  507. return var.mType;
  508. }
  509. bool IsVirtual()
  510. {
  511. return VarTypeIsVirtual(Type());
  512. }
  513. bool IsAlias()
  514. {
  515. return mType == VAR_ALIAS;
  516. }
  517. bool IsDirectConstant() // Is a constant and not an alias (i.e. downvar/upvar) for a constant.
  518. {
  519. return mType == VAR_CONSTANT;
  520. }
  521. // Convert VAR_NORMAL to VAR_CONSTANT.
  522. void MakeReadOnly()
  523. {
  524. ASSERT(mType == VAR_NORMAL || mType == VAR_CONSTANT); // Should never be called on VAR_ALIAS or VAR_VIRTUAL.
  525. ASSERT(!(mAttrib & VAR_ATTRIB_UNINITIALIZED));
  526. mType = VAR_CONSTANT;
  527. }
  528. #define VAR_IS_READONLY(var) ((var).IsReadOnly()) // This used to rely on var.Type(), which is no longer enough.
  529. bool IsReadOnly()
  530. {
  531. auto &var = *ResolveAlias();
  532. return var.mType == VAR_CONSTANT || (var.mType == VAR_VIRTUAL && !var.HasSetter());
  533. }
  534. bool IsStatic()
  535. {
  536. return (mScope & VAR_LOCAL_STATIC);
  537. }
  538. bool IsLocal()
  539. {
  540. // Since callers want to know whether this variable is local, even if it's a local alias for a
  541. // global, don't use the method below:
  542. // return (mType == VAR_ALIAS) ? mAliasFor->mIsLocal : mIsLocal;
  543. return (mScope & VAR_LOCAL);
  544. }
  545. bool IsNonStaticLocal()
  546. {
  547. // Since callers want to know whether this variable is local, even if it's a local alias for a
  548. // global, don't resolve VAR_ALIAS.
  549. // Even a ByRef local is considered local here because callers are interested in whether this
  550. // variable can vary from call to call to the same function (and a ByRef can vary in what it
  551. // points to). Variables that vary can thus be altered by the backup/restore process.
  552. return (mScope & (VAR_LOCAL|VAR_LOCAL_STATIC)) == VAR_LOCAL;
  553. }
  554. bool IsFuncParam()
  555. {
  556. return (mScope & VAR_LOCAL_FUNCPARAM);
  557. }
  558. bool IsDeclared()
  559. // Returns true if this is a declared var, such as "local var", "static var" or a func param.
  560. {
  561. return (mScope & VAR_DECLARED);
  562. }
  563. UCHAR &Scope()
  564. {
  565. return mScope;
  566. }
  567. static LPCTSTR DeclarationType(int aDeclType)
  568. {
  569. if (aDeclType & VAR_LOCAL)
  570. {
  571. if (aDeclType & VAR_LOCAL_STATIC)
  572. return _T("static");
  573. if (aDeclType & VAR_LOCAL_FUNCPARAM)
  574. return _T("parameter");
  575. return _T("local");
  576. }
  577. return _T("global");
  578. }
  579. bool IsAssignedSomewhere()
  580. {
  581. //return mAttrib & VAR_ATTRIB_HAS_ASSIGNMENT;
  582. return (mAttrib & ~VAR_ATTRIB_ALREADY_WARNED) != VAR_ATTRIB_UNINITIALIZED;
  583. // When this function is called (at load time), any of the other attributes
  584. // would mean that this var has a value, which means that it doesn't require
  585. // an assignment. If it lacks all attributes, it's presumably a built-in var.
  586. }
  587. void MarkAssignedSomewhere()
  588. {
  589. mAttrib |= VAR_ATTRIB_HAS_ASSIGNMENT;
  590. if (mType == VAR_ALIAS)
  591. mAliasFor->MarkAssignedSomewhere();
  592. }
  593. bool HasAlreadyWarned()
  594. {
  595. return mAttrib & VAR_ATTRIB_ALREADY_WARNED;
  596. }
  597. void MarkAlreadyWarned()
  598. {
  599. mAttrib |= VAR_ATTRIB_ALREADY_WARNED;
  600. }
  601. bool IsObject() // L31: Indicates this var contains an object reference which must be released if the var is emptied.
  602. {
  603. return (mAttrib & VAR_ATTRIB_IS_OBJECT);
  604. }
  605. bool HasObject() // L31: Indicates this var's effective value is an object reference.
  606. {
  607. Var &var = *ResolveAlias();
  608. return var.IsObject();
  609. }
  610. VarSizeType ByteCapacity()
  611. // Capacity includes the zero terminator (though if capacity is zero, there will also be a zero terminator in mContents due to it being "").
  612. {
  613. Var &var = *ResolveAlias();
  614. return var.mByteCapacity;
  615. }
  616. VarSizeType CharCapacity()
  617. {
  618. return ByteCapacity() / sizeof(TCHAR);
  619. }
  620. UNICODE_CHECK VarSizeType Capacity()
  621. {
  622. return CharCapacity();
  623. }
  624. BOOL HasContents()
  625. // A fast alternative to Length() that avoids updating mContents.
  626. // Caller must ensure that Type() is VAR_NORMAL.
  627. {
  628. Var &var = *ResolveAlias();
  629. // mAttrib is checked because mByteLength isn't applicable when the var contains
  630. // a pure number or an object.
  631. return (var.mAttrib & VAR_ATTRIB_TYPES) ? TRUE : var.mByteLength != 0;
  632. }
  633. VarSizeType &ByteLength()
  634. // This should not be called to discover a non-NORMAL var's length (nor that of an environment variable)
  635. // because their lengths aren't knowable without calling Get().
  636. // Returns a reference so that caller can use this function as an lvalue.
  637. {
  638. Var &var = *ResolveAlias();
  639. // There's no apparent reason to avoid using mByteLength for VAR_VIRTUAL,
  640. // so it's used temporarily despite the comments below. Even if mByteLength
  641. // isn't always accurate, it's no less accurate than a static variable would be.
  642. //if (mType == VAR_NORMAL)
  643. {
  644. if (var.mAttrib & VAR_ATTRIB_CONTENTS_OUT_OF_DATE)
  645. var.UpdateContents(); // Update mContents (and indirectly, mByteLength).
  646. return var.mByteLength;
  647. }
  648. // Since the length of the clipboard isn't normally tracked, we just return a
  649. // temporary storage area for the caller to use. Note: This approach is probably
  650. // not thread-safe, but currently there's only one thread so it's not an issue.
  651. // For reserved vars do the same thing as above, but this function should never
  652. // be called for them:
  653. //static VarSizeType length; // Must be static so that caller can use its contents. See above.
  654. //return length;
  655. }
  656. VarSizeType SetCharLength(VarSizeType len)
  657. {
  658. ByteLength() = len * sizeof(TCHAR);
  659. return len;
  660. }
  661. VarSizeType CharLength()
  662. {
  663. return ByteLength() / sizeof(TCHAR);
  664. }
  665. UNICODE_CHECK VarSizeType Length()
  666. {
  667. return CharLength();
  668. }
  669. //BYTE *ByteContents(BOOL aAllowUpdate = TRUE)
  670. //{
  671. // return (BYTE *) CharContents(aAllowUpdate);
  672. //}
  673. TCHAR *Contents(BOOL aAllowUpdate = TRUE)
  674. // Callers should almost always pass TRUE for aAllowUpdate because any caller who wants to READ from
  675. // mContents would almost always want it up-to-date. Any caller who wants to WRITE to mContents would
  676. // would almost always have called Assign(NULL, ...) prior to calling Contents(), which would have
  677. // cleared the VAR_ATTRIB_CONTENTS_OUT_OF_DATE flag.
  678. // UPDATE: If the variable is known to already be "up to date", passing FALSE reduces code size,
  679. // as the compiler often inlines the function and is able to optimize out the aAllowUpdate branches
  680. // (although the final result can be larger if it allows the compiler to inline more functions).
  681. {
  682. if (mType == VAR_ALIAS)
  683. return mAliasFor->Contents(aAllowUpdate);
  684. if ((mAttrib & VAR_ATTRIB_CONTENTS_OUT_OF_DATE) && aAllowUpdate) // VAR_ATTRIB_CONTENTS_OUT_OF_DATE is checked here and in the function below, for performance.
  685. UpdateContents(); // This also clears the VAR_ATTRIB_CONTENTS_OUT_OF_DATE.
  686. if (VarTypeIsVirtual(mType) && !(mAttrib & VAR_ATTRIB_VIRTUAL_OPEN) && aAllowUpdate)
  687. {
  688. // This var isn't open for writing, so populate mCharContents with its current value.
  689. PopulateVirtualVar();
  690. // The following ensures the contents are updated each time Contents() is called:
  691. mAttrib &= ~VAR_ATTRIB_VIRTUAL_OPEN;
  692. }
  693. return mCharContents;
  694. }
  695. // Populate a virtual var with its current value, as a string.
  696. // Caller has verified this->IsVirtual().
  697. ResultType PopulateVirtualVar();
  698. void ConvertToNonAliasIfNecessary() // __forceinline because it's currently only called from one place.
  699. // When this function actually converts an alias into a normal variable, the variable's old
  700. // attributes (especially mContents and mCapacity) become dominant again. This prevents a memory
  701. // leak in a case where a UDF is defined to provide a default value for a ByRef parameter, and is
  702. // called both with and without that parameter.
  703. {
  704. mAliasFor = NULL; // This also sets its counterpart in the union (mLength) to zero, which is appropriate because mContents should have been set to blank by a previous call to Free().
  705. mType = VAR_NORMAL; // It might already be this type, so this is just in case it's VAR_ALIAS.
  706. }
  707. Var *GetAliasFor()
  708. {
  709. ASSERT(mType == VAR_ALIAS);
  710. return mAliasFor;
  711. }
  712. Var *ResolveAlias()
  713. {
  714. // Return target if it's an alias, or itself if not.
  715. return mType == VAR_ALIAS ? mAliasFor->ResolveAlias() : this;
  716. }
  717. // Makes this var an alias of aTargetVar, or aTargetVar's target if it's an alias.
  718. // Copies any internal mObject ref used for managing the lifetime of the alias.
  719. void UpdateAlias(Var *aTargetVar);
  720. void UpdateAlias(VarRef *aTargetVar);
  721. void UpdateVirtualObj(IObject *aTargetRef);
  722. // Unconditionally makes this var an alias of aTargetVar, without resolving aliases.
  723. // Caller must ensure aTargetVar != nullptr && aTargetVar != this.
  724. void SetAliasDirect(Var *aTargetVar)
  725. {
  726. mAliasFor = aTargetVar; // Should always be non-NULL due to various checks elsewhere.
  727. mType = VAR_ALIAS; // It might already be this type, so this is just in case it's VAR_NORMAL.
  728. }
  729. // Retrieves the IObject interface for managing this var's lifetime,
  730. // converting this var to an alias for a new freevar if needed.
  731. IObject *GetRef();
  732. ResultType MoveToNewFreeVar(Var &aOther);
  733. ResultType Close()
  734. {
  735. if (mType == VAR_ALIAS)
  736. return mAliasFor->Close();
  737. if (VarTypeIsVirtual(mType))
  738. {
  739. // Commit the value in our temporary buffer.
  740. auto result = AssignVirtual(ExprTokenType(mCharContents, CharLength()));
  741. Free(); // Free temporary memory and remove VAR_ATTRIB_VIRTUAL_OPEN.
  742. return result;
  743. }
  744. // VAR_ATTRIB_CONTENTS_OUT_OF_DATE is removed below for maintainability; it shouldn't be
  745. // necessary because any caller of Close() should have previously called something that
  746. // updates the flags, such as Contents().
  747. mAttrib &= ~VAR_ATTRIB_OFTEN_REMOVED;
  748. return OK; // In all other cases.
  749. }
  750. // Constructor:
  751. Var(LPTSTR aVarName, UCHAR aScope)
  752. // The caller must ensure that aVarName is non-null.
  753. : mScope(aScope)
  754. , mAttrib(VAR_ATTRIB_UNINITIALIZED) // Seems best not to init empty vars to VAR_ATTRIB_NOT_NUMERIC because it would reduce maintainability, plus finding out whether an empty var is numeric via IsNumeric() is a very fast operation.
  755. , mName(aVarName) // Caller gave us a pointer to dynamic memory for this.
  756. , mType(VAR_NORMAL)
  757. {
  758. }
  759. Var(LPTSTR aVarName, VarEntry *aBuiltIn, UCHAR aScope)
  760. // The caller must ensure that aVarName is non-null.
  761. : mScope(aScope)
  762. , mName(aVarName) // Caller gave us a pointer to dynamic memory for this.
  763. , mAttrib(0) // Any vars that aren't VAR_NORMAL are considered initialized, by definition.
  764. , mType(VAR_VIRTUAL)
  765. , mVV(&aBuiltIn->type)
  766. {
  767. }
  768. Var() : Var(_T(""), VAR_VARREF)
  769. {
  770. // Vars constructed this way are for temporary use, and therefore must have mHowAllocated set
  771. // as below to prevent the use of SimpleHeap::Malloc(). Otherwise, each Var could allocate
  772. // some memory which cannot be freed until the program exits.
  773. mHowAllocated = ALLOC_MALLOC;
  774. }
  775. Var(IObject *aRef)
  776. // The caller must ensure that aVarName is non-null.
  777. : mType(VAR_VIRTUAL_OBJ), mObject(aRef)
  778. , mScope(0), mName(_T("")), mAttrib(VAR_ATTRIB_IS_OBJECT)
  779. , mHowAllocated(ALLOC_DISABLED)
  780. {
  781. ASSERT(aRef);
  782. }
  783. void *operator new(size_t aBytes) {return SimpleHeap::Malloc(aBytes);}
  784. void *operator new(size_t aBytes, void *p) {return p;}
  785. void *operator new[](size_t aBytes) {return SimpleHeap::Malloc(aBytes);}
  786. void operator delete(void *aPtr) {}
  787. void operator delete(void *aPtr, void *) {}
  788. void operator delete[](void *aPtr) {}
  789. ResultType InitializeConstant();
  790. bool IsUninitializedNormalVar()
  791. {
  792. Var &var = *ResolveAlias();
  793. return var.mType == VAR_NORMAL && (var.mAttrib & VAR_ATTRIB_UNINITIALIZED);
  794. }
  795. bool IsUninitialized()
  796. {
  797. Var &var = *ResolveAlias();
  798. return var.mAttrib & VAR_ATTRIB_UNINITIALIZED;
  799. }
  800. void MarkInitialized()
  801. {
  802. Var &var = *ResolveAlias();
  803. var.mAttrib &= ~VAR_ATTRIB_UNINITIALIZED;
  804. }
  805. void MarkUninitialized()
  806. {
  807. Var& var = *ResolveAlias();
  808. var.mAttrib |= VAR_ATTRIB_UNINITIALIZED;
  809. }
  810. ResultType Uninitialize(int aWhenToFree = VAR_FREE_IF_LARGE)
  811. {
  812. if (IsVirtual())
  813. {
  814. ExprTokenType unset;
  815. unset.symbol = SYM_MISSING;
  816. return AssignVirtual(unset);
  817. }
  818. Free(aWhenToFree | VAR_REQUIRE_INIT);
  819. return OK;
  820. }
  821. }; // class Var
  822. #pragma pack(pop) // Calling pack with no arguments restores the default value (which is 8, but "the alignment of a member will be on a boundary that is either a multiple of n or a multiple of the size of the member, whichever is smaller.")
  823. #pragma warning(pop)
  824. class VarRef : public ObjectBase, public Var
  825. {
  826. public:
  827. VarRef() {}
  828. ~VarRef()
  829. {
  830. Free(VAR_ALWAYS_FREE | VAR_CLEAR_ALIASES | VAR_REQUIRE_INIT);
  831. }
  832. IObject_Type_Impl("VarRef");
  833. ::Object *Base() { return ::Object::sVarRefPrototype; }
  834. void *operator new(size_t aBytes) { return malloc(aBytes); } // Must override Var::new, which uses SimpleHeap::Malloc.
  835. void *operator new[](size_t aBytes) { return malloc(aBytes); }
  836. void operator delete(void *aPtr) { free(aPtr); }
  837. void operator delete[](void *aPtr) { free(aPtr); }
  838. void __Value(ResultToken &aResultToken, int aID, int aFlags, ExprTokenType *aParam[], int aParamCount);
  839. static ObjectMember sMembers[];
  840. };
  841. #endif