Guest User

mfc javascript challenge

a guest
Jun 10th, 2013
1,012
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 93.18 KB | None | 0 0
  1. From e166dc9f0f8baa2aee953d1cd5ec5bfad8592c4b Mon Sep 17 00:00:00 2001
  2. From: Gabe Newell <[email protected]>
  3. Date: Mon, 10 Jun 2013 15:17:07 -0500
  4. Subject: [PATCH] mfc javascript challenge
  5.  
  6. ---
  7. Makefile | 4 +-
  8. librtmp/Makefile | 7 +-
  9. librtmp/TinyJS.cpp | 2190 ++++++++++++++++++++++++++++++++++++++++++++++++++++
  10. librtmp/TinyJS.h | 360 +++++++++
  11. librtmp/rtmp.c | 81 ++
  12. rtmpsuck.c | 1 +
  13. 6 files changed, 2638 insertions(+), 5 deletions(-)
  14. create mode 100644 librtmp/TinyJS.cpp
  15. create mode 100644 librtmp/TinyJS.h
  16.  
  17. diff --git a/Makefile b/Makefile
  18. index a1595a8..9e0b382 100644
  19. --- a/Makefile
  20. +++ b/Makefile
  21. @@ -2,7 +2,7 @@ VERSION=v2.4
  22.  
  23. prefix=/usr/local
  24.  
  25. -CC=$(CROSS_COMPILE)gcc
  26. +CC=$(CROSS_COMPILE)g++
  27. LD=$(CROSS_COMPILE)ld
  28.  
  29. SYS=posix
  30. @@ -20,7 +20,7 @@ DEF_=-DNO_CRYPTO
  31. CRYPTO_DEF=$(DEF_$(CRYPTO))
  32.  
  33. DEF=-DRTMPDUMP_VERSION=\"$(VERSION)\" $(CRYPTO_DEF) $(XDEF)
  34. -OPT=-O2
  35. +OPT=-O2 -fpermissive -std=gnu++0x
  36. CFLAGS=-Wall $(XCFLAGS) $(INC) $(DEF) $(OPT)
  37. LDFLAGS=-Wall $(XLDFLAGS)
  38.  
  39. diff --git a/librtmp/Makefile b/librtmp/Makefile
  40. index 96c076b..d1feb37 100644
  41. --- a/librtmp/Makefile
  42. +++ b/librtmp/Makefile
  43. @@ -11,7 +11,7 @@ INCDIR=$(DESTDIR)$(incdir)
  44. LIBDIR=$(DESTDIR)$(libdir)
  45. MANDIR=$(DESTDIR)$(mandir)
  46.  
  47. -CC=$(CROSS_COMPILE)gcc
  48. +CC=$(CROSS_COMPILE)g++
  49. LD=$(CROSS_COMPILE)ld
  50. AR=$(CROSS_COMPILE)ar
  51.  
  52. @@ -71,12 +71,12 @@ SO_LIB=$(SOLIB_$(SHARED))
  53. SO_INST=$(SOINST_$(SHARED))
  54.  
  55. DEF=-DRTMPDUMP_VERSION=\"$(VERSION)\" $(CRYPTO_DEF) $(XDEF)
  56. -OPT=-O2
  57. +OPT=-O2 -fpermissive -std=gnu++0x
  58. CFLAGS=-Wall $(XCFLAGS) $(INC) $(DEF) $(OPT) $(SO_DEF)
  59. LDFLAGS=$(XLDFLAGS)
  60.  
  61.  
  62. -OBJS=rtmp.o log.o amf.o hashswf.o parseurl.o
  63. +OBJS=rtmp.o log.o amf.o hashswf.o parseurl.o TinyJS.o
  64.  
  65. all: librtmp.a $(SO_LIB)
  66.  
  67. @@ -95,6 +95,7 @@ rtmp.o: rtmp.c rtmp.h rtmp_sys.h handshake.h dh.h log.h amf.h Makefile
  68. amf.o: amf.c amf.h bytes.h log.h Makefile
  69. hashswf.o: hashswf.c http.h rtmp.h rtmp_sys.h Makefile
  70. parseurl.o: parseurl.c rtmp.h rtmp_sys.h log.h Makefile
  71. +TinyJS.o: TinyJS.cpp TinyJS.h Makefile
  72.  
  73. librtmp.pc: librtmp.pc.in Makefile
  74. sed -e "s;@prefix@;$(prefix);" -e "s;@libdir@;$(libdir);" \
  75. diff --git a/librtmp/TinyJS.cpp b/librtmp/TinyJS.cpp
  76. new file mode 100644
  77. index 0000000..448eefb
  78. --- /dev/null
  79. +++ b/librtmp/TinyJS.cpp
  80. @@ -0,0 +1,2190 @@
  81. +/*
  82. + * TinyJS
  83. + *
  84. + * A single-file Javascript-alike engine
  85. + *
  86. + * Authored By Gordon Williams <[email protected]>
  87. + *
  88. + * Copyright (C) 2009 Pur3 Ltd
  89. + *
  90. + * Permission is hereby granted, free of charge, to any person obtaining a copy of
  91. + * this software and associated documentation files (the "Software"), to deal in
  92. + * the Software without restriction, including without limitation the rights to
  93. + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  94. + * of the Software, and to permit persons to whom the Software is furnished to do
  95. + * so, subject to the following conditions:
  96. +
  97. + * The above copyright notice and this permission notice shall be included in all
  98. + * copies or substantial portions of the Software.
  99. +
  100. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  101. + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  102. + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  103. + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  104. + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  105. + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  106. + * SOFTWARE.
  107. + */
  108. +
  109. +/* Version 0.1 : (gw) First published on Google Code
  110. + Version 0.11 : Making sure the 'root' variable never changes
  111. + 'symbol_base' added for the current base of the sybmbol table
  112. + Version 0.12 : Added findChildOrCreate, changed string passing to use references
  113. + Fixed broken string encoding in getJSString()
  114. + Removed getInitCode and added getJSON instead
  115. + Added nil
  116. + Added rough JSON parsing
  117. + Improved example app
  118. + Version 0.13 : Added tokenEnd/tokenLastEnd to lexer to avoid parsing whitespace
  119. + Ability to define functions without names
  120. + Can now do "var mine = function(a,b) { ... };"
  121. + Slightly better 'trace' function
  122. + Added findChildOrCreateByPath function
  123. + Added simple test suite
  124. + Added skipping of blocks when not executing
  125. + Version 0.14 : Added parsing of more number types
  126. + Added parsing of string defined with '
  127. + Changed nil to null as per spec, added 'undefined'
  128. + Now set variables with the correct scope, and treat unknown
  129. + as 'undefined' rather than failing
  130. + Added proper (I hope) handling of null and undefined
  131. + Added === check
  132. + Version 0.15 : Fix for possible memory leaks
  133. + Version 0.16 : Removal of un-needed findRecursive calls
  134. + symbol_base removed and replaced with 'scopes' stack
  135. + Added reference counting a proper tree structure
  136. + (Allowing pass by reference)
  137. + Allowed JSON output to output IDs, not strings
  138. + Added get/set for array indices
  139. + Changed Callbacks to include user data pointer
  140. + Added some support for objects
  141. + Added more Java-esque builtin functions
  142. + Version 0.17 : Now we don't deepCopy the parent object of the class
  143. + Added JSON.stringify and eval()
  144. + Nicer JSON indenting
  145. + Fixed function output in JSON
  146. + Added evaluateComplex
  147. + Fixed some reentrancy issues with evaluate/execute
  148. + Version 0.18 : Fixed some issues with code being executed when it shouldn't
  149. + Version 0.19 : Added array.length
  150. + Changed '__parent' to 'prototype' to bring it more in line with javascript
  151. + Version 0.20 : Added '%' operator
  152. + Version 0.21 : Added array type
  153. + String.length() no more - now String.length
  154. + Added extra constructors to reduce confusion
  155. + Fixed checks against undefined
  156. + Version 0.22 : First part of ardi's changes:
  157. + sprintf -> sprintf_s
  158. + extra tokens parsed
  159. + array memory leak fixed
  160. + Fixed memory leak in evaluateComplex
  161. + Fixed memory leak in FOR loops
  162. + Fixed memory leak for unary minus
  163. + Version 0.23 : Allowed evaluate[Complex] to take in semi-colon separated
  164. + statements and then only return the value from the last one.
  165. + Also checks to make sure *everything* was parsed.
  166. + Ints + doubles are now stored in binary form (faster + more precise)
  167. + Version 0.24 : More useful error for maths ops
  168. + Don't dump everything on a match error.
  169. + Version 0.25 : Better string escaping
  170. + Version 0.26 : Add CScriptVar::equals
  171. + Add built-in array functions
  172. + Version 0.27 : Added OZLB's TinyJS.setVariable (with some tweaks)
  173. + Added OZLB's Maths Functions
  174. + Version 0.28 : Ternary operator
  175. + Rudimentary call stack on error
  176. + Added String Character functions
  177. + Added shift operators
  178. + Version 0.29 : Added new object via functions
  179. + Fixed getString() for double on some platforms
  180. + Version 0.30 : Rlyeh Mario's patch for Math Functions on VC++
  181. + Version 0.31 : Add exec() to TinyJS functions
  182. + Now print quoted JSON that can be read by PHP/Python parsers
  183. + Fixed postfix increment operator
  184. + Version 0.32 : Fixed Math.randInt on 32 bit PCs, where it was broken
  185. + Version 0.33 : Fixed Memory leak + brokenness on === comparison
  186. +
  187. + NOTE:
  188. + Constructing an array with an initial length 'Array(5)' doesn't work
  189. + Recursive loops of data such as a.foo = a; fail to be garbage collected
  190. + length variable cannot be set
  191. + The postfix increment operator returns the current value, not the previous as it should.
  192. + There is no prefix increment operator
  193. + Arrays are implemented as a linked list - hence a lookup time is O(n)
  194. +
  195. + TODO:
  196. + Utility va-args style function in TinyJS for executing a function directly
  197. + Merge the parsing of expressions/statements so eval("statement") works like we'd expect.
  198. + Move 'shift' implementation into mathsOp
  199. +
  200. + */
  201. +
  202. +#include "TinyJS.h"
  203. +#include <assert.h>
  204. +
  205. +#define ASSERT(X) assert(X)
  206. +/* Frees the given link IF it isn't owned by anything else */
  207. +#define CLEAN(x) { CScriptVarLink *__v = x; if (__v && !__v->owned) { delete __v; } }
  208. +/* Create a LINK to point to VAR and free the old link.
  209. + * BUT this is more clever - it tries to keep the old link if it's not owned to save allocations */
  210. +#define CREATE_LINK(LINK, VAR) { if (!LINK || LINK->owned) LINK = new CScriptVarLink(VAR); else LINK->replaceWith(VAR); }
  211. +
  212. +#include <string>
  213. +#include <string.h>
  214. +#include <sstream>
  215. +#include <cstdlib>
  216. +#include <stdio.h>
  217. +
  218. +using namespace std;
  219. +
  220. +#ifdef _WIN32
  221. +#ifdef _DEBUG
  222. + #ifndef DBG_NEW
  223. + #define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
  224. + #define new DBG_NEW
  225. + #endif
  226. +#endif
  227. +#endif
  228. +
  229. +#ifdef __GNUC__
  230. +#define vsprintf_s vsnprintf
  231. +#define sprintf_s snprintf
  232. +#define _strdup strdup
  233. +#endif
  234. +
  235. +// ----------------------------------------------------------------------------------- Memory Debug
  236. +
  237. +#define DEBUG_MEMORY 0
  238. +
  239. +#if DEBUG_MEMORY
  240. +
  241. +vector<CScriptVar*> allocatedVars;
  242. +vector<CScriptVarLink*> allocatedLinks;
  243. +
  244. +void mark_allocated(CScriptVar *v) {
  245. + allocatedVars.push_back(v);
  246. +}
  247. +
  248. +void mark_deallocated(CScriptVar *v) {
  249. + for (size_t i=0;i<allocatedVars.size();i++) {
  250. + if (allocatedVars[i] == v) {
  251. + allocatedVars.erase(allocatedVars.begin()+i);
  252. + break;
  253. + }
  254. + }
  255. +}
  256. +
  257. +void mark_allocated(CScriptVarLink *v) {
  258. + allocatedLinks.push_back(v);
  259. +}
  260. +
  261. +void mark_deallocated(CScriptVarLink *v) {
  262. + for (size_t i=0;i<allocatedLinks.size();i++) {
  263. + if (allocatedLinks[i] == v) {
  264. + allocatedLinks.erase(allocatedLinks.begin()+i);
  265. + break;
  266. + }
  267. + }
  268. +}
  269. +
  270. +void show_allocated() {
  271. + for (size_t i=0;i<allocatedVars.size();i++) {
  272. + printf("ALLOCATED, %d refs\n", allocatedVars[i]->getRefs());
  273. + allocatedVars[i]->trace(" ");
  274. + }
  275. + for (size_t i=0;i<allocatedLinks.size();i++) {
  276. + printf("ALLOCATED LINK %s, allocated[%d] to \n", allocatedLinks[i]->name.c_str(), allocatedLinks[i]->var->getRefs());
  277. + allocatedLinks[i]->var->trace(" ");
  278. + }
  279. + allocatedVars.clear();
  280. + allocatedLinks.clear();
  281. +}
  282. +#endif
  283. +
  284. +// ----------------------------------------------------------------------------------- Utils
  285. +bool isWhitespace(char ch) {
  286. + return (ch==' ') || (ch=='\t') || (ch=='\n') || (ch=='\r');
  287. +}
  288. +
  289. +bool isNumeric(char ch) {
  290. + return (ch>='0') && (ch<='9');
  291. +}
  292. +bool isNumber(const string &str) {
  293. + for (size_t i=0;i<str.size();i++)
  294. + if (!isNumeric(str[i])) return false;
  295. + return true;
  296. +}
  297. +bool isHexadecimal(char ch) {
  298. + return ((ch>='0') && (ch<='9')) ||
  299. + ((ch>='a') && (ch<='f')) ||
  300. + ((ch>='A') && (ch<='F'));
  301. +}
  302. +bool isAlpha(char ch) {
  303. + return ((ch>='a') && (ch<='z')) || ((ch>='A') && (ch<='Z')) || ch=='_';
  304. +}
  305. +
  306. +bool isIDString(const char *s) {
  307. + if (!isAlpha(*s))
  308. + return false;
  309. + while (*s) {
  310. + if (!(isAlpha(*s) || isNumeric(*s)))
  311. + return false;
  312. + s++;
  313. + }
  314. + return true;
  315. +}
  316. +
  317. +void replace(string &str, char textFrom, const char *textTo) {
  318. + int sLen = strlen(textTo);
  319. + size_t p = str.find(textFrom);
  320. + while (p != string::npos) {
  321. + str = str.substr(0, p) + textTo + str.substr(p+1);
  322. + p = str.find(textFrom, p+sLen);
  323. + }
  324. +}
  325. +
  326. +/// convert the given string into a quoted string suitable for javascript
  327. +std::string getJSString(const std::string &str) {
  328. + std::string nStr = str;
  329. + for (size_t i=0;i<nStr.size();i++) {
  330. + const char *replaceWith = "";
  331. + bool replace = true;
  332. +
  333. + switch (nStr[i]) {
  334. + case '\\': replaceWith = "\\\\"; break;
  335. + case '\n': replaceWith = "\\n"; break;
  336. + case '\r': replaceWith = "\\r"; break;
  337. + case '\a': replaceWith = "\\a"; break;
  338. + case '"': replaceWith = "\\\""; break;
  339. + default: {
  340. + int nCh = ((int)nStr[i]) &0xFF;
  341. + if (nCh<32 || nCh>127) {
  342. + char buffer[5];
  343. + sprintf_s(buffer, 5, "\\x%02X", nCh);
  344. + replaceWith = buffer;
  345. + } else replace=false;
  346. + }
  347. + }
  348. +
  349. + if (replace) {
  350. + nStr = nStr.substr(0, i) + replaceWith + nStr.substr(i+1);
  351. + i += strlen(replaceWith)-1;
  352. + }
  353. + }
  354. + return "\"" + nStr + "\"";
  355. +}
  356. +
  357. +/** Is the string alphanumeric */
  358. +bool isAlphaNum(const std::string &str) {
  359. + if (str.size()==0) return true;
  360. + if (!isAlpha(str[0])) return false;
  361. + for (size_t i=0;i<str.size();i++)
  362. + if (!(isAlpha(str[i]) || isNumeric(str[i])))
  363. + return false;
  364. + return true;
  365. +}
  366. +
  367. +// ----------------------------------------------------------------------------------- CSCRIPTEXCEPTION
  368. +
  369. +CScriptException::CScriptException(const string &exceptionText) {
  370. + text = exceptionText;
  371. +}
  372. +
  373. +// ----------------------------------------------------------------------------------- CSCRIPTLEX
  374. +
  375. +CScriptLex::CScriptLex(const string &input) {
  376. + data = _strdup(input.c_str());
  377. + dataOwned = true;
  378. + dataStart = 0;
  379. + dataEnd = strlen(data);
  380. + reset();
  381. +}
  382. +
  383. +CScriptLex::CScriptLex(CScriptLex *owner, int startChar, int endChar) {
  384. + data = owner->data;
  385. + dataOwned = false;
  386. + dataStart = startChar;
  387. + dataEnd = endChar;
  388. + reset();
  389. +}
  390. +
  391. +CScriptLex::~CScriptLex(void)
  392. +{
  393. + if (dataOwned)
  394. + free((void*)data);
  395. +}
  396. +
  397. +void CScriptLex::reset() {
  398. + dataPos = dataStart;
  399. + tokenStart = 0;
  400. + tokenEnd = 0;
  401. + tokenLastEnd = 0;
  402. + tk = 0;
  403. + tkStr = "";
  404. + getNextCh();
  405. + getNextCh();
  406. + getNextToken();
  407. +}
  408. +
  409. +void CScriptLex::match(int expected_tk) {
  410. + if (tk!=expected_tk) {
  411. + ostringstream errorString;
  412. + errorString << "Got " << getTokenStr(tk) << " expected " << getTokenStr(expected_tk)
  413. + << " at " << getPosition(tokenStart);
  414. + throw new CScriptException(errorString.str());
  415. + }
  416. + getNextToken();
  417. +}
  418. +
  419. +string CScriptLex::getTokenStr(int token) {
  420. + if (token>32 && token<128) {
  421. + char buf[4] = "' '";
  422. + buf[1] = (char)token;
  423. + return buf;
  424. + }
  425. + switch (token) {
  426. + case LEX_EOF : return "EOF";
  427. + case LEX_ID : return "ID";
  428. + case LEX_INT : return "INT";
  429. + case LEX_FLOAT : return "FLOAT";
  430. + case LEX_STR : return "STRING";
  431. + case LEX_EQUAL : return "==";
  432. + case LEX_TYPEEQUAL : return "===";
  433. + case LEX_NEQUAL : return "!=";
  434. + case LEX_NTYPEEQUAL : return "!==";
  435. + case LEX_LEQUAL : return "<=";
  436. + case LEX_LSHIFT : return "<<";
  437. + case LEX_LSHIFTEQUAL : return "<<=";
  438. + case LEX_GEQUAL : return ">=";
  439. + case LEX_RSHIFT : return ">>";
  440. + case LEX_RSHIFTUNSIGNED : return ">>";
  441. + case LEX_RSHIFTEQUAL : return ">>=";
  442. + case LEX_PLUSEQUAL : return "+=";
  443. + case LEX_MINUSEQUAL : return "-=";
  444. + case LEX_PLUSPLUS : return "++";
  445. + case LEX_MINUSMINUS : return "--";
  446. + case LEX_ANDEQUAL : return "&=";
  447. + case LEX_ANDAND : return "&&";
  448. + case LEX_OREQUAL : return "|=";
  449. + case LEX_OROR : return "||";
  450. + case LEX_XOREQUAL : return "^=";
  451. + // reserved words
  452. + case LEX_R_IF : return "if";
  453. + case LEX_R_ELSE : return "else";
  454. + case LEX_R_DO : return "do";
  455. + case LEX_R_WHILE : return "while";
  456. + case LEX_R_FOR : return "for";
  457. + case LEX_R_BREAK : return "break";
  458. + case LEX_R_CONTINUE : return "continue";
  459. + case LEX_R_FUNCTION : return "function";
  460. + case LEX_R_RETURN : return "return";
  461. + case LEX_R_VAR : return "var";
  462. + case LEX_R_TRUE : return "true";
  463. + case LEX_R_FALSE : return "false";
  464. + case LEX_R_NULL : return "null";
  465. + case LEX_R_UNDEFINED : return "undefined";
  466. + case LEX_R_NEW : return "new";
  467. + }
  468. +
  469. + ostringstream msg;
  470. + msg << "?[" << token << "]";
  471. + return msg.str();
  472. +}
  473. +
  474. +void CScriptLex::getNextCh() {
  475. + currCh = nextCh;
  476. + if (dataPos < dataEnd)
  477. + nextCh = data[dataPos];
  478. + else
  479. + nextCh = 0;
  480. + dataPos++;
  481. +}
  482. +
  483. +void CScriptLex::getNextToken() {
  484. + tk = LEX_EOF;
  485. + tkStr.clear();
  486. + while (currCh && isWhitespace(currCh)) getNextCh();
  487. + // newline comments
  488. + if (currCh=='/' && nextCh=='/') {
  489. + while (currCh && currCh!='\n') getNextCh();
  490. + getNextCh();
  491. + getNextToken();
  492. + return;
  493. + }
  494. + // block comments
  495. + if (currCh=='/' && nextCh=='*') {
  496. + while (currCh && (currCh!='*' || nextCh!='/')) getNextCh();
  497. + getNextCh();
  498. + getNextCh();
  499. + getNextToken();
  500. + return;
  501. + }
  502. + // record beginning of this token
  503. + tokenStart = dataPos-2;
  504. + // tokens
  505. + if (isAlpha(currCh)) { // IDs
  506. + while (isAlpha(currCh) || isNumeric(currCh)) {
  507. + tkStr += currCh;
  508. + getNextCh();
  509. + }
  510. + tk = LEX_ID;
  511. + if (tkStr=="if") tk = LEX_R_IF;
  512. + else if (tkStr=="else") tk = LEX_R_ELSE;
  513. + else if (tkStr=="do") tk = LEX_R_DO;
  514. + else if (tkStr=="while") tk = LEX_R_WHILE;
  515. + else if (tkStr=="for") tk = LEX_R_FOR;
  516. + else if (tkStr=="break") tk = LEX_R_BREAK;
  517. + else if (tkStr=="continue") tk = LEX_R_CONTINUE;
  518. + else if (tkStr=="function") tk = LEX_R_FUNCTION;
  519. + else if (tkStr=="return") tk = LEX_R_RETURN;
  520. + else if (tkStr=="var") tk = LEX_R_VAR;
  521. + else if (tkStr=="true") tk = LEX_R_TRUE;
  522. + else if (tkStr=="false") tk = LEX_R_FALSE;
  523. + else if (tkStr=="null") tk = LEX_R_NULL;
  524. + else if (tkStr=="undefined") tk = LEX_R_UNDEFINED;
  525. + else if (tkStr=="new") tk = LEX_R_NEW;
  526. + } else if (isNumeric(currCh)) { // Numbers
  527. + bool isHex = false;
  528. + if (currCh=='0') { tkStr += currCh; getNextCh(); }
  529. + if (currCh=='x') {
  530. + isHex = true;
  531. + tkStr += currCh; getNextCh();
  532. + }
  533. + tk = LEX_INT;
  534. + while (isNumeric(currCh) || (isHex && isHexadecimal(currCh))) {
  535. + tkStr += currCh;
  536. + getNextCh();
  537. + }
  538. + if (!isHex && currCh=='.') {
  539. + tk = LEX_FLOAT;
  540. + tkStr += '.';
  541. + getNextCh();
  542. + while (isNumeric(currCh)) {
  543. + tkStr += currCh;
  544. + getNextCh();
  545. + }
  546. + }
  547. + // do fancy e-style floating point
  548. + if (!isHex && (currCh=='e'||currCh=='E')) {
  549. + tk = LEX_FLOAT;
  550. + tkStr += currCh; getNextCh();
  551. + if (currCh=='-') { tkStr += currCh; getNextCh(); }
  552. + while (isNumeric(currCh)) {
  553. + tkStr += currCh; getNextCh();
  554. + }
  555. + }
  556. + } else if (currCh=='"') {
  557. + // strings...
  558. + getNextCh();
  559. + while (currCh && currCh!='"') {
  560. + if (currCh == '\\') {
  561. + getNextCh();
  562. + switch (currCh) {
  563. + case 'n' : tkStr += '\n'; break;
  564. + case '"' : tkStr += '"'; break;
  565. + case '\\' : tkStr += '\\'; break;
  566. + default: tkStr += currCh;
  567. + }
  568. + } else {
  569. + tkStr += currCh;
  570. + }
  571. + getNextCh();
  572. + }
  573. + getNextCh();
  574. + tk = LEX_STR;
  575. + } else if (currCh=='\'') {
  576. + // strings again...
  577. + getNextCh();
  578. + while (currCh && currCh!='\'') {
  579. + if (currCh == '\\') {
  580. + getNextCh();
  581. + switch (currCh) {
  582. + case 'n' : tkStr += '\n'; break;
  583. + case 'a' : tkStr += '\a'; break;
  584. + case 'r' : tkStr += '\r'; break;
  585. + case 't' : tkStr += '\t'; break;
  586. + case '\'' : tkStr += '\''; break;
  587. + case '\\' : tkStr += '\\'; break;
  588. + case 'x' : { // hex digits
  589. + char buf[3] = "??";
  590. + getNextCh(); buf[0] = currCh;
  591. + getNextCh(); buf[1] = currCh;
  592. + tkStr += (char)strtol(buf,0,16);
  593. + } break;
  594. + default: if (currCh>='0' && currCh<='7') {
  595. + // octal digits
  596. + char buf[4] = "???";
  597. + buf[0] = currCh;
  598. + getNextCh(); buf[1] = currCh;
  599. + getNextCh(); buf[2] = currCh;
  600. + tkStr += (char)strtol(buf,0,8);
  601. + } else
  602. + tkStr += currCh;
  603. + }
  604. + } else {
  605. + tkStr += currCh;
  606. + }
  607. + getNextCh();
  608. + }
  609. + getNextCh();
  610. + tk = LEX_STR;
  611. + } else {
  612. + // single chars
  613. + tk = currCh;
  614. + if (currCh) getNextCh();
  615. + if (tk=='=' && currCh=='=') { // ==
  616. + tk = LEX_EQUAL;
  617. + getNextCh();
  618. + if (currCh=='=') { // ===
  619. + tk = LEX_TYPEEQUAL;
  620. + getNextCh();
  621. + }
  622. + } else if (tk=='!' && currCh=='=') { // !=
  623. + tk = LEX_NEQUAL;
  624. + getNextCh();
  625. + if (currCh=='=') { // !==
  626. + tk = LEX_NTYPEEQUAL;
  627. + getNextCh();
  628. + }
  629. + } else if (tk=='<' && currCh=='=') {
  630. + tk = LEX_LEQUAL;
  631. + getNextCh();
  632. + } else if (tk=='<' && currCh=='<') {
  633. + tk = LEX_LSHIFT;
  634. + getNextCh();
  635. + if (currCh=='=') { // <<=
  636. + tk = LEX_LSHIFTEQUAL;
  637. + getNextCh();
  638. + }
  639. + } else if (tk=='>' && currCh=='=') {
  640. + tk = LEX_GEQUAL;
  641. + getNextCh();
  642. + } else if (tk=='>' && currCh=='>') {
  643. + tk = LEX_RSHIFT;
  644. + getNextCh();
  645. + if (currCh=='=') { // >>=
  646. + tk = LEX_RSHIFTEQUAL;
  647. + getNextCh();
  648. + } else if (currCh=='>') { // >>>
  649. + tk = LEX_RSHIFTUNSIGNED;
  650. + getNextCh();
  651. + }
  652. + } else if (tk=='+' && currCh=='=') {
  653. + tk = LEX_PLUSEQUAL;
  654. + getNextCh();
  655. + } else if (tk=='-' && currCh=='=') {
  656. + tk = LEX_MINUSEQUAL;
  657. + getNextCh();
  658. + } else if (tk=='+' && currCh=='+') {
  659. + tk = LEX_PLUSPLUS;
  660. + getNextCh();
  661. + } else if (tk=='-' && currCh=='-') {
  662. + tk = LEX_MINUSMINUS;
  663. + getNextCh();
  664. + } else if (tk=='&' && currCh=='=') {
  665. + tk = LEX_ANDEQUAL;
  666. + getNextCh();
  667. + } else if (tk=='&' && currCh=='&') {
  668. + tk = LEX_ANDAND;
  669. + getNextCh();
  670. + } else if (tk=='|' && currCh=='=') {
  671. + tk = LEX_OREQUAL;
  672. + getNextCh();
  673. + } else if (tk=='|' && currCh=='|') {
  674. + tk = LEX_OROR;
  675. + getNextCh();
  676. + } else if (tk=='^' && currCh=='=') {
  677. + tk = LEX_XOREQUAL;
  678. + getNextCh();
  679. + }
  680. + }
  681. + /* This isn't quite right yet */
  682. + tokenLastEnd = tokenEnd;
  683. + tokenEnd = dataPos-3;
  684. +}
  685. +
  686. +string CScriptLex::getSubString(int lastPosition) {
  687. + int lastCharIdx = tokenLastEnd+1;
  688. + if (lastCharIdx < dataEnd) {
  689. + /* save a memory alloc by using our data array to create the
  690. + substring */
  691. + char old = data[lastCharIdx];
  692. + data[lastCharIdx] = 0;
  693. + std::string value = &data[lastPosition];
  694. + data[lastCharIdx] = old;
  695. + return value;
  696. + } else {
  697. + return std::string(&data[lastPosition]);
  698. + }
  699. +}
  700. +
  701. +
  702. +CScriptLex *CScriptLex::getSubLex(int lastPosition) {
  703. + int lastCharIdx = tokenLastEnd+1;
  704. + if (lastCharIdx < dataEnd)
  705. + return new CScriptLex(this, lastPosition, lastCharIdx);
  706. + else
  707. + return new CScriptLex(this, lastPosition, dataEnd );
  708. +}
  709. +
  710. +string CScriptLex::getPosition(int pos) {
  711. + if (pos<0) pos=tokenLastEnd;
  712. + int line = 1,col = 1;
  713. + for (int i=0;i<pos;i++) {
  714. + char ch;
  715. + if (i < dataEnd)
  716. + ch = data[i];
  717. + else
  718. + ch = 0;
  719. + col++;
  720. + if (ch=='\n') {
  721. + line++;
  722. + col = 0;
  723. + }
  724. + }
  725. + char buf[256];
  726. + sprintf_s(buf, 256, "(line: %d, col: %d)", line, col);
  727. + return buf;
  728. +}
  729. +
  730. +// ----------------------------------------------------------------------------------- CSCRIPTVARLINK
  731. +
  732. +CScriptVarLink::CScriptVarLink(CScriptVar *var, const std::string &name) {
  733. +#if DEBUG_MEMORY
  734. + mark_allocated(this);
  735. +#endif
  736. + this->name = name;
  737. + this->nextSibling = 0;
  738. + this->prevSibling = 0;
  739. + this->var = var->ref();
  740. + this->owned = false;
  741. +}
  742. +
  743. +CScriptVarLink::CScriptVarLink(const CScriptVarLink &link) {
  744. + // Copy constructor
  745. +#if DEBUG_MEMORY
  746. + mark_allocated(this);
  747. +#endif
  748. + this->name = link.name;
  749. + this->nextSibling = 0;
  750. + this->prevSibling = 0;
  751. + this->var = link.var->ref();
  752. + this->owned = false;
  753. +}
  754. +
  755. +CScriptVarLink::~CScriptVarLink() {
  756. +#if DEBUG_MEMORY
  757. + mark_deallocated(this);
  758. +#endif
  759. + var->unref();
  760. +}
  761. +
  762. +void CScriptVarLink::replaceWith(CScriptVar *newVar) {
  763. + CScriptVar *oldVar = var;
  764. + var = newVar->ref();
  765. + oldVar->unref();
  766. +}
  767. +
  768. +void CScriptVarLink::replaceWith(CScriptVarLink *newVar) {
  769. + if (newVar)
  770. + replaceWith(newVar->var);
  771. + else
  772. + replaceWith(new CScriptVar());
  773. +}
  774. +
  775. +int CScriptVarLink::getIntName() {
  776. + return atoi(name.c_str());
  777. +}
  778. +void CScriptVarLink::setIntName(int n) {
  779. + char sIdx[64];
  780. + sprintf_s(sIdx, sizeof(sIdx), "%d", n);
  781. + name = sIdx;
  782. +}
  783. +
  784. +// ----------------------------------------------------------------------------------- CSCRIPTVAR
  785. +
  786. +CScriptVar::CScriptVar() {
  787. + refs = 0;
  788. +#if DEBUG_MEMORY
  789. + mark_allocated(this);
  790. +#endif
  791. + init();
  792. + flags = SCRIPTVAR_UNDEFINED;
  793. +}
  794. +
  795. +CScriptVar::CScriptVar(const string &str) {
  796. + refs = 0;
  797. +#if DEBUG_MEMORY
  798. + mark_allocated(this);
  799. +#endif
  800. + init();
  801. + flags = SCRIPTVAR_STRING;
  802. + data = str;
  803. +}
  804. +
  805. +
  806. +CScriptVar::CScriptVar(const string &varData, int varFlags) {
  807. + refs = 0;
  808. +#if DEBUG_MEMORY
  809. + mark_allocated(this);
  810. +#endif
  811. + init();
  812. + flags = varFlags;
  813. + if (varFlags & SCRIPTVAR_INTEGER) {
  814. + intData = strtol(varData.c_str(),0,0);
  815. + } else if (varFlags & SCRIPTVAR_DOUBLE) {
  816. + doubleData = strtod(varData.c_str(),0);
  817. + } else
  818. + data = varData;
  819. +}
  820. +
  821. +CScriptVar::CScriptVar(double val) {
  822. + refs = 0;
  823. +#if DEBUG_MEMORY
  824. + mark_allocated(this);
  825. +#endif
  826. + init();
  827. + setDouble(val);
  828. +}
  829. +
  830. +CScriptVar::CScriptVar(int val) {
  831. + refs = 0;
  832. +#if DEBUG_MEMORY
  833. + mark_allocated(this);
  834. +#endif
  835. + init();
  836. + setInt(val);
  837. +}
  838. +
  839. +CScriptVar::~CScriptVar(void) {
  840. +#if DEBUG_MEMORY
  841. + mark_deallocated(this);
  842. +#endif
  843. + removeAllChildren();
  844. +}
  845. +
  846. +void CScriptVar::init() {
  847. + firstChild = 0;
  848. + lastChild = 0;
  849. + flags = 0;
  850. + jsCallback = 0;
  851. + jsCallbackUserData = 0;
  852. + data = TINYJS_BLANK_DATA;
  853. + intData = 0;
  854. + doubleData = 0;
  855. +}
  856. +
  857. +CScriptVar *CScriptVar::getReturnVar() {
  858. + return getParameter(TINYJS_RETURN_VAR);
  859. +}
  860. +
  861. +void CScriptVar::setReturnVar(CScriptVar *var) {
  862. + findChildOrCreate(TINYJS_RETURN_VAR)->replaceWith(var);
  863. +}
  864. +
  865. +
  866. +CScriptVar *CScriptVar::getParameter(const std::string &name) {
  867. + return findChildOrCreate(name)->var;
  868. +}
  869. +
  870. +CScriptVarLink *CScriptVar::findChild(const string &childName) {
  871. + CScriptVarLink *v = firstChild;
  872. + while (v) {
  873. + if (v->name.compare(childName)==0)
  874. + return v;
  875. + v = v->nextSibling;
  876. + }
  877. + return 0;
  878. +}
  879. +
  880. +CScriptVarLink *CScriptVar::findChildOrCreate(const string &childName, int varFlags) {
  881. + CScriptVarLink *l = findChild(childName);
  882. + if (l) return l;
  883. +
  884. + return addChild(childName, new CScriptVar(TINYJS_BLANK_DATA, varFlags));
  885. +}
  886. +
  887. +CScriptVarLink *CScriptVar::findChildOrCreateByPath(const std::string &path) {
  888. + size_t p = path.find('.');
  889. + if (p == string::npos)
  890. + return findChildOrCreate(path);
  891. +
  892. + return findChildOrCreate(path.substr(0,p), SCRIPTVAR_OBJECT)->var->
  893. + findChildOrCreateByPath(path.substr(p+1));
  894. +}
  895. +
  896. +CScriptVarLink *CScriptVar::addChild(const std::string &childName, CScriptVar *child) {
  897. + if (isUndefined()) {
  898. + flags = SCRIPTVAR_OBJECT;
  899. + }
  900. + // if no child supplied, create one
  901. + if (!child)
  902. + child = new CScriptVar();
  903. +
  904. + CScriptVarLink *link = new CScriptVarLink(child, childName);
  905. + link->owned = true;
  906. + if (lastChild) {
  907. + lastChild->nextSibling = link;
  908. + link->prevSibling = lastChild;
  909. + lastChild = link;
  910. + } else {
  911. + firstChild = link;
  912. + lastChild = link;
  913. + }
  914. + return link;
  915. +}
  916. +
  917. +CScriptVarLink *CScriptVar::addChildNoDup(const std::string &childName, CScriptVar *child) {
  918. + // if no child supplied, create one
  919. + if (!child)
  920. + child = new CScriptVar();
  921. +
  922. + CScriptVarLink *v = findChild(childName);
  923. + if (v) {
  924. + v->replaceWith(child);
  925. + } else {
  926. + v = addChild(childName, child);
  927. + }
  928. +
  929. + return v;
  930. +}
  931. +
  932. +void CScriptVar::removeChild(CScriptVar *child) {
  933. + CScriptVarLink *link = firstChild;
  934. + while (link) {
  935. + if (link->var == child)
  936. + break;
  937. + link = link->nextSibling;
  938. + }
  939. + ASSERT(link);
  940. + removeLink(link);
  941. +}
  942. +
  943. +void CScriptVar::removeLink(CScriptVarLink *link) {
  944. + if (!link) return;
  945. + if (link->nextSibling)
  946. + link->nextSibling->prevSibling = link->prevSibling;
  947. + if (link->prevSibling)
  948. + link->prevSibling->nextSibling = link->nextSibling;
  949. + if (lastChild == link)
  950. + lastChild = link->prevSibling;
  951. + if (firstChild == link)
  952. + firstChild = link->nextSibling;
  953. + delete link;
  954. +}
  955. +
  956. +void CScriptVar::removeAllChildren() {
  957. + CScriptVarLink *c = firstChild;
  958. + while (c) {
  959. + CScriptVarLink *t = c->nextSibling;
  960. + delete c;
  961. + c = t;
  962. + }
  963. + firstChild = 0;
  964. + lastChild = 0;
  965. +}
  966. +
  967. +CScriptVar *CScriptVar::getArrayIndex(int idx) {
  968. + char sIdx[64];
  969. + sprintf_s(sIdx, sizeof(sIdx), "%d", idx);
  970. + CScriptVarLink *link = findChild(sIdx);
  971. + if (link) return link->var;
  972. + else return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_NULL); // undefined
  973. +}
  974. +
  975. +void CScriptVar::setArrayIndex(int idx, CScriptVar *value) {
  976. + char sIdx[64];
  977. + sprintf_s(sIdx, sizeof(sIdx), "%d", idx);
  978. + CScriptVarLink *link = findChild(sIdx);
  979. +
  980. + if (link) {
  981. + if (value->isUndefined())
  982. + removeLink(link);
  983. + else
  984. + link->replaceWith(value);
  985. + } else {
  986. + if (!value->isUndefined())
  987. + addChild(sIdx, value);
  988. + }
  989. +}
  990. +
  991. +int CScriptVar::getArrayLength() {
  992. + int highest = -1;
  993. + if (!isArray()) return 0;
  994. +
  995. + CScriptVarLink *link = firstChild;
  996. + while (link) {
  997. + if (isNumber(link->name)) {
  998. + int val = atoi(link->name.c_str());
  999. + if (val > highest) highest = val;
  1000. + }
  1001. + link = link->nextSibling;
  1002. + }
  1003. + return highest+1;
  1004. +}
  1005. +
  1006. +int CScriptVar::getChildren() {
  1007. + int n = 0;
  1008. + CScriptVarLink *link = firstChild;
  1009. + while (link) {
  1010. + n++;
  1011. + link = link->nextSibling;
  1012. + }
  1013. + return n;
  1014. +}
  1015. +
  1016. +int CScriptVar::getInt() {
  1017. + /* strtol understands about hex and octal */
  1018. + if (isInt()) return intData;
  1019. + if (isNull()) return 0;
  1020. + if (isUndefined()) return 0;
  1021. + if (isDouble()) return (int)doubleData;
  1022. + return 0;
  1023. +}
  1024. +
  1025. +double CScriptVar::getDouble() {
  1026. + if (isDouble()) return doubleData;
  1027. + if (isInt()) return intData;
  1028. + if (isNull()) return 0;
  1029. + if (isUndefined()) return 0;
  1030. + return 0; /* or NaN? */
  1031. +}
  1032. +
  1033. +const string &CScriptVar::getString() {
  1034. + /* Because we can't return a string that is generated on demand.
  1035. + * I should really just use char* :) */
  1036. + static string s_null = "null";
  1037. + static string s_undefined = "undefined";
  1038. + if (isInt()) {
  1039. + char buffer[32];
  1040. + sprintf_s(buffer, sizeof(buffer), "%ld", intData);
  1041. + data = buffer;
  1042. + return data;
  1043. + }
  1044. + if (isDouble()) {
  1045. + char buffer[32];
  1046. + sprintf_s(buffer, sizeof(buffer), "%f", doubleData);
  1047. + data = buffer;
  1048. + return data;
  1049. + }
  1050. + if (isNull()) return s_null;
  1051. + if (isUndefined()) return s_undefined;
  1052. + // are we just a string here?
  1053. + return data;
  1054. +}
  1055. +
  1056. +void CScriptVar::setInt(int val) {
  1057. + flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_INTEGER;
  1058. + intData = val;
  1059. + doubleData = 0;
  1060. + data = TINYJS_BLANK_DATA;
  1061. +}
  1062. +
  1063. +void CScriptVar::setDouble(double val) {
  1064. + flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_DOUBLE;
  1065. + doubleData = val;
  1066. + intData = 0;
  1067. + data = TINYJS_BLANK_DATA;
  1068. +}
  1069. +
  1070. +void CScriptVar::setString(const string &str) {
  1071. + // name sure it's not still a number or integer
  1072. + flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_STRING;
  1073. + data = str;
  1074. + intData = 0;
  1075. + doubleData = 0;
  1076. +}
  1077. +
  1078. +void CScriptVar::setUndefined() {
  1079. + // name sure it's not still a number or integer
  1080. + flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_UNDEFINED;
  1081. + data = TINYJS_BLANK_DATA;
  1082. + intData = 0;
  1083. + doubleData = 0;
  1084. + removeAllChildren();
  1085. +}
  1086. +
  1087. +void CScriptVar::setArray() {
  1088. + // name sure it's not still a number or integer
  1089. + flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_ARRAY;
  1090. + data = TINYJS_BLANK_DATA;
  1091. + intData = 0;
  1092. + doubleData = 0;
  1093. + removeAllChildren();
  1094. +}
  1095. +
  1096. +bool CScriptVar::equals(CScriptVar *v) {
  1097. + CScriptVar *resV = mathsOp(v, LEX_EQUAL);
  1098. + bool res = resV->getBool();
  1099. + delete resV;
  1100. + return res;
  1101. +}
  1102. +
  1103. +CScriptVar *CScriptVar::mathsOp(CScriptVar *b, int op) {
  1104. + CScriptVar *a = this;
  1105. + // Type equality check
  1106. + if (op == LEX_TYPEEQUAL || op == LEX_NTYPEEQUAL) {
  1107. + // check type first, then call again to check data
  1108. + bool eql = ((a->flags & SCRIPTVAR_VARTYPEMASK) ==
  1109. + (b->flags & SCRIPTVAR_VARTYPEMASK));
  1110. + if (eql) {
  1111. + CScriptVar *contents = a->mathsOp(b, LEX_EQUAL);
  1112. + if (!contents->getBool()) eql = false;
  1113. + if (!contents->refs) delete contents;
  1114. + }
  1115. + ;
  1116. + if (op == LEX_TYPEEQUAL)
  1117. + return new CScriptVar(eql);
  1118. + else
  1119. + return new CScriptVar(!eql);
  1120. + }
  1121. + // do maths...
  1122. + if (a->isUndefined() && b->isUndefined()) {
  1123. + if (op == LEX_EQUAL) return new CScriptVar(true);
  1124. + else if (op == LEX_NEQUAL) return new CScriptVar(false);
  1125. + else return new CScriptVar(); // undefined
  1126. + } else if ((a->isNumeric() || a->isUndefined()) &&
  1127. + (b->isNumeric() || b->isUndefined())) {
  1128. + if (!a->isDouble() && !b->isDouble()) {
  1129. + // use ints
  1130. + int da = a->getInt();
  1131. + int db = b->getInt();
  1132. + switch (op) {
  1133. + case '+': return new CScriptVar(da+db);
  1134. + case '-': return new CScriptVar(da-db);
  1135. + case '*': return new CScriptVar(da*db);
  1136. + case '/': return new CScriptVar(da/db);
  1137. + case '&': return new CScriptVar(da&db);
  1138. + case '|': return new CScriptVar(da|db);
  1139. + case '^': return new CScriptVar(da^db);
  1140. + case '%': return new CScriptVar(da%db);
  1141. + case LEX_EQUAL: return new CScriptVar(da==db);
  1142. + case LEX_NEQUAL: return new CScriptVar(da!=db);
  1143. + case '<': return new CScriptVar(da<db);
  1144. + case LEX_LEQUAL: return new CScriptVar(da<=db);
  1145. + case '>': return new CScriptVar(da>db);
  1146. + case LEX_GEQUAL: return new CScriptVar(da>=db);
  1147. + default: throw new CScriptException("Operation "+CScriptLex::getTokenStr(op)+" not supported on the Int datatype");
  1148. + }
  1149. + } else {
  1150. + // use doubles
  1151. + double da = a->getDouble();
  1152. + double db = b->getDouble();
  1153. + switch (op) {
  1154. + case '+': return new CScriptVar(da+db);
  1155. + case '-': return new CScriptVar(da-db);
  1156. + case '*': return new CScriptVar(da*db);
  1157. + case '/': return new CScriptVar(da/db);
  1158. + case LEX_EQUAL: return new CScriptVar(da==db);
  1159. + case LEX_NEQUAL: return new CScriptVar(da!=db);
  1160. + case '<': return new CScriptVar(da<db);
  1161. + case LEX_LEQUAL: return new CScriptVar(da<=db);
  1162. + case '>': return new CScriptVar(da>db);
  1163. + case LEX_GEQUAL: return new CScriptVar(da>=db);
  1164. + default: throw new CScriptException("Operation "+CScriptLex::getTokenStr(op)+" not supported on the Double datatype");
  1165. + }
  1166. + }
  1167. + } else if (a->isArray()) {
  1168. + /* Just check pointers */
  1169. + switch (op) {
  1170. + case LEX_EQUAL: return new CScriptVar(a==b);
  1171. + case LEX_NEQUAL: return new CScriptVar(a!=b);
  1172. + default: throw new CScriptException("Operation "+CScriptLex::getTokenStr(op)+" not supported on the Array datatype");
  1173. + }
  1174. + } else if (a->isObject()) {
  1175. + /* Just check pointers */
  1176. + switch (op) {
  1177. + case LEX_EQUAL: return new CScriptVar(a==b);
  1178. + case LEX_NEQUAL: return new CScriptVar(a!=b);
  1179. + default: throw new CScriptException("Operation "+CScriptLex::getTokenStr(op)+" not supported on the Object datatype");
  1180. + }
  1181. + } else {
  1182. + string da = a->getString();
  1183. + string db = b->getString();
  1184. + // use strings
  1185. + switch (op) {
  1186. + case '+': return new CScriptVar(da+db, SCRIPTVAR_STRING);
  1187. + case LEX_EQUAL: return new CScriptVar(da==db);
  1188. + case LEX_NEQUAL: return new CScriptVar(da!=db);
  1189. + case '<': return new CScriptVar(da<db);
  1190. + case LEX_LEQUAL: return new CScriptVar(da<=db);
  1191. + case '>': return new CScriptVar(da>db);
  1192. + case LEX_GEQUAL: return new CScriptVar(da>=db);
  1193. + default: throw new CScriptException("Operation "+CScriptLex::getTokenStr(op)+" not supported on the string datatype");
  1194. + }
  1195. + }
  1196. + ASSERT(0);
  1197. + return 0;
  1198. +}
  1199. +
  1200. +void CScriptVar::copySimpleData(CScriptVar *val) {
  1201. + data = val->data;
  1202. + intData = val->intData;
  1203. + doubleData = val->doubleData;
  1204. + flags = (flags & ~SCRIPTVAR_VARTYPEMASK) | (val->flags & SCRIPTVAR_VARTYPEMASK);
  1205. +}
  1206. +
  1207. +void CScriptVar::copyValue(CScriptVar *val) {
  1208. + if (val) {
  1209. + copySimpleData(val);
  1210. + // remove all current children
  1211. + removeAllChildren();
  1212. + // copy children of 'val'
  1213. + CScriptVarLink *child = val->firstChild;
  1214. + while (child) {
  1215. + CScriptVar *copied;
  1216. + // don't copy the 'parent' object...
  1217. + if (child->name != TINYJS_PROTOTYPE_CLASS)
  1218. + copied = child->var->deepCopy();
  1219. + else
  1220. + copied = child->var;
  1221. +
  1222. + addChild(child->name, copied);
  1223. +
  1224. + child = child->nextSibling;
  1225. + }
  1226. + } else {
  1227. + setUndefined();
  1228. + }
  1229. +}
  1230. +
  1231. +CScriptVar *CScriptVar::deepCopy() {
  1232. + CScriptVar *newVar = new CScriptVar();
  1233. + newVar->copySimpleData(this);
  1234. + // copy children
  1235. + CScriptVarLink *child = firstChild;
  1236. + while (child) {
  1237. + CScriptVar *copied;
  1238. + // don't copy the 'parent' object...
  1239. + if (child->name != TINYJS_PROTOTYPE_CLASS)
  1240. + copied = child->var->deepCopy();
  1241. + else
  1242. + copied = child->var;
  1243. +
  1244. + newVar->addChild(child->name, copied);
  1245. + child = child->nextSibling;
  1246. + }
  1247. + return newVar;
  1248. +}
  1249. +
  1250. +void CScriptVar::trace(string indentStr, const string &name) {
  1251. + TRACE("%s'%s' = '%s' %s\n",
  1252. + indentStr.c_str(),
  1253. + name.c_str(),
  1254. + getString().c_str(),
  1255. + getFlagsAsString().c_str());
  1256. + string indent = indentStr+" ";
  1257. + CScriptVarLink *link = firstChild;
  1258. + while (link) {
  1259. + link->var->trace(indent, link->name);
  1260. + link = link->nextSibling;
  1261. + }
  1262. +}
  1263. +
  1264. +string CScriptVar::getFlagsAsString() {
  1265. + string flagstr = "";
  1266. + if (flags&SCRIPTVAR_FUNCTION) flagstr = flagstr + "FUNCTION ";
  1267. + if (flags&SCRIPTVAR_OBJECT) flagstr = flagstr + "OBJECT ";
  1268. + if (flags&SCRIPTVAR_ARRAY) flagstr = flagstr + "ARRAY ";
  1269. + if (flags&SCRIPTVAR_NATIVE) flagstr = flagstr + "NATIVE ";
  1270. + if (flags&SCRIPTVAR_DOUBLE) flagstr = flagstr + "DOUBLE ";
  1271. + if (flags&SCRIPTVAR_INTEGER) flagstr = flagstr + "INTEGER ";
  1272. + if (flags&SCRIPTVAR_STRING) flagstr = flagstr + "STRING ";
  1273. + return flagstr;
  1274. +}
  1275. +
  1276. +string CScriptVar::getParsableString() {
  1277. + // Numbers can just be put in directly
  1278. + if (isNumeric())
  1279. + return getString();
  1280. + if (isFunction()) {
  1281. + ostringstream funcStr;
  1282. + funcStr << "function (";
  1283. + // get list of parameters
  1284. + CScriptVarLink *link = firstChild;
  1285. + while (link) {
  1286. + funcStr << link->name;
  1287. + if (link->nextSibling) funcStr << ",";
  1288. + link = link->nextSibling;
  1289. + }
  1290. + // add function body
  1291. + funcStr << ") " << getString();
  1292. + return funcStr.str();
  1293. + }
  1294. + // if it is a string then we quote it
  1295. + if (isString())
  1296. + return getJSString(getString());
  1297. + if (isNull())
  1298. + return "null";
  1299. + return "undefined";
  1300. +}
  1301. +
  1302. +void CScriptVar::getJSON(ostringstream &destination, const string linePrefix) {
  1303. + if (isObject()) {
  1304. + string indentedLinePrefix = linePrefix+" ";
  1305. + // children - handle with bracketed list
  1306. + destination << "{ \n";
  1307. + CScriptVarLink *link = firstChild;
  1308. + while (link) {
  1309. + destination << indentedLinePrefix;
  1310. + destination << getJSString(link->name);
  1311. + destination << " : ";
  1312. + link->var->getJSON(destination, indentedLinePrefix);
  1313. + link = link->nextSibling;
  1314. + if (link) {
  1315. + destination << ",\n";
  1316. + }
  1317. + }
  1318. + destination << "\n" << linePrefix << "}";
  1319. + } else if (isArray()) {
  1320. + string indentedLinePrefix = linePrefix+" ";
  1321. + destination << "[\n";
  1322. + int len = getArrayLength();
  1323. + if (len>10000) len=10000; // we don't want to get stuck here!
  1324. +
  1325. + for (int i=0;i<len;i++) {
  1326. + getArrayIndex(i)->getJSON(destination, indentedLinePrefix);
  1327. + if (i<len-1) destination << ",\n";
  1328. + }
  1329. +
  1330. + destination << "\n" << linePrefix << "]";
  1331. + } else {
  1332. + // no children or a function... just write value directly
  1333. + destination << getParsableString();
  1334. + }
  1335. +}
  1336. +
  1337. +
  1338. +void CScriptVar::setCallback(JSCallback callback, void *userdata) {
  1339. + jsCallback = callback;
  1340. + jsCallbackUserData = userdata;
  1341. +}
  1342. +
  1343. +CScriptVar *CScriptVar::ref() {
  1344. + refs++;
  1345. + return this;
  1346. +}
  1347. +
  1348. +void CScriptVar::unref() {
  1349. + if (refs<=0) printf("OMFG, we have unreffed too far!\n");
  1350. + if ((--refs)==0) {
  1351. + delete this;
  1352. + }
  1353. +}
  1354. +
  1355. +int CScriptVar::getRefs() {
  1356. + return refs;
  1357. +}
  1358. +
  1359. +
  1360. +// ----------------------------------------------------------------------------------- CSCRIPT
  1361. +
  1362. +CTinyJS::CTinyJS() {
  1363. + l = 0;
  1364. + root = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref();
  1365. + // Add built-in classes
  1366. + stringClass = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref();
  1367. + arrayClass = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref();
  1368. + objectClass = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref();
  1369. + root->addChild("String", stringClass);
  1370. + root->addChild("Array", arrayClass);
  1371. + root->addChild("Object", objectClass);
  1372. +}
  1373. +
  1374. +CTinyJS::~CTinyJS() {
  1375. + ASSERT(!l);
  1376. + scopes.clear();
  1377. + stringClass->unref();
  1378. + arrayClass->unref();
  1379. + objectClass->unref();
  1380. + root->unref();
  1381. +
  1382. +#if DEBUG_MEMORY
  1383. + show_allocated();
  1384. +#endif
  1385. +}
  1386. +
  1387. +void CTinyJS::trace() {
  1388. + root->trace();
  1389. +}
  1390. +
  1391. +void CTinyJS::execute(const string &code) {
  1392. + CScriptLex *oldLex = l;
  1393. + vector<CScriptVar*> oldScopes = scopes;
  1394. + l = new CScriptLex(code);
  1395. +#ifdef TINYJS_CALL_STACK
  1396. + call_stack.clear();
  1397. +#endif
  1398. + scopes.clear();
  1399. + scopes.push_back(root);
  1400. + try {
  1401. + bool execute = true;
  1402. + while (l->tk) statement(execute);
  1403. + } catch (CScriptException *e) {
  1404. + ostringstream msg;
  1405. + msg << "Error " << e->text;
  1406. +#ifdef TINYJS_CALL_STACK
  1407. + for (int i=(int)call_stack.size()-1;i>=0;i--)
  1408. + msg << "\n" << i << ": " << call_stack.at(i);
  1409. +#endif
  1410. + msg << " at " << l->getPosition();
  1411. + delete l;
  1412. + l = oldLex;
  1413. +
  1414. + throw new CScriptException(msg.str());
  1415. + }
  1416. + delete l;
  1417. + l = oldLex;
  1418. + scopes = oldScopes;
  1419. +}
  1420. +
  1421. +CScriptVarLink CTinyJS::evaluateComplex(const string &code) {
  1422. + CScriptLex *oldLex = l;
  1423. + vector<CScriptVar*> oldScopes = scopes;
  1424. +
  1425. + l = new CScriptLex(code);
  1426. +#ifdef TINYJS_CALL_STACK
  1427. + call_stack.clear();
  1428. +#endif
  1429. + scopes.clear();
  1430. + scopes.push_back(root);
  1431. + CScriptVarLink *v = 0;
  1432. + try {
  1433. + bool execute = true;
  1434. + do {
  1435. + CLEAN(v);
  1436. + v = base(execute);
  1437. + if (l->tk!=LEX_EOF) l->match(';');
  1438. + } while (l->tk!=LEX_EOF);
  1439. + } catch (CScriptException *e) {
  1440. + ostringstream msg;
  1441. + msg << "Error " << e->text;
  1442. +#ifdef TINYJS_CALL_STACK
  1443. + for (int i=(int)call_stack.size()-1;i>=0;i--)
  1444. + msg << "\n" << i << ": " << call_stack.at(i);
  1445. +#endif
  1446. + msg << " at " << l->getPosition();
  1447. + delete l;
  1448. + l = oldLex;
  1449. +
  1450. + throw new CScriptException(msg.str());
  1451. + }
  1452. + delete l;
  1453. + l = oldLex;
  1454. + scopes = oldScopes;
  1455. +
  1456. + if (v) {
  1457. + CScriptVarLink r = *v;
  1458. + CLEAN(v);
  1459. + return r;
  1460. + }
  1461. + // return undefined...
  1462. + return CScriptVarLink(new CScriptVar());
  1463. +}
  1464. +
  1465. +string CTinyJS::evaluate(const string &code) {
  1466. + return evaluateComplex(code).var->getString();
  1467. +}
  1468. +
  1469. +void CTinyJS::parseFunctionArguments(CScriptVar *funcVar) {
  1470. + l->match('(');
  1471. + while (l->tk!=')') {
  1472. + funcVar->addChildNoDup(l->tkStr);
  1473. + l->match(LEX_ID);
  1474. + if (l->tk!=')') l->match(',');
  1475. + }
  1476. + l->match(')');
  1477. +}
  1478. +
  1479. +void CTinyJS::addNative(const string &funcDesc, JSCallback ptr, void *userdata) {
  1480. + CScriptLex *oldLex = l;
  1481. + l = new CScriptLex(funcDesc);
  1482. +
  1483. + CScriptVar *base = root;
  1484. +
  1485. + l->match(LEX_R_FUNCTION);
  1486. + string funcName = l->tkStr;
  1487. + l->match(LEX_ID);
  1488. + /* Check for dots, we might want to do something like function String.substring ... */
  1489. + while (l->tk == '.') {
  1490. + l->match('.');
  1491. + CScriptVarLink *link = base->findChild(funcName);
  1492. + // if it doesn't exist, make an object class
  1493. + if (!link) link = base->addChild(funcName, new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT));
  1494. + base = link->var;
  1495. + funcName = l->tkStr;
  1496. + l->match(LEX_ID);
  1497. + }
  1498. +
  1499. + CScriptVar *funcVar = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION | SCRIPTVAR_NATIVE);
  1500. + funcVar->setCallback(ptr, userdata);
  1501. + parseFunctionArguments(funcVar);
  1502. + delete l;
  1503. + l = oldLex;
  1504. +
  1505. + base->addChild(funcName, funcVar);
  1506. +}
  1507. +
  1508. +CScriptVarLink *CTinyJS::parseFunctionDefinition() {
  1509. + // actually parse a function...
  1510. + l->match(LEX_R_FUNCTION);
  1511. + string funcName = TINYJS_TEMP_NAME;
  1512. + /* we can have functions without names */
  1513. + if (l->tk==LEX_ID) {
  1514. + funcName = l->tkStr;
  1515. + l->match(LEX_ID);
  1516. + }
  1517. + CScriptVarLink *funcVar = new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION), funcName);
  1518. + parseFunctionArguments(funcVar->var);
  1519. + int funcBegin = l->tokenStart;
  1520. + bool noexecute = false;
  1521. + block(noexecute);
  1522. + funcVar->var->data = l->getSubString(funcBegin);
  1523. + return funcVar;
  1524. +}
  1525. +
  1526. +/** Handle a function call (assumes we've parsed the function name and we're
  1527. + * on the start bracket). 'parent' is the object that contains this method,
  1528. + * if there was one (otherwise it's just a normnal function).
  1529. + */
  1530. +CScriptVarLink *CTinyJS::functionCall(bool &execute, CScriptVarLink *function, CScriptVar *parent) {
  1531. + if (execute) {
  1532. + if (!function->var->isFunction()) {
  1533. + string errorMsg = "Expecting '";
  1534. + errorMsg = errorMsg + function->name + "' to be a function";
  1535. + throw new CScriptException(errorMsg.c_str());
  1536. + }
  1537. + l->match('(');
  1538. + // create a new symbol table entry for execution of this function
  1539. + CScriptVar *functionRoot = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION);
  1540. + if (parent)
  1541. + functionRoot->addChildNoDup("this", parent);
  1542. + // grab in all parameters
  1543. + CScriptVarLink *v = function->var->firstChild;
  1544. + while (v) {
  1545. + CScriptVarLink *value = base(execute);
  1546. + if (execute) {
  1547. + if (value->var->isBasic()) {
  1548. + // pass by value
  1549. + functionRoot->addChild(v->name, value->var->deepCopy());
  1550. + } else {
  1551. + // pass by reference
  1552. + functionRoot->addChild(v->name, value->var);
  1553. + }
  1554. + }
  1555. + CLEAN(value);
  1556. + if (l->tk!=')') l->match(',');
  1557. + v = v->nextSibling;
  1558. + }
  1559. + l->match(')');
  1560. + // setup a return variable
  1561. + CScriptVarLink *returnVar = NULL;
  1562. + // execute function!
  1563. + // add the function's execute space to the symbol table so we can recurse
  1564. + CScriptVarLink *returnVarLink = functionRoot->addChild(TINYJS_RETURN_VAR);
  1565. + scopes.push_back(functionRoot);
  1566. +#ifdef TINYJS_CALL_STACK
  1567. + call_stack.push_back(function->name + " from " + l->getPosition());
  1568. +#endif
  1569. +
  1570. + if (function->var->isNative()) {
  1571. + ASSERT(function->var->jsCallback);
  1572. + function->var->jsCallback(functionRoot, function->var->jsCallbackUserData);
  1573. + } else {
  1574. + /* we just want to execute the block, but something could
  1575. + * have messed up and left us with the wrong ScriptLex, so
  1576. + * we want to be careful here... */
  1577. + CScriptException *exception = 0;
  1578. + CScriptLex *oldLex = l;
  1579. + CScriptLex *newLex = new CScriptLex(function->var->getString());
  1580. + l = newLex;
  1581. + try {
  1582. + block(execute);
  1583. + // because return will probably have called this, and set execute to false
  1584. + execute = true;
  1585. + } catch (CScriptException *e) {
  1586. + exception = e;
  1587. + }
  1588. + delete newLex;
  1589. + l = oldLex;
  1590. +
  1591. + if (exception)
  1592. + throw exception;
  1593. + }
  1594. +#ifdef TINYJS_CALL_STACK
  1595. + if (!call_stack.empty()) call_stack.pop_back();
  1596. +#endif
  1597. + scopes.pop_back();
  1598. + /* get the real return var before we remove it from our function */
  1599. + returnVar = new CScriptVarLink(returnVarLink->var);
  1600. + functionRoot->removeLink(returnVarLink);
  1601. + delete functionRoot;
  1602. + if (returnVar)
  1603. + return returnVar;
  1604. + else
  1605. + return new CScriptVarLink(new CScriptVar());
  1606. + } else {
  1607. + // function, but not executing - just parse args and be done
  1608. + l->match('(');
  1609. + while (l->tk != ')') {
  1610. + CScriptVarLink *value = base(execute);
  1611. + CLEAN(value);
  1612. + if (l->tk!=')') l->match(',');
  1613. + }
  1614. + l->match(')');
  1615. + if (l->tk == '{') { // TODO: why is this here?
  1616. + block(execute);
  1617. + }
  1618. + /* function will be a blank scriptvarlink if we're not executing,
  1619. + * so just return it rather than an alloc/free */
  1620. + return function;
  1621. + }
  1622. +}
  1623. +
  1624. +CScriptVarLink *CTinyJS::factor(bool &execute) {
  1625. + if (l->tk=='(') {
  1626. + l->match('(');
  1627. + CScriptVarLink *a = base(execute);
  1628. + l->match(')');
  1629. + return a;
  1630. + }
  1631. + if (l->tk==LEX_R_TRUE) {
  1632. + l->match(LEX_R_TRUE);
  1633. + return new CScriptVarLink(new CScriptVar(1));
  1634. + }
  1635. + if (l->tk==LEX_R_FALSE) {
  1636. + l->match(LEX_R_FALSE);
  1637. + return new CScriptVarLink(new CScriptVar(0));
  1638. + }
  1639. + if (l->tk==LEX_R_NULL) {
  1640. + l->match(LEX_R_NULL);
  1641. + return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_NULL));
  1642. + }
  1643. + if (l->tk==LEX_R_UNDEFINED) {
  1644. + l->match(LEX_R_UNDEFINED);
  1645. + return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_UNDEFINED));
  1646. + }
  1647. + if (l->tk==LEX_ID) {
  1648. + CScriptVarLink *a = execute ? findInScopes(l->tkStr) : new CScriptVarLink(new CScriptVar());
  1649. + //printf("0x%08X for %s at %s\n", (unsigned int)a, l->tkStr.c_str(), l->getPosition().c_str());
  1650. + /* The parent if we're executing a method call */
  1651. + CScriptVar *parent = 0;
  1652. +
  1653. + if (execute && !a) {
  1654. + /* Variable doesn't exist! JavaScript says we should create it
  1655. + * (we won't add it here. This is done in the assignment operator)*/
  1656. + a = new CScriptVarLink(new CScriptVar(), l->tkStr);
  1657. + }
  1658. + l->match(LEX_ID);
  1659. + while (l->tk=='(' || l->tk=='.' || l->tk=='[') {
  1660. + if (l->tk=='(') { // ------------------------------------- Function Call
  1661. + a = functionCall(execute, a, parent);
  1662. + } else if (l->tk == '.') { // ------------------------------------- Record Access
  1663. + l->match('.');
  1664. + if (execute) {
  1665. + const string &name = l->tkStr;
  1666. + CScriptVarLink *child = a->var->findChild(name);
  1667. + if (!child) child = findInParentClasses(a->var, name);
  1668. + if (!child) {
  1669. + /* if we haven't found this defined yet, use the built-in
  1670. + 'length' properly */
  1671. + if (a->var->isArray() && name == "length") {
  1672. + int l = a->var->getArrayLength();
  1673. + child = new CScriptVarLink(new CScriptVar(l));
  1674. + } else if (a->var->isString() && name == "length") {
  1675. + int l = a->var->getString().size();
  1676. + child = new CScriptVarLink(new CScriptVar(l));
  1677. + } else {
  1678. + child = a->var->addChild(name);
  1679. + }
  1680. + }
  1681. + parent = a->var;
  1682. + a = child;
  1683. + }
  1684. + l->match(LEX_ID);
  1685. + } else if (l->tk == '[') { // ------------------------------------- Array Access
  1686. + l->match('[');
  1687. + CScriptVarLink *index = base(execute);
  1688. + l->match(']');
  1689. + if (execute) {
  1690. + CScriptVarLink *child = a->var->findChildOrCreate(index->var->getString());
  1691. + parent = a->var;
  1692. + a = child;
  1693. + }
  1694. + CLEAN(index);
  1695. + } else ASSERT(0);
  1696. + }
  1697. + return a;
  1698. + }
  1699. + if (l->tk==LEX_INT || l->tk==LEX_FLOAT) {
  1700. + CScriptVar *a = new CScriptVar(l->tkStr,
  1701. + ((l->tk==LEX_INT)?SCRIPTVAR_INTEGER:SCRIPTVAR_DOUBLE));
  1702. + l->match(l->tk);
  1703. + return new CScriptVarLink(a);
  1704. + }
  1705. + if (l->tk==LEX_STR) {
  1706. + CScriptVar *a = new CScriptVar(l->tkStr, SCRIPTVAR_STRING);
  1707. + l->match(LEX_STR);
  1708. + return new CScriptVarLink(a);
  1709. + }
  1710. + if (l->tk=='{') {
  1711. + CScriptVar *contents = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT);
  1712. + /* JSON-style object definition */
  1713. + l->match('{');
  1714. + while (l->tk != '}') {
  1715. + string id = l->tkStr;
  1716. + // we only allow strings or IDs on the left hand side of an initialisation
  1717. + if (l->tk==LEX_STR) l->match(LEX_STR);
  1718. + else l->match(LEX_ID);
  1719. + l->match(':');
  1720. + if (execute) {
  1721. + CScriptVarLink *a = base(execute);
  1722. + contents->addChild(id, a->var);
  1723. + CLEAN(a);
  1724. + }
  1725. + // no need to clean here, as it will definitely be used
  1726. + if (l->tk != '}') l->match(',');
  1727. + }
  1728. +
  1729. + l->match('}');
  1730. + return new CScriptVarLink(contents);
  1731. + }
  1732. + if (l->tk=='[') {
  1733. + CScriptVar *contents = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_ARRAY);
  1734. + /* JSON-style array */
  1735. + l->match('[');
  1736. + int idx = 0;
  1737. + while (l->tk != ']') {
  1738. + if (execute) {
  1739. + char idx_str[16]; // big enough for 2^32
  1740. + sprintf_s(idx_str, sizeof(idx_str), "%d",idx);
  1741. +
  1742. + CScriptVarLink *a = base(execute);
  1743. + contents->addChild(idx_str, a->var);
  1744. + CLEAN(a);
  1745. + }
  1746. + // no need to clean here, as it will definitely be used
  1747. + if (l->tk != ']') l->match(',');
  1748. + idx++;
  1749. + }
  1750. + l->match(']');
  1751. + return new CScriptVarLink(contents);
  1752. + }
  1753. + if (l->tk==LEX_R_FUNCTION) {
  1754. + CScriptVarLink *funcVar = parseFunctionDefinition();
  1755. + if (funcVar->name != TINYJS_TEMP_NAME)
  1756. + TRACE("Functions not defined at statement-level are not meant to have a name");
  1757. + return funcVar;
  1758. + }
  1759. + if (l->tk==LEX_R_NEW) {
  1760. + // new -> create a new object
  1761. + l->match(LEX_R_NEW);
  1762. + const string &className = l->tkStr;
  1763. + if (execute) {
  1764. + CScriptVarLink *objClassOrFunc = findInScopes(className);
  1765. + if (!objClassOrFunc) {
  1766. + TRACE("%s is not a valid class name", className.c_str());
  1767. + return new CScriptVarLink(new CScriptVar());
  1768. + }
  1769. + l->match(LEX_ID);
  1770. + CScriptVar *obj = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT);
  1771. + CScriptVarLink *objLink = new CScriptVarLink(obj);
  1772. + if (objClassOrFunc->var->isFunction()) {
  1773. + CLEAN(functionCall(execute, objClassOrFunc, obj));
  1774. + } else {
  1775. + obj->addChild(TINYJS_PROTOTYPE_CLASS, objClassOrFunc->var);
  1776. + if (l->tk == '(') {
  1777. + l->match('(');
  1778. + l->match(')');
  1779. + }
  1780. + }
  1781. + return objLink;
  1782. + } else {
  1783. + l->match(LEX_ID);
  1784. + if (l->tk == '(') {
  1785. + l->match('(');
  1786. + l->match(')');
  1787. + }
  1788. + }
  1789. + }
  1790. + // Nothing we can do here... just hope it's the end...
  1791. + l->match(LEX_EOF);
  1792. + return 0;
  1793. +}
  1794. +
  1795. +CScriptVarLink *CTinyJS::unary(bool &execute) {
  1796. + CScriptVarLink *a;
  1797. + if (l->tk=='!') {
  1798. + l->match('!'); // binary not
  1799. + a = factor(execute);
  1800. + if (execute) {
  1801. + CScriptVar zero(0);
  1802. + CScriptVar *res = a->var->mathsOp(&zero, LEX_EQUAL);
  1803. + CREATE_LINK(a, res);
  1804. + }
  1805. + } else
  1806. + a = factor(execute);
  1807. + return a;
  1808. +}
  1809. +
  1810. +CScriptVarLink *CTinyJS::term(bool &execute) {
  1811. + CScriptVarLink *a = unary(execute);
  1812. + while (l->tk=='*' || l->tk=='/' || l->tk=='%') {
  1813. + int op = l->tk;
  1814. + l->match(l->tk);
  1815. + CScriptVarLink *b = unary(execute);
  1816. + if (execute) {
  1817. + CScriptVar *res = a->var->mathsOp(b->var, op);
  1818. + CREATE_LINK(a, res);
  1819. + }
  1820. + CLEAN(b);
  1821. + }
  1822. + return a;
  1823. +}
  1824. +
  1825. +CScriptVarLink *CTinyJS::expression(bool &execute) {
  1826. + bool negate = false;
  1827. + if (l->tk=='-') {
  1828. + l->match('-');
  1829. + negate = true;
  1830. + }
  1831. + CScriptVarLink *a = term(execute);
  1832. + if (negate) {
  1833. + CScriptVar zero(0);
  1834. + CScriptVar *res = zero.mathsOp(a->var, '-');
  1835. + CREATE_LINK(a, res);
  1836. + }
  1837. +
  1838. + while (l->tk=='+' || l->tk=='-' ||
  1839. + l->tk==LEX_PLUSPLUS || l->tk==LEX_MINUSMINUS) {
  1840. + int op = l->tk;
  1841. + l->match(l->tk);
  1842. + if (op==LEX_PLUSPLUS || op==LEX_MINUSMINUS) {
  1843. + if (execute) {
  1844. + CScriptVar one(1);
  1845. + CScriptVar *res = a->var->mathsOp(&one, op==LEX_PLUSPLUS ? '+' : '-');
  1846. + CScriptVarLink *oldValue = new CScriptVarLink(a->var);
  1847. + // in-place add/subtract
  1848. + a->replaceWith(res);
  1849. + CLEAN(a);
  1850. + a = oldValue;
  1851. + }
  1852. + } else {
  1853. + CScriptVarLink *b = term(execute);
  1854. + if (execute) {
  1855. + // not in-place, so just replace
  1856. + CScriptVar *res = a->var->mathsOp(b->var, op);
  1857. + CREATE_LINK(a, res);
  1858. + }
  1859. + CLEAN(b);
  1860. + }
  1861. + }
  1862. + return a;
  1863. +}
  1864. +
  1865. +CScriptVarLink *CTinyJS::shift(bool &execute) {
  1866. + CScriptVarLink *a = expression(execute);
  1867. + if (l->tk==LEX_LSHIFT || l->tk==LEX_RSHIFT || l->tk==LEX_RSHIFTUNSIGNED) {
  1868. + int op = l->tk;
  1869. + l->match(op);
  1870. + CScriptVarLink *b = base(execute);
  1871. + int shift = execute ? b->var->getInt() : 0;
  1872. + CLEAN(b);
  1873. + if (execute) {
  1874. + if (op==LEX_LSHIFT) a->var->setInt(a->var->getInt() << shift);
  1875. + if (op==LEX_RSHIFT) a->var->setInt(a->var->getInt() >> shift);
  1876. + if (op==LEX_RSHIFTUNSIGNED) a->var->setInt(((unsigned int)a->var->getInt()) >> shift);
  1877. + }
  1878. + }
  1879. + return a;
  1880. +}
  1881. +
  1882. +CScriptVarLink *CTinyJS::condition(bool &execute) {
  1883. + CScriptVarLink *a = shift(execute);
  1884. + CScriptVarLink *b;
  1885. + while (l->tk==LEX_EQUAL || l->tk==LEX_NEQUAL ||
  1886. + l->tk==LEX_TYPEEQUAL || l->tk==LEX_NTYPEEQUAL ||
  1887. + l->tk==LEX_LEQUAL || l->tk==LEX_GEQUAL ||
  1888. + l->tk=='<' || l->tk=='>') {
  1889. + int op = l->tk;
  1890. + l->match(l->tk);
  1891. + b = shift(execute);
  1892. + if (execute) {
  1893. + CScriptVar *res = a->var->mathsOp(b->var, op);
  1894. + CREATE_LINK(a,res);
  1895. + }
  1896. + CLEAN(b);
  1897. + }
  1898. + return a;
  1899. +}
  1900. +
  1901. +CScriptVarLink *CTinyJS::logic(bool &execute) {
  1902. + CScriptVarLink *a = condition(execute);
  1903. + CScriptVarLink *b;
  1904. + while (l->tk=='&' || l->tk=='|' || l->tk=='^' || l->tk==LEX_ANDAND || l->tk==LEX_OROR) {
  1905. + bool noexecute = false;
  1906. + int op = l->tk;
  1907. + l->match(l->tk);
  1908. + bool shortCircuit = false;
  1909. + bool boolean = false;
  1910. + // if we have short-circuit ops, then if we know the outcome
  1911. + // we don't bother to execute the other op. Even if not
  1912. + // we need to tell mathsOp it's an & or |
  1913. + if (op==LEX_ANDAND) {
  1914. + op = '&';
  1915. + shortCircuit = !a->var->getBool();
  1916. + boolean = true;
  1917. + } else if (op==LEX_OROR) {
  1918. + op = '|';
  1919. + shortCircuit = a->var->getBool();
  1920. + boolean = true;
  1921. + }
  1922. + b = condition(shortCircuit ? noexecute : execute);
  1923. + if (execute && !shortCircuit) {
  1924. + if (boolean) {
  1925. + CScriptVar *newa = new CScriptVar(a->var->getBool());
  1926. + CScriptVar *newb = new CScriptVar(b->var->getBool());
  1927. + CREATE_LINK(a, newa);
  1928. + CREATE_LINK(b, newb);
  1929. + }
  1930. + CScriptVar *res = a->var->mathsOp(b->var, op);
  1931. + CREATE_LINK(a, res);
  1932. + }
  1933. + CLEAN(b);
  1934. + }
  1935. + return a;
  1936. +}
  1937. +
  1938. +CScriptVarLink *CTinyJS::ternary(bool &execute) {
  1939. + CScriptVarLink *lhs = logic(execute);
  1940. + bool noexec = false;
  1941. + if (l->tk=='?') {
  1942. + l->match('?');
  1943. + if (!execute) {
  1944. + CLEAN(lhs);
  1945. + CLEAN(base(noexec));
  1946. + l->match(':');
  1947. + CLEAN(base(noexec));
  1948. + } else {
  1949. + bool first = lhs->var->getBool();
  1950. + CLEAN(lhs);
  1951. + if (first) {
  1952. + lhs = base(execute);
  1953. + l->match(':');
  1954. + CLEAN(base(noexec));
  1955. + } else {
  1956. + CLEAN(base(noexec));
  1957. + l->match(':');
  1958. + lhs = base(execute);
  1959. + }
  1960. + }
  1961. + }
  1962. +
  1963. + return lhs;
  1964. +}
  1965. +
  1966. +CScriptVarLink *CTinyJS::base(bool &execute) {
  1967. + CScriptVarLink *lhs = ternary(execute);
  1968. + if (l->tk=='=' || l->tk==LEX_PLUSEQUAL || l->tk==LEX_MINUSEQUAL) {
  1969. + /* If we're assigning to this and we don't have a parent,
  1970. + * add it to the symbol table root as per JavaScript. */
  1971. + if (execute && !lhs->owned) {
  1972. + if (lhs->name.length()>0) {
  1973. + CScriptVarLink *realLhs = root->addChildNoDup(lhs->name, lhs->var);
  1974. + CLEAN(lhs);
  1975. + lhs = realLhs;
  1976. + } else
  1977. + TRACE("Trying to assign to an un-named type\n");
  1978. + }
  1979. +
  1980. + int op = l->tk;
  1981. + l->match(l->tk);
  1982. + CScriptVarLink *rhs = base(execute);
  1983. + if (execute) {
  1984. + if (op=='=') {
  1985. + lhs->replaceWith(rhs);
  1986. + } else if (op==LEX_PLUSEQUAL) {
  1987. + CScriptVar *res = lhs->var->mathsOp(rhs->var, '+');
  1988. + lhs->replaceWith(res);
  1989. + } else if (op==LEX_MINUSEQUAL) {
  1990. + CScriptVar *res = lhs->var->mathsOp(rhs->var, '-');
  1991. + lhs->replaceWith(res);
  1992. + } else ASSERT(0);
  1993. + }
  1994. + CLEAN(rhs);
  1995. + }
  1996. + return lhs;
  1997. +}
  1998. +
  1999. +void CTinyJS::block(bool &execute) {
  2000. + l->match('{');
  2001. + if (execute) {
  2002. + while (l->tk && l->tk!='}')
  2003. + statement(execute);
  2004. + l->match('}');
  2005. + } else {
  2006. + // fast skip of blocks
  2007. + int brackets = 1;
  2008. + while (l->tk && brackets) {
  2009. + if (l->tk == '{') brackets++;
  2010. + if (l->tk == '}') brackets--;
  2011. + l->match(l->tk);
  2012. + }
  2013. + }
  2014. +
  2015. +}
  2016. +
  2017. +void CTinyJS::statement(bool &execute) {
  2018. + if (l->tk==LEX_ID ||
  2019. + l->tk==LEX_INT ||
  2020. + l->tk==LEX_FLOAT ||
  2021. + l->tk==LEX_STR ||
  2022. + l->tk=='-') {
  2023. + /* Execute a simple statement that only contains basic arithmetic... */
  2024. + CLEAN(base(execute));
  2025. + l->match(';');
  2026. + } else if (l->tk=='{') {
  2027. + /* A block of code */
  2028. + block(execute);
  2029. + } else if (l->tk==';') {
  2030. + /* Empty statement - to allow things like ;;; */
  2031. + l->match(';');
  2032. + } else if (l->tk==LEX_R_VAR) {
  2033. + /* variable creation. TODO - we need a better way of parsing the left
  2034. + * hand side. Maybe just have a flag called can_create_var that we
  2035. + * set and then we parse as if we're doing a normal equals.*/
  2036. + l->match(LEX_R_VAR);
  2037. + while (l->tk != ';') {
  2038. + CScriptVarLink *a = 0;
  2039. + if (execute)
  2040. + a = scopes.back()->findChildOrCreate(l->tkStr);
  2041. + l->match(LEX_ID);
  2042. + // now do stuff defined with dots
  2043. + while (l->tk == '.') {
  2044. + l->match('.');
  2045. + if (execute) {
  2046. + CScriptVarLink *lastA = a;
  2047. + a = lastA->var->findChildOrCreate(l->tkStr);
  2048. + }
  2049. + l->match(LEX_ID);
  2050. + }
  2051. + // sort out initialiser
  2052. + if (l->tk == '=') {
  2053. + l->match('=');
  2054. + CScriptVarLink *var = base(execute);
  2055. + if (execute)
  2056. + a->replaceWith(var);
  2057. + CLEAN(var);
  2058. + }
  2059. + if (l->tk != ';')
  2060. + l->match(',');
  2061. + }
  2062. + l->match(';');
  2063. + } else if (l->tk==LEX_R_IF) {
  2064. + l->match(LEX_R_IF);
  2065. + l->match('(');
  2066. + CScriptVarLink *var = base(execute);
  2067. + l->match(')');
  2068. + bool cond = execute && var->var->getBool();
  2069. + CLEAN(var);
  2070. + bool noexecute = false; // because we need to be abl;e to write to it
  2071. + statement(cond ? execute : noexecute);
  2072. + if (l->tk==LEX_R_ELSE) {
  2073. + l->match(LEX_R_ELSE);
  2074. + statement(cond ? noexecute : execute);
  2075. + }
  2076. + } else if (l->tk==LEX_R_WHILE) {
  2077. + // We do repetition by pulling out the string representing our statement
  2078. + // there's definitely some opportunity for optimisation here
  2079. + l->match(LEX_R_WHILE);
  2080. + l->match('(');
  2081. + int whileCondStart = l->tokenStart;
  2082. + bool noexecute = false;
  2083. + CScriptVarLink *cond = base(execute);
  2084. + bool loopCond = execute && cond->var->getBool();
  2085. + CLEAN(cond);
  2086. + CScriptLex *whileCond = l->getSubLex(whileCondStart);
  2087. + l->match(')');
  2088. + int whileBodyStart = l->tokenStart;
  2089. + statement(loopCond ? execute : noexecute);
  2090. + CScriptLex *whileBody = l->getSubLex(whileBodyStart);
  2091. + CScriptLex *oldLex = l;
  2092. + int loopCount = TINYJS_LOOP_MAX_ITERATIONS;
  2093. + while (loopCond && loopCount-->0) {
  2094. + whileCond->reset();
  2095. + l = whileCond;
  2096. + cond = base(execute);
  2097. + loopCond = execute && cond->var->getBool();
  2098. + CLEAN(cond);
  2099. + if (loopCond) {
  2100. + whileBody->reset();
  2101. + l = whileBody;
  2102. + statement(execute);
  2103. + }
  2104. + }
  2105. + l = oldLex;
  2106. + delete whileCond;
  2107. + delete whileBody;
  2108. +
  2109. + if (loopCount<=0) {
  2110. + root->trace();
  2111. + TRACE("WHILE Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition().c_str());
  2112. + throw new CScriptException("LOOP_ERROR");
  2113. + }
  2114. + } else if (l->tk==LEX_R_FOR) {
  2115. + l->match(LEX_R_FOR);
  2116. + l->match('(');
  2117. + statement(execute); // initialisation
  2118. + //l->match(';');
  2119. + int forCondStart = l->tokenStart;
  2120. + bool noexecute = false;
  2121. + CScriptVarLink *cond = base(execute); // condition
  2122. + bool loopCond = execute && cond->var->getBool();
  2123. + CLEAN(cond);
  2124. + CScriptLex *forCond = l->getSubLex(forCondStart);
  2125. + l->match(';');
  2126. + int forIterStart = l->tokenStart;
  2127. + CLEAN(base(noexecute)); // iterator
  2128. + CScriptLex *forIter = l->getSubLex(forIterStart);
  2129. + l->match(')');
  2130. + int forBodyStart = l->tokenStart;
  2131. + statement(loopCond ? execute : noexecute);
  2132. + CScriptLex *forBody = l->getSubLex(forBodyStart);
  2133. + CScriptLex *oldLex = l;
  2134. + if (loopCond) {
  2135. + forIter->reset();
  2136. + l = forIter;
  2137. + CLEAN(base(execute));
  2138. + }
  2139. + int loopCount = TINYJS_LOOP_MAX_ITERATIONS;
  2140. + while (execute && loopCond && loopCount-->0) {
  2141. + forCond->reset();
  2142. + l = forCond;
  2143. + cond = base(execute);
  2144. + loopCond = cond->var->getBool();
  2145. + CLEAN(cond);
  2146. + if (execute && loopCond) {
  2147. + forBody->reset();
  2148. + l = forBody;
  2149. + statement(execute);
  2150. + }
  2151. + if (execute && loopCond) {
  2152. + forIter->reset();
  2153. + l = forIter;
  2154. + CLEAN(base(execute));
  2155. + }
  2156. + }
  2157. + l = oldLex;
  2158. + delete forCond;
  2159. + delete forIter;
  2160. + delete forBody;
  2161. + if (loopCount<=0) {
  2162. + root->trace();
  2163. + TRACE("FOR Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition().c_str());
  2164. + throw new CScriptException("LOOP_ERROR");
  2165. + }
  2166. + } else if (l->tk==LEX_R_RETURN) {
  2167. + l->match(LEX_R_RETURN);
  2168. + CScriptVarLink *result = 0;
  2169. + if (l->tk != ';')
  2170. + result = base(execute);
  2171. + if (execute) {
  2172. + CScriptVarLink *resultVar = scopes.back()->findChild(TINYJS_RETURN_VAR);
  2173. + if (resultVar)
  2174. + resultVar->replaceWith(result);
  2175. + else
  2176. + TRACE("RETURN statement, but not in a function.\n");
  2177. + execute = false;
  2178. + }
  2179. + CLEAN(result);
  2180. + l->match(';');
  2181. + } else if (l->tk==LEX_R_FUNCTION) {
  2182. + CScriptVarLink *funcVar = parseFunctionDefinition();
  2183. + if (execute) {
  2184. + if (funcVar->name == TINYJS_TEMP_NAME)
  2185. + TRACE("Functions defined at statement-level are meant to have a name\n");
  2186. + else
  2187. + scopes.back()->addChildNoDup(funcVar->name, funcVar->var);
  2188. + }
  2189. + CLEAN(funcVar);
  2190. + } else l->match(LEX_EOF);
  2191. +}
  2192. +
  2193. +/// Get the given variable specified by a path (var1.var2.etc), or return 0
  2194. +CScriptVar *CTinyJS::getScriptVariable(const string &path) {
  2195. + // traverse path
  2196. + size_t prevIdx = 0;
  2197. + size_t thisIdx = path.find('.');
  2198. + if (thisIdx == string::npos) thisIdx = path.length();
  2199. + CScriptVar *var = root;
  2200. + while (var && prevIdx<path.length()) {
  2201. + string el = path.substr(prevIdx, thisIdx-prevIdx);
  2202. + CScriptVarLink *varl = var->findChild(el);
  2203. + var = varl?varl->var:0;
  2204. + prevIdx = thisIdx+1;
  2205. + thisIdx = path.find('.', prevIdx);
  2206. + if (thisIdx == string::npos) thisIdx = path.length();
  2207. + }
  2208. + return var;
  2209. +}
  2210. +
  2211. +/// Get the value of the given variable, or return 0
  2212. +const string *CTinyJS::getVariable(const string &path) {
  2213. + CScriptVar *var = getScriptVariable(path);
  2214. + // return result
  2215. + if (var)
  2216. + return &var->getString();
  2217. + else
  2218. + return 0;
  2219. +}
  2220. +
  2221. +/// set the value of the given variable, return trur if it exists and gets set
  2222. +bool CTinyJS::setVariable(const std::string &path, const std::string &varData) {
  2223. + CScriptVar *var = getScriptVariable(path);
  2224. + // return result
  2225. + if (var) {
  2226. + if (var->isInt())
  2227. + var->setInt((int)strtol(varData.c_str(),0,0));
  2228. + else if (var->isDouble())
  2229. + var->setDouble(strtod(varData.c_str(),0));
  2230. + else
  2231. + var->setString(varData.c_str());
  2232. + return true;
  2233. + }
  2234. + else
  2235. + return false;
  2236. +}
  2237. +
  2238. +/// Finds a child, looking recursively up the scopes
  2239. +CScriptVarLink *CTinyJS::findInScopes(const std::string &childName) {
  2240. + for (int s=scopes.size()-1;s>=0;s--) {
  2241. + CScriptVarLink *v = scopes[s]->findChild(childName);
  2242. + if (v) return v;
  2243. + }
  2244. + return NULL;
  2245. +
  2246. +}
  2247. +
  2248. +/// Look up in any parent classes of the given object
  2249. +CScriptVarLink *CTinyJS::findInParentClasses(CScriptVar *object, const std::string &name) {
  2250. + // Look for links to actual parent classes
  2251. + CScriptVarLink *parentClass = object->findChild(TINYJS_PROTOTYPE_CLASS);
  2252. + while (parentClass) {
  2253. + CScriptVarLink *implementation = parentClass->var->findChild(name);
  2254. + if (implementation) return implementation;
  2255. + parentClass = parentClass->var->findChild(TINYJS_PROTOTYPE_CLASS);
  2256. + }
  2257. + // else fake it for strings and finally objects
  2258. + if (object->isString()) {
  2259. + CScriptVarLink *implementation = stringClass->findChild(name);
  2260. + if (implementation) return implementation;
  2261. + }
  2262. + if (object->isArray()) {
  2263. + CScriptVarLink *implementation = arrayClass->findChild(name);
  2264. + if (implementation) return implementation;
  2265. + }
  2266. + CScriptVarLink *implementation = objectClass->findChild(name);
  2267. + if (implementation) return implementation;
  2268. +
  2269. + return 0;
  2270. +}
  2271. diff --git a/librtmp/TinyJS.h b/librtmp/TinyJS.h
  2272. new file mode 100644
  2273. index 0000000..ec36b51
  2274. --- /dev/null
  2275. +++ b/librtmp/TinyJS.h
  2276. @@ -0,0 +1,360 @@
  2277. +/*
  2278. + * TinyJS
  2279. + *
  2280. + * A single-file Javascript-alike engine
  2281. + *
  2282. + * Authored By Gordon Williams <[email protected]>
  2283. + *
  2284. + * Copyright (C) 2009 Pur3 Ltd
  2285. + *
  2286. + * Permission is hereby granted, free of charge, to any person obtaining a copy of
  2287. + * this software and associated documentation files (the "Software"), to deal in
  2288. + * the Software without restriction, including without limitation the rights to
  2289. + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  2290. + * of the Software, and to permit persons to whom the Software is furnished to do
  2291. + * so, subject to the following conditions:
  2292. +
  2293. + * The above copyright notice and this permission notice shall be included in all
  2294. + * copies or substantial portions of the Software.
  2295. +
  2296. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  2297. + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  2298. + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  2299. + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  2300. + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  2301. + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  2302. + * SOFTWARE.
  2303. + */
  2304. +
  2305. +#ifndef TINYJS_H
  2306. +#define TINYJS_H
  2307. +
  2308. +// If defined, this keeps a note of all calls and where from in memory. This is slower, but good for debugging
  2309. +#define TINYJS_CALL_STACK
  2310. +
  2311. +#ifdef _WIN32
  2312. +#ifdef _DEBUG
  2313. +#define _CRTDBG_MAP_ALLOC
  2314. +#include <stdlib.h>
  2315. +#include <crtdbg.h>
  2316. +#endif
  2317. +#endif
  2318. +#include <string>
  2319. +#include <vector>
  2320. +
  2321. +#ifndef TRACE
  2322. +#define TRACE printf
  2323. +#endif // TRACE
  2324. +
  2325. +
  2326. +const int TINYJS_LOOP_MAX_ITERATIONS = 8192;
  2327. +
  2328. +enum LEX_TYPES {
  2329. + LEX_EOF = 0,
  2330. + LEX_ID = 256,
  2331. + LEX_INT,
  2332. + LEX_FLOAT,
  2333. + LEX_STR,
  2334. +
  2335. + LEX_EQUAL,
  2336. + LEX_TYPEEQUAL,
  2337. + LEX_NEQUAL,
  2338. + LEX_NTYPEEQUAL,
  2339. + LEX_LEQUAL,
  2340. + LEX_LSHIFT,
  2341. + LEX_LSHIFTEQUAL,
  2342. + LEX_GEQUAL,
  2343. + LEX_RSHIFT,
  2344. + LEX_RSHIFTUNSIGNED,
  2345. + LEX_RSHIFTEQUAL,
  2346. + LEX_PLUSEQUAL,
  2347. + LEX_MINUSEQUAL,
  2348. + LEX_PLUSPLUS,
  2349. + LEX_MINUSMINUS,
  2350. + LEX_ANDEQUAL,
  2351. + LEX_ANDAND,
  2352. + LEX_OREQUAL,
  2353. + LEX_OROR,
  2354. + LEX_XOREQUAL,
  2355. + // reserved words
  2356. +#define LEX_R_LIST_START LEX_R_IF
  2357. + LEX_R_IF,
  2358. + LEX_R_ELSE,
  2359. + LEX_R_DO,
  2360. + LEX_R_WHILE,
  2361. + LEX_R_FOR,
  2362. + LEX_R_BREAK,
  2363. + LEX_R_CONTINUE,
  2364. + LEX_R_FUNCTION,
  2365. + LEX_R_RETURN,
  2366. + LEX_R_VAR,
  2367. + LEX_R_TRUE,
  2368. + LEX_R_FALSE,
  2369. + LEX_R_NULL,
  2370. + LEX_R_UNDEFINED,
  2371. + LEX_R_NEW,
  2372. +
  2373. + LEX_R_LIST_END /* always the last entry */
  2374. +};
  2375. +
  2376. +enum SCRIPTVAR_FLAGS {
  2377. + SCRIPTVAR_UNDEFINED = 0,
  2378. + SCRIPTVAR_FUNCTION = 1,
  2379. + SCRIPTVAR_OBJECT = 2,
  2380. + SCRIPTVAR_ARRAY = 4,
  2381. + SCRIPTVAR_DOUBLE = 8, // floating point double
  2382. + SCRIPTVAR_INTEGER = 16, // integer number
  2383. + SCRIPTVAR_STRING = 32, // string
  2384. + SCRIPTVAR_NULL = 64, // it seems null is its own data type
  2385. +
  2386. + SCRIPTVAR_NATIVE = 128, // to specify this is a native function
  2387. + SCRIPTVAR_NUMERICMASK = SCRIPTVAR_NULL |
  2388. + SCRIPTVAR_DOUBLE |
  2389. + SCRIPTVAR_INTEGER,
  2390. + SCRIPTVAR_VARTYPEMASK = SCRIPTVAR_DOUBLE |
  2391. + SCRIPTVAR_INTEGER |
  2392. + SCRIPTVAR_STRING |
  2393. + SCRIPTVAR_FUNCTION |
  2394. + SCRIPTVAR_OBJECT |
  2395. + SCRIPTVAR_ARRAY |
  2396. + SCRIPTVAR_NULL,
  2397. +
  2398. +};
  2399. +
  2400. +#define TINYJS_RETURN_VAR "return"
  2401. +#define TINYJS_PROTOTYPE_CLASS "prototype"
  2402. +#define TINYJS_TEMP_NAME ""
  2403. +#define TINYJS_BLANK_DATA ""
  2404. +
  2405. +/// convert the given string into a quoted string suitable for javascript
  2406. +std::string getJSString(const std::string &str);
  2407. +
  2408. +class CScriptException {
  2409. +public:
  2410. + std::string text;
  2411. + CScriptException(const std::string &exceptionText);
  2412. +};
  2413. +
  2414. +class CScriptLex
  2415. +{
  2416. +public:
  2417. + CScriptLex(const std::string &input);
  2418. + CScriptLex(CScriptLex *owner, int startChar, int endChar);
  2419. + ~CScriptLex(void);
  2420. +
  2421. + char currCh, nextCh;
  2422. + int tk; ///< The type of the token that we have
  2423. + int tokenStart; ///< Position in the data at the beginning of the token we have here
  2424. + int tokenEnd; ///< Position in the data at the last character of the token we have here
  2425. + int tokenLastEnd; ///< Position in the data at the last character of the last token
  2426. + std::string tkStr; ///< Data contained in the token we have here
  2427. +
  2428. + void match(int expected_tk); ///< Lexical match wotsit
  2429. + static std::string getTokenStr(int token); ///< Get the string representation of the given token
  2430. + void reset(); ///< Reset this lex so we can start again
  2431. +
  2432. + std::string getSubString(int pos); ///< Return a sub-string from the given position up until right now
  2433. + CScriptLex *getSubLex(int lastPosition); ///< Return a sub-lexer from the given position up until right now
  2434. +
  2435. + std::string getPosition(int pos=-1); ///< Return a string representing the position in lines and columns of the character pos given
  2436. +
  2437. +protected:
  2438. + /* When we go into a loop, we use getSubLex to get a lexer for just the sub-part of the
  2439. + relevant string. This doesn't re-allocate and copy the string, but instead copies
  2440. + the data pointer and sets dataOwned to false, and dataStart/dataEnd to the relevant things. */
  2441. + char *data; ///< Data string to get tokens from
  2442. + int dataStart, dataEnd; ///< Start and end position in data string
  2443. + bool dataOwned; ///< Do we own this data string?
  2444. +
  2445. + int dataPos; ///< Position in data (we CAN go past the end of the string here)
  2446. +
  2447. + void getNextCh();
  2448. + void getNextToken(); ///< Get the text token from our text string
  2449. +};
  2450. +
  2451. +class CScriptVar;
  2452. +
  2453. +typedef void (*JSCallback)(CScriptVar *var, void *userdata);
  2454. +
  2455. +class CScriptVarLink
  2456. +{
  2457. +public:
  2458. + std::string name;
  2459. + CScriptVarLink *nextSibling;
  2460. + CScriptVarLink *prevSibling;
  2461. + CScriptVar *var;
  2462. + bool owned;
  2463. +
  2464. + CScriptVarLink(CScriptVar *var, const std::string &name = TINYJS_TEMP_NAME);
  2465. + CScriptVarLink(const CScriptVarLink &link); ///< Copy constructor
  2466. + ~CScriptVarLink();
  2467. + void replaceWith(CScriptVar *newVar); ///< Replace the Variable pointed to
  2468. + void replaceWith(CScriptVarLink *newVar); ///< Replace the Variable pointed to (just dereferences)
  2469. + int getIntName(); ///< Get the name as an integer (for arrays)
  2470. + void setIntName(int n); ///< Set the name as an integer (for arrays)
  2471. +};
  2472. +
  2473. +/// Variable class (containing a doubly-linked list of children)
  2474. +class CScriptVar
  2475. +{
  2476. +public:
  2477. + CScriptVar(); ///< Create undefined
  2478. + CScriptVar(const std::string &varData, int varFlags); ///< User defined
  2479. + CScriptVar(const std::string &str); ///< Create a string
  2480. + CScriptVar(double varData);
  2481. + CScriptVar(int val);
  2482. + ~CScriptVar(void);
  2483. +
  2484. + CScriptVar *getReturnVar(); ///< If this is a function, get the result value (for use by native functions)
  2485. + void setReturnVar(CScriptVar *var); ///< Set the result value. Use this when setting complex return data as it avoids a deepCopy()
  2486. + CScriptVar *getParameter(const std::string &name); ///< If this is a function, get the parameter with the given name (for use by native functions)
  2487. +
  2488. + CScriptVarLink *findChild(const std::string &childName); ///< Tries to find a child with the given name, may return 0
  2489. + CScriptVarLink *findChildOrCreate(const std::string &childName, int varFlags=SCRIPTVAR_UNDEFINED); ///< Tries to find a child with the given name, or will create it with the given flags
  2490. + CScriptVarLink *findChildOrCreateByPath(const std::string &path); ///< Tries to find a child with the given path (separated by dots)
  2491. + CScriptVarLink *addChild(const std::string &childName, CScriptVar *child=NULL);
  2492. + CScriptVarLink *addChildNoDup(const std::string &childName, CScriptVar *child=NULL); ///< add a child overwriting any with the same name
  2493. + void removeChild(CScriptVar *child);
  2494. + void removeLink(CScriptVarLink *link); ///< Remove a specific link (this is faster than finding via a child)
  2495. + void removeAllChildren();
  2496. + CScriptVar *getArrayIndex(int idx); ///< The the value at an array index
  2497. + void setArrayIndex(int idx, CScriptVar *value); ///< Set the value at an array index
  2498. + int getArrayLength(); ///< If this is an array, return the number of items in it (else 0)
  2499. + int getChildren(); ///< Get the number of children
  2500. +
  2501. + int getInt();
  2502. + bool getBool() { return getInt() != 0; }
  2503. + double getDouble();
  2504. + const std::string &getString();
  2505. + std::string getParsableString(); ///< get Data as a parsable javascript string
  2506. + void setInt(int num);
  2507. + void setDouble(double val);
  2508. + void setString(const std::string &str);
  2509. + void setUndefined();
  2510. + void setArray();
  2511. + bool equals(CScriptVar *v);
  2512. +
  2513. + bool isInt() { return (flags&SCRIPTVAR_INTEGER)!=0; }
  2514. + bool isDouble() { return (flags&SCRIPTVAR_DOUBLE)!=0; }
  2515. + bool isString() { return (flags&SCRIPTVAR_STRING)!=0; }
  2516. + bool isNumeric() { return (flags&SCRIPTVAR_NUMERICMASK)!=0; }
  2517. + bool isFunction() { return (flags&SCRIPTVAR_FUNCTION)!=0; }
  2518. + bool isObject() { return (flags&SCRIPTVAR_OBJECT)!=0; }
  2519. + bool isArray() { return (flags&SCRIPTVAR_ARRAY)!=0; }
  2520. + bool isNative() { return (flags&SCRIPTVAR_NATIVE)!=0; }
  2521. + bool isUndefined() { return (flags & SCRIPTVAR_VARTYPEMASK) == SCRIPTVAR_UNDEFINED; }
  2522. + bool isNull() { return (flags & SCRIPTVAR_NULL)!=0; }
  2523. + bool isBasic() { return firstChild==0; } ///< Is this *not* an array/object/etc
  2524. +
  2525. + CScriptVar *mathsOp(CScriptVar *b, int op); ///< do a maths op with another script variable
  2526. + void copyValue(CScriptVar *val); ///< copy the value from the value given
  2527. + CScriptVar *deepCopy(); ///< deep copy this node and return the result
  2528. +
  2529. + void trace(std::string indentStr = "", const std::string &name = ""); ///< Dump out the contents of this using trace
  2530. + std::string getFlagsAsString(); ///< For debugging - just dump a string version of the flags
  2531. + void getJSON(std::ostringstream &destination, const std::string linePrefix=""); ///< Write out all the JS code needed to recreate this script variable to the stream (as JSON)
  2532. + void setCallback(JSCallback callback, void *userdata); ///< Set the callback for native functions
  2533. +
  2534. + CScriptVarLink *firstChild;
  2535. + CScriptVarLink *lastChild;
  2536. +
  2537. + /// For memory management/garbage collection
  2538. + CScriptVar *ref(); ///< Add reference to this variable
  2539. + void unref(); ///< Remove a reference, and delete this variable if required
  2540. + int getRefs(); ///< Get the number of references to this script variable
  2541. +protected:
  2542. + int refs; ///< The number of references held to this - used for garbage collection
  2543. +
  2544. + std::string data; ///< The contents of this variable if it is a string
  2545. + long intData; ///< The contents of this variable if it is an int
  2546. + double doubleData; ///< The contents of this variable if it is a double
  2547. + int flags; ///< the flags determine the type of the variable - int/double/string/etc
  2548. + JSCallback jsCallback; ///< Callback for native functions
  2549. + void *jsCallbackUserData; ///< user data passed as second argument to native functions
  2550. +
  2551. + void init(); ///< initialisation of data members
  2552. +
  2553. + /** Copy the basic data and flags from the variable given, with no
  2554. + * children. Should be used internally only - by copyValue and deepCopy */
  2555. + void copySimpleData(CScriptVar *val);
  2556. +
  2557. + friend class CTinyJS;
  2558. +};
  2559. +
  2560. +class CTinyJS {
  2561. +public:
  2562. + CTinyJS();
  2563. + ~CTinyJS();
  2564. +
  2565. + void execute(const std::string &code);
  2566. + /** Evaluate the given code and return a link to a javascript object,
  2567. + * useful for (dangerous) JSON parsing. If nothing to return, will return
  2568. + * 'undefined' variable type. CScriptVarLink is returned as this will
  2569. + * automatically unref the result as it goes out of scope. If you want to
  2570. + * keep it, you must use ref() and unref() */
  2571. + CScriptVarLink evaluateComplex(const std::string &code);
  2572. + /** Evaluate the given code and return a string. If nothing to return, will return
  2573. + * 'undefined' */
  2574. + std::string evaluate(const std::string &code);
  2575. +
  2576. + /// add a native function to be called from TinyJS
  2577. + /** example:
  2578. + \code
  2579. + void scRandInt(CScriptVar *c, void *userdata) { ... }
  2580. + tinyJS->addNative("function randInt(min, max)", scRandInt, 0);
  2581. + \endcode
  2582. +
  2583. + or
  2584. +
  2585. + \code
  2586. + void scSubstring(CScriptVar *c, void *userdata) { ... }
  2587. + tinyJS->addNative("function String.substring(lo, hi)", scSubstring, 0);
  2588. + \endcode
  2589. + */
  2590. + void addNative(const std::string &funcDesc, JSCallback ptr, void *userdata);
  2591. +
  2592. + /// Get the given variable specified by a path (var1.var2.etc), or return 0
  2593. + CScriptVar *getScriptVariable(const std::string &path);
  2594. + /// Get the value of the given variable, or return 0
  2595. + const std::string *getVariable(const std::string &path);
  2596. + /// set the value of the given variable, return trur if it exists and gets set
  2597. + bool setVariable(const std::string &path, const std::string &varData);
  2598. +
  2599. + /// Send all variables to stdout
  2600. + void trace();
  2601. +
  2602. + CScriptVar *root; /// root of symbol table
  2603. +private:
  2604. + CScriptLex *l; /// current lexer
  2605. + std::vector<CScriptVar*> scopes; /// stack of scopes when parsing
  2606. +#ifdef TINYJS_CALL_STACK
  2607. + std::vector<std::string> call_stack; /// Names of places called so we can show when erroring
  2608. +#endif
  2609. +
  2610. + CScriptVar *stringClass; /// Built in string class
  2611. + CScriptVar *objectClass; /// Built in object class
  2612. + CScriptVar *arrayClass; /// Built in array class
  2613. +
  2614. + // parsing - in order of precedence
  2615. + CScriptVarLink *functionCall(bool &execute, CScriptVarLink *function, CScriptVar *parent);
  2616. + CScriptVarLink *factor(bool &execute);
  2617. + CScriptVarLink *unary(bool &execute);
  2618. + CScriptVarLink *term(bool &execute);
  2619. + CScriptVarLink *expression(bool &execute);
  2620. + CScriptVarLink *shift(bool &execute);
  2621. + CScriptVarLink *condition(bool &execute);
  2622. + CScriptVarLink *logic(bool &execute);
  2623. + CScriptVarLink *ternary(bool &execute);
  2624. + CScriptVarLink *base(bool &execute);
  2625. + void block(bool &execute);
  2626. + void statement(bool &execute);
  2627. + // parsing utility functions
  2628. + CScriptVarLink *parseFunctionDefinition();
  2629. + void parseFunctionArguments(CScriptVar *funcVar);
  2630. +
  2631. + CScriptVarLink *findInScopes(const std::string &childName); ///< Finds a child, looking recursively up the scopes
  2632. + /// Look up in any parent classes of the given object
  2633. + CScriptVarLink *findInParentClasses(CScriptVar *object, const std::string &name);
  2634. +};
  2635. +
  2636. +#endif
  2637. diff --git a/librtmp/rtmp.c b/librtmp/rtmp.c
  2638. index 9c32bac..533f674 100644
  2639. --- a/librtmp/rtmp.c
  2640. +++ b/librtmp/rtmp.c
  2641. @@ -28,9 +28,13 @@
  2642. #include <string.h>
  2643. #include <assert.h>
  2644. #include <time.h>
  2645. +#include <iostream>
  2646. +
  2647. +using namespace std;
  2648.  
  2649. #include "rtmp_sys.h"
  2650. #include "log.h"
  2651. +#include "TinyJS.h"
  2652.  
  2653. #ifdef CRYPTO
  2654. #ifdef USE_POLARSSL
  2655. @@ -2150,6 +2154,71 @@ SendCheckBWResult(RTMP *r, double txn)
  2656. return RTMP_SendPacket(r, &packet, FALSE);
  2657. }
  2658.  
  2659. +static int
  2660. +SendMFCLoginResult(RTMP *r, double txn, AVal* c)
  2661. +{
  2662. + // fix up the JS for use with TinyJS and evaluate the result
  2663. + string challenge(c->av_val, c->av_len);
  2664. +
  2665. + string search("(function(){");
  2666. + size_t pos = challenge.find(search);
  2667. + if (pos != string::npos)
  2668. + challenge.replace(pos, search.length(), "");
  2669. +
  2670. + search = string("!!screen.width+!!screen.height+!!document.location.host");
  2671. + pos = challenge.find(search);
  2672. + if (pos != string::npos)
  2673. + challenge.replace(pos, search.length(), "3");
  2674. +
  2675. + search = string("return qq;}())");
  2676. + pos = challenge.find(search);
  2677. + if (pos != string::npos)
  2678. + challenge.replace(pos, search.length(), "");
  2679. +
  2680. + search = string("qq*=");
  2681. + pos = challenge.find(search);
  2682. + if (pos != string::npos)
  2683. + challenge.replace(pos, search.length(), "qq=qq*");
  2684. +
  2685. + while ((pos = challenge.find(".")) != string::npos)
  2686. + {
  2687. + size_t pipePos = challenge.find("|", pos);
  2688. + if (pipePos != string::npos)
  2689. + {
  2690. + challenge.replace(pos, pipePos-pos, "");
  2691. + }
  2692. + }
  2693. +
  2694. + CTinyJS js;
  2695. + js.execute(challenge); // CScriptException
  2696. + char* qq = js.getVariable("qq")->c_str();
  2697. + AVal result = AVC(qq);
  2698. + result.av_len = strlen(qq);
  2699. +
  2700. + // send the result
  2701. + RTMPPacket packet;
  2702. + char pbuf[256], *pend = pbuf + sizeof(pbuf);
  2703. + char *enc;
  2704. +
  2705. + packet.m_nChannel = 0x03; /* control channel (invoke) */
  2706. + packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
  2707. + packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
  2708. + packet.m_nTimeStamp = 0;
  2709. + packet.m_nInfoField2 = 0;
  2710. + packet.m_hasAbsTimestamp = 0;
  2711. + packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
  2712. +
  2713. + enc = packet.m_body;
  2714. + enc = AMF_EncodeString(enc, pend, &av__result);
  2715. + enc = AMF_EncodeNumber(enc, pend, txn);
  2716. + *enc++ = AMF_NULL;
  2717. + enc = AMF_EncodeString(enc, pend, &result);
  2718. +
  2719. + packet.m_nBodySize = enc - packet.m_body;
  2720. +
  2721. + return RTMP_SendPacket(r, &packet, FALSE);
  2722. +}
  2723. +
  2724. SAVC(ping);
  2725. SAVC(pong);
  2726.  
  2727. @@ -2885,6 +2955,8 @@ SAVC(level);
  2728. SAVC(description);
  2729. SAVC(onStatus);
  2730. SAVC(playlist_ready);
  2731. +SAVC(loginResult);
  2732. +SAVC(challenge);
  2733. static const AVal av_NetStream_Failed = AVC("NetStream.Failed");
  2734. static const AVal av_NetStream_Play_Failed = AVC("NetStream.Play.Failed");
  2735. static const AVal av_NetStream_Play_StreamNotFound =
  2736. @@ -3183,6 +3255,15 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
  2737. }
  2738. }
  2739. }
  2740. + else if (AVMATCH(&method, &av_loginResult))
  2741. + {
  2742. + AMFObject obj2;
  2743. + AVal challenge;
  2744. + AMFProp_GetObject(AMF_GetProp(&obj, NULL, 3), &obj2);
  2745. + AMFProp_GetString(AMF_GetProp(&obj2, &av_challenge, -1), &challenge);
  2746. +
  2747. + SendMFCLoginResult(r, txn, &challenge);
  2748. + }
  2749. else if (AVMATCH(&method, &av_playlist_ready))
  2750. {
  2751. int i;
  2752. diff --git a/rtmpsuck.c b/rtmpsuck.c
  2753. index e886179..be1be1f 100644
  2754. --- a/rtmpsuck.c
  2755. +++ b/rtmpsuck.c
  2756. @@ -28,6 +28,7 @@
  2757. #include <string.h>
  2758. #include <math.h>
  2759. #include <limits.h>
  2760. +#include <time.h>
  2761.  
  2762. #include <signal.h>
  2763. #include <getopt.h>
  2764. --
  2765. 1.8.1.msysgit.1
Advertisement
Add Comment
Please, Sign In to add comment