jsinteractive.c 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686
  1. /*
  2. * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
  3. *
  4. * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
  5. *
  6. * This Source Code Form is subject to the terms of the Mozilla Public
  7. * License, v. 2.0. If a copy of the MPL was not distributed with this
  8. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  9. *
  10. * ----------------------------------------------------------------------------
  11. * Interactive Shell implementation
  12. * ----------------------------------------------------------------------------
  13. */
  14. #include "jsutils.h"
  15. #include "jsinteractive.h"
  16. #include "jshardware.h"
  17. #include "jswrapper.h"
  18. #include "jswrap_json.h"
  19. #include "jswrap_io.h"
  20. #ifdef USE_NET
  21. #include "httpserver.h"
  22. #endif
  23. #ifdef USE_LCD_FSMC
  24. #include "graphics.h"
  25. #include "lcd_fsmc.h"
  26. #endif
  27. #ifdef ARM
  28. #define CHAR_DELETE_SEND 0x08
  29. #else
  30. #define CHAR_DELETE_SEND '\b'
  31. #endif
  32. // ----------------------------------------------------------------------------
  33. typedef struct TimerState {
  34. JsSysTime time;
  35. JsSysTime interval;
  36. bool recurring;
  37. JsVarRef callback; // a calback, or 0
  38. } TimerState;
  39. typedef enum {
  40. IS_NONE,
  41. IS_HAD_R,
  42. IS_HAD_27,
  43. IS_HAD_27_79,
  44. IS_HAD_27_91,
  45. IS_HAD_27_91_49,
  46. IS_HAD_27_91_51,
  47. IS_HAD_27_91_52,
  48. IS_HAD_27_91_53,
  49. IS_HAD_27_91_54,
  50. } InputState;
  51. TODOFlags todo = TODO_NOTHING;
  52. JsVar *events = 0; // Array of events to execute
  53. JsVarRef timerArray = 0; // Linked List of timers to check and run
  54. JsVarRef watchArray = 0; // Linked List of input watches to check and run
  55. // ----------------------------------------------------------------------------
  56. IOEventFlags consoleDevice = DEFAULT_CONSOLE_DEVICE; ///< The console device for user interaction
  57. Pin pinBusyIndicator = DEFAULT_BUSY_PIN_INDICATOR;
  58. Pin pinSleepIndicator = DEFAULT_SLEEP_PIN_INDICATOR;
  59. bool echo; ///< do we provide any user feedback?
  60. bool allowDeepSleep;
  61. // ----------------------------------------------------------------------------
  62. JsParse p; ///< The parser we're using for interactiveness
  63. JsVar *inputLine = 0; ///< The current input line
  64. bool inputLineRemoved = false;
  65. int inputCursorPos = 0; ///< The position of the cursor in the input line
  66. InputState inputState = 0; ///< state for dealing with cursor keys
  67. bool hasUsedHistory = false; ///< Used to speed up - if we were cycling through history and then edit, we need to copy the string
  68. unsigned char loopsIdling; ///< How many times around the loop have we been entirely idle?
  69. bool interruptedDuringEvent; ///< Were we interrupted while executing an event? If so may want to clear timers
  70. // ----------------------------------------------------------------------------
  71. IOEventFlags jsiGetDeviceFromClass(JsVar *class) {
  72. // Built-in classes have their object data set to the device name
  73. return jshFromDeviceString(class->varData.str);
  74. }
  75. JsVar *jsiGetClassNameFromDevice(IOEventFlags device) {
  76. const char *deviceName = jshGetDeviceString(device);
  77. return jsvFindChildFromString(p.root, deviceName, false);
  78. }
  79. static inline bool jsiShowInputLine() {
  80. return echo && !inputLineRemoved;
  81. }
  82. /// Change the console to a new location
  83. void jsiSetConsoleDevice(IOEventFlags device) {
  84. if (device == consoleDevice) return;
  85. if (!jshIsDeviceInitialised(device)) {
  86. JshUSARTInfo inf;
  87. jshUSARTInitInfo(&inf);
  88. jshUSARTSetup(device, &inf);
  89. }
  90. jsiConsoleRemoveInputLine();
  91. if (echo) { // intentionally not using jsiShowInputLine()
  92. jsiConsolePrint("Console Moved to ");
  93. jsiConsolePrint(jshGetDeviceString(device));
  94. jsiConsolePrint("\n");
  95. }
  96. IOEventFlags oldDevice = consoleDevice;
  97. consoleDevice = device;
  98. if (echo) { // intentionally not using jsiShowInputLine()
  99. jsiConsolePrint("Console Moved from ");
  100. jsiConsolePrint(jshGetDeviceString(oldDevice));
  101. jsiConsolePrint("\n");
  102. }
  103. }
  104. /// Get the device that the console is currently on
  105. IOEventFlags jsiGetConsoleDevice() {
  106. return consoleDevice;
  107. }
  108. void jsiConsolePrintChar(char data) {
  109. jshTransmit(consoleDevice, (unsigned char)data);
  110. }
  111. void jsiConsolePrint(const char *str) {
  112. while (*str) {
  113. if (*str == '\n') jsiConsolePrintChar('\r');
  114. jsiConsolePrintChar(*(str++));
  115. }
  116. }
  117. void jsiConsolePrintf(const char *fmt, ...) {
  118. va_list argp;
  119. va_start(argp, fmt);
  120. vcbprintf((vcbprintf_callback)jsiConsolePrint,0, fmt, argp);
  121. va_end(argp);
  122. }
  123. void jsiConsolePrintInt(JsVarInt d) {
  124. char buf[32];
  125. itoa(d, buf, 10);
  126. jsiConsolePrint(buf);
  127. }
  128. /// Print the contents of a string var from a character position until end of line (adding an extra ' ' to delete a character if there was one)
  129. void jsiConsolePrintStringVarUntilEOL(JsVar *v, int fromCharacter, bool andBackup) {
  130. int chars = 0;
  131. JsvStringIterator it;
  132. jsvStringIteratorNew(&it, v, fromCharacter);
  133. while (jsvStringIteratorHasChar(&it)) {
  134. char ch = jsvStringIteratorGetChar(&it);
  135. if (ch == '\n') break;
  136. jsiConsolePrintChar(ch);
  137. chars++;
  138. jsvStringIteratorNext(&it);
  139. }
  140. jsvStringIteratorFree(&it);
  141. if (andBackup) {
  142. jsiConsolePrintChar(' ');chars++;
  143. while (chars--) jsiConsolePrintChar(0x08); //delete
  144. }
  145. }
  146. /** Print the contents of a string var - directly - starting from the given character, and
  147. * using newLineCh to prefix new lines (if it is not 0). */
  148. void jsiConsolePrintStringVarWithNewLineChar(JsVar *v, int fromCharacter, char newLineCh) {
  149. JsvStringIterator it;
  150. jsvStringIteratorNew(&it, v, fromCharacter);
  151. while (jsvStringIteratorHasChar(&it)) {
  152. char ch = jsvStringIteratorGetChar(&it);
  153. if (ch == '\n') jsiConsolePrintChar('\r');
  154. jsiConsolePrintChar(ch);
  155. if (ch == '\n' && newLineCh) jsiConsolePrintChar(newLineCh);
  156. jsvStringIteratorNext(&it);
  157. }
  158. jsvStringIteratorFree(&it);
  159. }
  160. /// Print the contents of a string var - directly
  161. void jsiConsolePrintStringVar(JsVar *v) {
  162. jsiConsolePrintStringVarWithNewLineChar(v,0,0);
  163. }
  164. /** Assuming that we are at the end of the string, this backs up
  165. * and deletes it */
  166. void jsiConsoleEraseStringVarBackwards(JsVar *v) {
  167. assert(jsvHasCharacterData(v));
  168. int line, lines = jsvGetLinesInString(v);
  169. for (line=lines;line>0;line--) {
  170. int i,chars = jsvGetCharsOnLine(v, line);
  171. if (line==lines) {
  172. for (i=0;i<chars;i++) jsiConsolePrintChar(0x08); // move cursor back
  173. }
  174. for (i=0;i<chars;i++) jsiConsolePrintChar(' '); // move cursor forwards and wipe out
  175. for (i=0;i<chars;i++) jsiConsolePrintChar(0x08); // move cursor back
  176. if (line>1) {
  177. // clear the character before - this would have had a colon
  178. jsiConsolePrintChar(0x08);
  179. jsiConsolePrintChar(' ');
  180. // move cursor up
  181. jsiConsolePrintChar(27);
  182. jsiConsolePrintChar(91);
  183. jsiConsolePrintChar(65);
  184. }
  185. }
  186. }
  187. /** Assuming that we are at fromCharacter position in the string var,
  188. * erase everything that comes AFTER and return the cursor to 'fromCharacter'
  189. * On newlines, if erasePrevCharacter, we remove the character before too. */
  190. void jsiConsoleEraseStringVarFrom(JsVar *v, int fromCharacter, bool erasePrevCharacter) {
  191. assert(jsvHasCharacterData(v));
  192. int cursorLine, cursorCol;
  193. jsvGetLineAndCol(v, fromCharacter, &cursorLine, &cursorCol);
  194. // delete contents of current line
  195. int i, chars = jsvGetCharsOnLine(v, cursorLine);
  196. for (i=cursorCol;i<=chars;i++) jsiConsolePrintChar(' ');
  197. for (i=0;i<chars;i++) jsiConsolePrintChar(0x08); // move cursor back
  198. int line, lines = jsvGetLinesInString(v);
  199. for (line=cursorLine+1;line<=lines;line++) {
  200. jsiConsolePrintChar(27);
  201. jsiConsolePrintChar(91);
  202. jsiConsolePrintChar(66); // move down
  203. chars = jsvGetCharsOnLine(v, line);
  204. for (i=0;i<chars;i++) jsiConsolePrintChar(' '); // move cursor forwards and wipe out
  205. for (i=0;i<chars;i++) jsiConsolePrintChar(0x08); // move cursor back
  206. if (erasePrevCharacter) {
  207. jsiConsolePrintChar(0x08); // move cursor back
  208. jsiConsolePrintChar(' ');
  209. }
  210. }
  211. // move the cursor back up
  212. for (line=cursorLine+1;line<=lines;line++) {
  213. jsiConsolePrintChar(27);
  214. jsiConsolePrintChar(91);
  215. jsiConsolePrintChar(65);
  216. }
  217. // move the cursor forwards
  218. for (i=1;i<cursorCol;i++) {
  219. jsiConsolePrintChar(27);
  220. jsiConsolePrintChar(91);
  221. jsiConsolePrintChar(67);
  222. }
  223. }
  224. void jsiMoveCursor(int oldX, int oldY, int newX, int newY) {
  225. // see http://www.termsys.demon.co.uk/vtansi.htm - we could do this better
  226. // move cursor
  227. while (oldX < newX) {
  228. jsiConsolePrintChar(27);
  229. jsiConsolePrintChar(91);
  230. jsiConsolePrintChar(67);
  231. oldX++;
  232. }
  233. while (oldX > newX) {
  234. jsiConsolePrintChar(27);
  235. jsiConsolePrintChar(91);
  236. jsiConsolePrintChar(68);
  237. oldX--;
  238. }
  239. while (oldY < newY) {
  240. jsiConsolePrintChar(27);
  241. jsiConsolePrintChar(91);
  242. jsiConsolePrintChar(66);
  243. oldY++;
  244. }
  245. while (oldY > newY) {
  246. jsiConsolePrintChar(27);
  247. jsiConsolePrintChar(91);
  248. jsiConsolePrintChar(65);
  249. oldY--;
  250. }
  251. }
  252. void jsiMoveCursorChar(JsVar *v, int fromCharacter, int toCharacter) {
  253. if (fromCharacter==toCharacter) return;
  254. int oldX, oldY;
  255. jsvGetLineAndCol(v, fromCharacter, &oldY, &oldX);
  256. int newX, newY;
  257. jsvGetLineAndCol(v, toCharacter, &newY, &newX);
  258. jsiMoveCursor(oldX, oldY, newX, newY);
  259. }
  260. /// If the input line was shown in the console, remove it
  261. void jsiConsoleRemoveInputLine() {
  262. if (!inputLineRemoved) {
  263. inputLineRemoved = true;
  264. if (echo && inputLine) { // intentionally not using jsiShowInputLine()
  265. jsiMoveCursorChar(inputLine, inputCursorPos, 0);
  266. jsiConsoleEraseStringVarFrom(inputLine, 0, true);
  267. jsiConsolePrintChar(0x08); // go back to start of line
  268. }
  269. }
  270. }
  271. /// If the input line has been removed, return it
  272. void jsiReturnInputLine() {
  273. if (inputLineRemoved) {
  274. inputLineRemoved = false;
  275. if (echo) { // intentionally not using jsiShowInputLine()
  276. jsiConsolePrintChar('\r'); // carriage return
  277. jsiConsolePrintChar('>');
  278. jsiConsolePrintStringVarWithNewLineChar(inputLine, 0, ':');
  279. jsiMoveCursorChar(inputLine, (int)jsvGetStringLength(inputLine), inputCursorPos);
  280. }
  281. }
  282. }
  283. void jsiConsolePrintPosition(struct JsLex *lex, int tokenPos) {
  284. int line,col;
  285. jsvGetLineAndCol(lex->sourceVar, tokenPos, &line, &col);
  286. jsiConsolePrintf("line %d col %d\n",line,col);
  287. }
  288. void jsiConsolePrintTokenLineMarker(struct JsLex *lex, int tokenPos) {
  289. int line = 1,col = 1;
  290. jsvGetLineAndCol(lex->sourceVar, tokenPos, &line, &col);
  291. int startOfLine = jsvGetIndexFromLineAndCol(lex->sourceVar, line, 1);
  292. jsiConsolePrintStringVarUntilEOL(lex->sourceVar, startOfLine, false);
  293. jsiConsolePrint("\n");
  294. while (col-- > 1) jsiConsolePrintChar(' ');
  295. jsiConsolePrintChar('^');
  296. jsiConsolePrint("\n");
  297. }
  298. /// Print the contents of a string var to a device - directly
  299. void jsiTransmitStringVar(IOEventFlags device, JsVar *v) {
  300. JsvStringIterator it;
  301. jsvStringIteratorNew(&it, v, 0);
  302. while (jsvStringIteratorHasChar(&it)) {
  303. char ch = jsvStringIteratorGetChar(&it);
  304. jshTransmit(device, (unsigned char)ch);
  305. jsvStringIteratorNext(&it);
  306. }
  307. jsvStringIteratorFree(&it);
  308. }
  309. void jsiSetBusy(JsiBusyDevice device, bool isBusy) {
  310. static JsiBusyDevice business = 0;
  311. if (isBusy)
  312. business |= device;
  313. else
  314. business &= (JsiBusyDevice)~device;
  315. if (pinBusyIndicator != PIN_UNDEFINED)
  316. jshPinOutput(pinBusyIndicator, business!=0);
  317. }
  318. void jsiSetSleep(bool isSleep) {
  319. if (pinSleepIndicator != PIN_UNDEFINED)
  320. jshPinOutput(pinSleepIndicator, isSleep);
  321. }
  322. static JsVarRef _jsiInitNamedArray(const char *name) {
  323. JsVar *arrayName = jsvFindChildFromString(p.root, name, true);
  324. if (!arrayName) return 0; // out of memory
  325. if (!arrayName->firstChild) {
  326. JsVar *array = jsvNewWithFlags(JSV_ARRAY);
  327. if (!array) { // out of memory
  328. jsvUnLock(arrayName);
  329. return 0;
  330. }
  331. arrayName->firstChild = jsvGetRef(jsvRef(array));
  332. jsvUnLock(array);
  333. }
  334. JsVarRef arrayRef = jsvRefRef(arrayName->firstChild);
  335. jsvUnLock(arrayName);
  336. return arrayRef;
  337. }
  338. // Used when recovering after being flashed
  339. // 'claim' anything we are using
  340. void jsiSoftInit() {
  341. #ifdef USE_NET
  342. httpInit();
  343. #endif
  344. #ifdef USE_LCD_FSMC
  345. JsVar *parent = jspNewObject(jsiGetParser(), "LCD", "Graphics");
  346. if (parent) {
  347. JsVar *parentObj = jsvSkipName(parent);
  348. JsGraphics gfx;
  349. graphicsStructInit(&gfx);
  350. gfx.data.type = JSGRAPHICSTYPE_FSMC;
  351. gfx.graphicsVar = parentObj;
  352. gfx.data.width = 320;
  353. gfx.data.height = 240;
  354. gfx.data.bpp = 16;
  355. lcdInit_FSMC(&gfx);
  356. lcdSetCallbacks_FSMC(&gfx);
  357. graphicsSplash(&gfx);
  358. graphicsSetVar(&gfx);
  359. jsvUnLock(parentObj);
  360. jsvUnLock(parent);
  361. }
  362. #endif
  363. events = jsvNewWithFlags(JSV_ARRAY);
  364. inputLine = jsvNewFromEmptyString();
  365. inputCursorPos = 0;
  366. allowDeepSleep = false;
  367. // Load timer/watch arrays
  368. timerArray = _jsiInitNamedArray(JSI_TIMERS_NAME);
  369. watchArray = _jsiInitNamedArray(JSI_WATCHES_NAME);
  370. // Now run initialisation code
  371. JsVar *initName = jsvFindChildFromString(p.root, JSI_INIT_CODE_NAME, false);
  372. if (initName && initName->firstChild) {
  373. //jsiConsolePrint("Running initialisation code...\n");
  374. JsVar *initCode = jsvLock(initName->firstChild);
  375. jsvUnLock(jspEvaluateVar(&p, initCode, 0));
  376. jsvUnLock(initCode);
  377. jsvRemoveChild(p.root, initName);
  378. }
  379. jsvUnLock(initName);
  380. // Check any existing watches and set up interrupts for them
  381. if (watchArray) {
  382. JsVar *watchArrayPtr = jsvLock(watchArray);
  383. JsVarRef watch = watchArrayPtr->firstChild;
  384. while (watch) {
  385. JsVar *watchNamePtr = jsvLock(watch);
  386. JsVar *watchPin = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "pin", false));
  387. jshPinWatch(jshGetPinFromVar(watchPin), true);
  388. jsvUnLock(watchPin);
  389. watch = watchNamePtr->nextSibling;
  390. jsvUnLock(watchNamePtr);
  391. }
  392. jsvUnLock(watchArrayPtr);
  393. }
  394. // Check any existing timers and try and set time correctly
  395. if (timerArray) {
  396. JsSysTime currentTime = jshGetSystemTime();
  397. JsVar *timerArrayPtr = jsvLock(timerArray);
  398. JsVarRef timer = timerArrayPtr->firstChild;
  399. while (timer) {
  400. JsVar *timerNamePtr = jsvLock(timer);
  401. JsVar *timerTime = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(timerNamePtr->firstChild, "time", false));
  402. JsVarFloat interval = jsvGetFloatAndUnLock(jsvSkipNameAndUnLock(jsvFindChildFromStringRef(timerNamePtr->firstChild, "interval", false)));
  403. jsvSetInteger(timerTime, currentTime + jshGetTimeFromMilliseconds(interval));
  404. jsvUnLock(timerTime);
  405. timer = timerNamePtr->nextSibling;
  406. jsvUnLock(timerNamePtr);
  407. }
  408. jsvUnLock(timerArrayPtr);
  409. }
  410. // And look for onInit function
  411. JsVar *onInit = jsvFindChildFromString(p.root, JSI_ONINIT_NAME, false);
  412. if (onInit && onInit->firstChild) {
  413. if (echo) jsiConsolePrint("Running onInit()...\n");
  414. JsVar *func = jsvSkipName(onInit);
  415. if (jsvIsFunction(func))
  416. jspExecuteFunction(&p, func, 0, 0, (JsVar**)0);
  417. else if (jsvIsString(func))
  418. jsvUnLock(jspEvaluateVar(&p, func, 0));
  419. else
  420. jsError("onInit is not a Function or a String");
  421. }
  422. jsvUnLock(onInit);
  423. }
  424. /** Append the code required to initialise a serial port to this string */
  425. void jsiAppendSerialInitialisation(JsVar *str, const char *serialName, bool addCallbacks) {
  426. JsVar *serialVar = jsvObjectGetChild(p.root, serialName, 0);
  427. if (serialVar) {
  428. if (addCallbacks) {
  429. JsVar *onData = jsvSkipOneNameAndUnLock(jsvFindChildFromString(serialVar, USART_CALLBACK_NAME, false));
  430. if (onData) {
  431. JsVar *onDataStr = jsvAsString(onData, true/*unlock*/);
  432. jsvAppendString(str, serialName);
  433. jsvAppendString(str, ".onData(");
  434. jsvAppendStringVarComplete(str, onDataStr);
  435. jsvAppendString(str, ");\n");
  436. jsvUnLock(onDataStr);
  437. }
  438. }
  439. JsVar *baud = jsvObjectGetChild(serialVar, USART_BAUDRATE_NAME, 0);
  440. JsVar *options = jsvObjectGetChild(serialVar, DEVICE_OPTIONS_NAME, 0);
  441. if (baud || options) {
  442. JsVarInt baudrate = jsvGetInteger(baud);
  443. if (baudrate <= 0) baudrate = DEFAULT_BAUD_RATE;
  444. jsvAppendPrintf(str, "%s.setup(%d", serialName, baudrate);
  445. if (jsvIsObject(options)) {
  446. jsvAppendString(str, ", ");
  447. jsfGetJSON(options, str);
  448. }
  449. jsvAppendString(str, ");\n");
  450. }
  451. jsvUnLock(baud);
  452. jsvUnLock(options);
  453. jsvUnLock(serialVar);
  454. }
  455. }
  456. /** Append the code required to initialise a SPI port to this string */
  457. void jsiAppendDeviceInitialisation(JsVar *str, const char *deviceName) {
  458. JsVar *deviceVar = jsvObjectGetChild(p.root, deviceName, 0);
  459. if (deviceVar) {
  460. JsVar *options = jsvObjectGetChild(deviceVar, DEVICE_OPTIONS_NAME, 0);
  461. if (options) {
  462. jsvAppendString(str, deviceName);
  463. jsvAppendString(str, ".setup(");
  464. if (jsvIsObject(options)) {
  465. jsfGetJSON(options, str);
  466. }
  467. jsvAppendString(str, ");\n");
  468. }
  469. jsvUnLock(options);
  470. jsvUnLock(deviceVar);
  471. }
  472. }
  473. /** Append all the code required to initialise hardware to this string */
  474. void jsiAppendHardwareInitialisation(JsVar *str, bool addCallbacks) {
  475. if (!echo) jsvAppendString(str, "echo(0);");
  476. if (pinBusyIndicator != DEFAULT_BUSY_PIN_INDICATOR) {
  477. jsvAppendPrintf(str, "setBusyIndicator(%p);\n", pinBusyIndicator);
  478. }
  479. if (pinSleepIndicator != DEFAULT_BUSY_PIN_INDICATOR) {
  480. jsvAppendPrintf(str, "setSleepIndicator(%p);\n", pinSleepIndicator);
  481. }
  482. if (allowDeepSleep) {
  483. jsvAppendPrintf(str, "setDeepSleep(1);\n");
  484. }
  485. jsiAppendSerialInitialisation(str, "USB", addCallbacks);
  486. int i;
  487. for (i=0;i<USARTS;i++)
  488. jsiAppendSerialInitialisation(str, jshGetDeviceString(EV_SERIAL1+i), addCallbacks);
  489. for (i=0;i<SPIS;i++)
  490. jsiAppendDeviceInitialisation(str, jshGetDeviceString(EV_SPI1+i));
  491. for (i=0;i<I2CS;i++)
  492. jsiAppendDeviceInitialisation(str, jshGetDeviceString(EV_I2C1+i));
  493. // pins
  494. Pin pin;
  495. for (pin=0;jshIsPinValid(pin) && pin<255;pin++) {
  496. if (IS_PIN_USED_INTERNALLY(pin)) continue;
  497. JshPinState state = jshPinGetState(pin);
  498. JshPinState statem = state&JSHPINSTATE_MASK;
  499. if (statem == JSHPINSTATE_GPIO_OUT) {
  500. bool isOn = (state&JSHPINSTATE_PIN_IS_ON)!=0;
  501. if (!isOn && IS_PIN_A_LED(pin)) continue;
  502. jsvAppendPrintf(str, "digitalWrite(%p,%d);\n",pin,isOn?1:0);
  503. } else if (/*statem == JSHPINSTATE_GPIO_IN ||*/statem == JSHPINSTATE_GPIO_IN_PULLUP || statem == JSHPINSTATE_GPIO_IN_PULLDOWN) {
  504. // don't bother with normal inputs, as they come up in this state (ish) anyway
  505. const char *s = "";
  506. if (statem == JSHPINSTATE_GPIO_IN_PULLUP) s="_pullup";
  507. if (statem == JSHPINSTATE_GPIO_IN_PULLDOWN) s="_pulldown";
  508. jsvAppendPrintf(str, "pinMode(%p,input%s);\n",pin,s);
  509. }
  510. }
  511. }
  512. // Used when shutting down before flashing
  513. // 'release' anything we are using, but ensure that it doesn't get freed
  514. void jsiSoftKill() {
  515. jsvUnLock(inputLine);
  516. inputLine=0;
  517. inputCursorPos = 0;
  518. // Unref Watches/etc
  519. if (events) {
  520. jsvUnLock(events);
  521. events=0;
  522. }
  523. if (timerArray) {
  524. jsvUnRefRef(timerArray);
  525. timerArray=0;
  526. }
  527. if (watchArray) {
  528. // Check any existing watches and disable interrupts for them
  529. JsVar *watchArrayPtr = jsvLock(watchArray);
  530. JsVarRef watch = watchArrayPtr->firstChild;
  531. while (watch) {
  532. JsVar *watchNamePtr = jsvLock(watch);
  533. JsVar *watchPin = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "pin", false));
  534. jshPinWatch(jshGetPinFromVar(watchPin), false);
  535. jsvUnLock(watchPin);
  536. watch = watchNamePtr->nextSibling;
  537. jsvUnLock(watchNamePtr);
  538. }
  539. jsvUnLock(watchArrayPtr);
  540. jsvUnRefRef(watchArray);
  541. watchArray=0;
  542. }
  543. // Save initialisation information
  544. JsVar *initName = jsvFindChildFromString(p.root, JSI_INIT_CODE_NAME, true);
  545. if (initName->firstChild) {
  546. jsvUnRefRef(initName->firstChild);
  547. initName->firstChild = 0;
  548. }
  549. JsVar *initCode = jsvNewFromEmptyString();
  550. if (initCode) { // out of memory
  551. initName->firstChild = jsvGetRef(jsvRef(initCode));
  552. jsiAppendHardwareInitialisation(initCode, false);
  553. jsvUnLock(initCode);
  554. }
  555. jsvUnLock(initName);
  556. #ifdef USE_NET
  557. httpKill();
  558. #endif
  559. }
  560. void jsiInit(bool autoLoad) {
  561. jsvInit();
  562. jspInit(&p);
  563. #ifdef USE_LCD
  564. lcdInit_Main(&LCD);
  565. #ifdef USE_LCD_ILI9325
  566. lcdInit_FSMC_ILI9325(&LCD, LCD_WIDTH, LCD_HEIGHT);
  567. #endif
  568. #ifdef USE_LCD_SDL
  569. lcdInit_SDL(&LCD, LCD_WIDTH, LCD_HEIGHT);
  570. #endif
  571. lcdClear(0);
  572. lcdSplash();
  573. #endif
  574. /*for (i=0;i<IOPINS;i++)
  575. ioPinState[i].callbacks = 0;*/
  576. // Set state
  577. interruptedDuringEvent = false;
  578. // Set defaults
  579. echo = true;
  580. consoleDevice = DEFAULT_CONSOLE_DEVICE;
  581. pinBusyIndicator = DEFAULT_BUSY_PIN_INDICATOR;
  582. if (jshIsUSBSERIALConnected())
  583. consoleDevice = EV_USBSERIAL;
  584. /* If flash contains any code, then we should
  585. Try and load from it... */
  586. if (autoLoad && jshFlashContainsCode()) {
  587. jspSoftKill(&p);
  588. jsvSoftKill();
  589. jshLoadFromFlash();
  590. jsvSoftInit();
  591. jspSoftInit(&p);
  592. }
  593. //jsvTrace(jsvGetRef(jsiGetParser()->root), 0)
  594. // Softinit may run initialisation code that will overwrite defaults
  595. jsiSoftInit();
  596. if (echo) { // intentionally not using jsiShowInputLine()
  597. #ifndef LINUX
  598. // set up terminal to avoid word wrap
  599. jsiConsolePrint("\e[?7l");
  600. #endif
  601. // rectangles @ http://www.network-science.de/ascii/
  602. jsiConsolePrint("\n"
  603. " _____ _ \n"
  604. " | __|___ ___ ___ _ _|_|___ ___ \n"
  605. " | __|_ -| . | _| | | | | . |\n"
  606. " |_____|___| _|_| |___|_|_|_|___|");
  607. jsiConsolePrint("\n"
  608. " |_| http://www.espruino.com\n"
  609. " "JS_VERSION" Copyright 2013 Gordon Williams\n"
  610. "-------------------------------------------\n"
  611. " KickStarter Version\n"
  612. "-------------------------------------------\n");
  613. jsiConsolePrint("\n>");
  614. }
  615. }
  616. void jsiKill() {
  617. jsiSoftKill();
  618. jspKill(&p);
  619. jsvKill();
  620. }
  621. int jsiCountBracketsInInput() {
  622. int brackets = 0;
  623. JsLex lex;
  624. jslInit(&lex, inputLine);
  625. while (lex.tk!=LEX_EOF && lex.tk!=LEX_UNFINISHED_COMMENT) {
  626. if (lex.tk=='{' || lex.tk=='[' || lex.tk=='(') brackets++;
  627. if (lex.tk=='}' || lex.tk==']' || lex.tk==')') brackets--;
  628. if (brackets<0) break; // closing bracket before opening!
  629. jslGetNextToken(&lex);
  630. }
  631. if (lex.tk==LEX_UNFINISHED_COMMENT)
  632. brackets=1000; // if there's an unfinished comment, we're in the middle of something
  633. jslKill(&lex);
  634. return brackets;
  635. }
  636. /// Tries to get rid of some memory (by clearing command history). Returns true if it got rid of something, false if it didn't.
  637. bool jsiFreeMoreMemory() {
  638. JsVar *history = jsvObjectGetChild(p.root, JSI_HISTORY_NAME, 0);
  639. if (!history) return 0;
  640. JsVar *item = jsvArrayPopFirst(history);
  641. bool freed = item!=0;
  642. jsvUnLock(item);
  643. jsvUnLock(history);
  644. // TODO: could also free the array structure?
  645. return freed;
  646. }
  647. // Add a new line to the command history
  648. void jsiHistoryAddLine(JsVar *newLine) {
  649. if (!newLine || jsvGetStringLength(newLine)==0) return;
  650. JsVar *history = jsvFindChildFromString(p.root, JSI_HISTORY_NAME, true);
  651. if (!history) return; // out of memory
  652. // ensure we actually have the history array
  653. if (!history->firstChild) {
  654. JsVar *arr = jsvNewWithFlags(JSV_ARRAY);
  655. if (!arr) {// out of memory
  656. jsvUnLock(history);
  657. return;
  658. }
  659. history->firstChild = jsvGetRef(jsvRef(arr));
  660. jsvUnLock(arr);
  661. }
  662. history = jsvSkipNameAndUnLock(history);
  663. // if it was already in history, remove it - we'll put it back in front
  664. JsVar *alreadyInHistory = jsvGetArrayIndexOf(history, newLine, false/*not exact*/);
  665. if (alreadyInHistory) {
  666. jsvRemoveChild(history, alreadyInHistory);
  667. jsvUnLock(alreadyInHistory);
  668. }
  669. // put it back in front
  670. jsvArrayPush(history, newLine);
  671. jsvUnLock(history);
  672. }
  673. JsVar *jsiGetHistoryLine(bool previous /* next if false */) {
  674. JsVar *history = jsvObjectGetChild(p.root, JSI_HISTORY_NAME, 0);
  675. JsVar *historyLine = 0;
  676. if (history) {
  677. JsVar *idx = jsvGetArrayIndexOf(history, inputLine, true/*exact*/); // get index of current line
  678. if (idx) {
  679. if (previous && idx->prevSibling) {
  680. historyLine = jsvSkipNameAndUnLock(jsvLock(idx->prevSibling));
  681. } else if (!previous && idx->nextSibling) {
  682. historyLine = jsvSkipNameAndUnLock(jsvLock(idx->nextSibling));
  683. }
  684. jsvUnLock(idx);
  685. } else {
  686. if (previous) historyLine = jsvSkipNameAndUnLock(jsvArrayGetLast(history));
  687. // if next, we weren't using history so couldn't go forwards
  688. }
  689. jsvUnLock(history);
  690. }
  691. return historyLine;
  692. }
  693. bool jsiIsInHistory(JsVar *line) {
  694. JsVar *history = jsvObjectGetChild(p.root, JSI_HISTORY_NAME, 0);
  695. if (!history) return false;
  696. JsVar *historyFound = jsvGetArrayIndexOf(history, line, true/*exact*/);
  697. bool inHistory = historyFound!=0;
  698. jsvUnLock(historyFound);
  699. jsvUnLock(history);
  700. return inHistory;
  701. }
  702. void jsiReplaceInputLine(JsVar *newLine) {
  703. if (jsiShowInputLine()) {
  704. int oldLen = (int)jsvGetStringLength(inputLine);
  705. jsiMoveCursorChar(inputLine, inputCursorPos, oldLen); // move cursor to end
  706. jsiConsoleEraseStringVarBackwards(inputLine);
  707. jsiConsolePrintStringVarWithNewLineChar(newLine,0,':');
  708. }
  709. jsvUnLock(inputLine);
  710. inputLine = jsvLockAgain(newLine);
  711. inputCursorPos = (int)jsvGetStringLength(inputLine);
  712. }
  713. void jsiChangeToHistory(bool previous) {
  714. JsVar *nextHistory = jsiGetHistoryLine(previous);
  715. if (nextHistory) {
  716. jsiReplaceInputLine(nextHistory);
  717. jsvUnLock(nextHistory);
  718. hasUsedHistory = true;
  719. } else if (!previous) { // if next, but we have something, just clear the line
  720. if (jsiShowInputLine()) {
  721. jsiConsoleEraseStringVarBackwards(inputLine);
  722. }
  723. jsvUnLock(inputLine);
  724. inputLine = jsvNewFromEmptyString();
  725. inputCursorPos = 0;
  726. }
  727. }
  728. void jsiIsAboutToEditInputLine() {
  729. // we probably plan to do something with the line now - check it wasn't in history
  730. // and if it was, duplicate it
  731. if (hasUsedHistory) {
  732. hasUsedHistory = false;
  733. if (jsiIsInHistory(inputLine)) {
  734. JsVar *newLine = jsvCopy(inputLine);
  735. if (newLine) { // could have been out of memory!
  736. jsvUnLock(inputLine);
  737. inputLine = newLine;
  738. }
  739. }
  740. }
  741. }
  742. void jsiHandleDelete(bool isBackspace) {
  743. int l = (int)jsvGetStringLength(inputLine);
  744. if (isBackspace && inputCursorPos<=0) return; // at beginning of line
  745. if (!isBackspace && inputCursorPos>=l) return; // at end of line
  746. // work out if we are deleting a newline
  747. bool deleteNewline = (isBackspace && jsvGetCharInString(inputLine,inputCursorPos-1)=='\n') ||
  748. (!isBackspace && jsvGetCharInString(inputLine,inputCursorPos)=='\n');
  749. // If we mod this to keep the string, use jsiIsAboutToEditInputLine
  750. if (deleteNewline && jsiShowInputLine()) {
  751. jsiConsoleEraseStringVarFrom(inputLine, inputCursorPos, true/*before newline*/); // erase all in front
  752. if (isBackspace) {
  753. // delete newline char
  754. jsiConsolePrintChar(0x08);
  755. jsiConsolePrintChar(' ');
  756. jsiMoveCursorChar(inputLine, inputCursorPos, inputCursorPos-1); // move cursor back
  757. }
  758. }
  759. JsVar *v = jsvNewFromEmptyString();
  760. int p = inputCursorPos;
  761. if (isBackspace) p--;
  762. if (p>0) jsvAppendStringVar(v, inputLine, 0, p); // add before cursor (delete)
  763. if (p+1<l) jsvAppendStringVar(v, inputLine, p+1, JSVAPPENDSTRINGVAR_MAXLENGTH); // add the rest
  764. jsvUnLock(inputLine);
  765. inputLine=v;
  766. if (isBackspace)
  767. inputCursorPos--; // move cursor back
  768. // update the console
  769. if (jsiShowInputLine()) {
  770. if (deleteNewline) {
  771. // we already removed everything, so just put it back
  772. jsiConsolePrintStringVarWithNewLineChar(inputLine, inputCursorPos, ':');
  773. jsiMoveCursorChar(inputLine, (int)jsvGetStringLength(inputLine), inputCursorPos); // move cursor back
  774. } else {
  775. // clear the character and move line back
  776. if (isBackspace) jsiConsolePrintChar(0x08);
  777. jsiConsolePrintStringVarUntilEOL(inputLine, inputCursorPos, true/*and backup*/);
  778. }
  779. }
  780. }
  781. void jsiHandleHome() {
  782. while (inputCursorPos>0 && jsvGetCharInString(inputLine,inputCursorPos-1)!='\n') {
  783. if (jsiShowInputLine()) jsiConsolePrintChar(0x08);
  784. inputCursorPos--;
  785. }
  786. }
  787. void jsiHandleEnd() {
  788. int l = (int)jsvGetStringLength(inputLine);
  789. while (inputCursorPos<l && jsvGetCharInString(inputLine,inputCursorPos)!='\n') {
  790. if (jsiShowInputLine())
  791. jsiConsolePrintChar(jsvGetCharInString(inputLine,inputCursorPos));
  792. inputCursorPos++;
  793. }
  794. }
  795. /** Page up/down move cursor to beginnint or end */
  796. void jsiHandlePageUpDown(bool isDown) {
  797. int x,y;
  798. jsvGetLineAndCol(inputLine, inputCursorPos, &y, &x);
  799. if (!isDown) { // up
  800. inputCursorPos = 0;
  801. } else { // down
  802. inputCursorPos = (int)jsvGetStringLength(inputLine);
  803. }
  804. int newX=x,newY=y;
  805. jsvGetLineAndCol(inputLine, inputCursorPos, &newY, &newX);
  806. jsiMoveCursor(x,y,newX,newY);
  807. }
  808. void jsiHandleMoveUpDown(int direction) {
  809. int x,y, lines=jsvGetLinesInString(inputLine);
  810. jsvGetLineAndCol(inputLine, inputCursorPos, &y, &x);
  811. int newX=x,newY=y;
  812. newY+=direction;
  813. if (newY<1) newY=1;
  814. if (newY>lines) newY=lines;
  815. // work out cursor pos and feed back through - we might not be able to get right to the same place
  816. // if we move up
  817. inputCursorPos = jsvGetIndexFromLineAndCol(inputLine, newY, newX);
  818. jsvGetLineAndCol(inputLine, inputCursorPos, &newY, &newX);
  819. if (jsiShowInputLine()) {
  820. jsiMoveCursor(x,y,newX,newY);
  821. }
  822. }
  823. bool jsiAtEndOfInputLine() {
  824. int i = inputCursorPos, l = (int)jsvGetStringLength(inputLine);
  825. while (i < l) {
  826. if (!isWhitespace(jsvGetCharInString(inputLine, i)))
  827. return false;
  828. i++;
  829. }
  830. return true;
  831. }
  832. void jsiHandleChar(char ch) {
  833. //jsiConsolePrintf("[%d:%d]\n", inputState, ch);
  834. //
  835. // special stuff
  836. // 27 then 91 then 68 - left
  837. // 27 then 91 then 67 - right
  838. // 27 then 91 then 65 - up
  839. // 27 then 91 then 66 - down
  840. // 27 then 91 then 51 then 126 - backwards delete
  841. // 27 then 91 then 52 then 126 - numpad end
  842. // 27 then 91 then 49 then 126 - numpad home
  843. // 27 then 91 then 53 then 126 - pgup
  844. // 27 then 91 then 54 then 126 - pgdn
  845. // 27 then 79 then 70 - home
  846. // 27 then 79 then 72 - end
  847. if (ch == 0) {
  848. inputState = IS_NONE; // ignore 0 - it's scary
  849. } else if (ch == 27) {
  850. inputState = IS_HAD_27;
  851. } else if (inputState==IS_HAD_27) {
  852. inputState = IS_NONE;
  853. if (ch == 79)
  854. inputState = IS_HAD_27_79;
  855. else if (ch == 91)
  856. inputState = IS_HAD_27_91;
  857. } else if (inputState==IS_HAD_27_79) { // Numpad
  858. inputState = IS_NONE;
  859. if (ch == 70) jsiHandleEnd();
  860. else if (ch == 72) jsiHandleHome();
  861. else if (ch == 111) jsiHandleChar('/');
  862. else if (ch == 106) jsiHandleChar('*');
  863. else if (ch == 109) jsiHandleChar('-');
  864. else if (ch == 107) jsiHandleChar('+');
  865. else if (ch == 77) jsiHandleChar('\r');
  866. } else if (inputState==IS_HAD_27_91) {
  867. inputState = IS_NONE;
  868. if (ch==68) { // left
  869. if (inputCursorPos>0 && jsvGetCharInString(inputLine,inputCursorPos-1)!='\n') {
  870. inputCursorPos--;
  871. if (jsiShowInputLine()) {
  872. jsiConsolePrintChar(27);
  873. jsiConsolePrintChar(91);
  874. jsiConsolePrintChar(68);
  875. }
  876. }
  877. } else if (ch==67) { // right
  878. if (inputCursorPos<(int)jsvGetStringLength(inputLine) && jsvGetCharInString(inputLine,inputCursorPos)!='\n') {
  879. inputCursorPos++;
  880. if (jsiShowInputLine()) {
  881. jsiConsolePrintChar(27);
  882. jsiConsolePrintChar(91);
  883. jsiConsolePrintChar(67);
  884. }
  885. }
  886. } else if (ch==65) { // up
  887. int l = (int)jsvGetStringLength(inputLine);
  888. if ((l==0 || jsiIsInHistory(inputLine)) && inputCursorPos==l)
  889. jsiChangeToHistory(true); // if at end of line
  890. else
  891. jsiHandleMoveUpDown(-1);
  892. } else if (ch==66) { // down
  893. int l = (int)jsvGetStringLength(inputLine);
  894. if ((l==0 || jsiIsInHistory(inputLine)) && inputCursorPos==l)
  895. jsiChangeToHistory(false); // if at end of line
  896. else
  897. jsiHandleMoveUpDown(1);
  898. } else if (ch==49) {
  899. inputState=IS_HAD_27_91_49;
  900. } else if (ch==51) {
  901. inputState=IS_HAD_27_91_51;
  902. } else if (ch==52) {
  903. inputState=IS_HAD_27_91_52;
  904. } else if (ch==53) {
  905. inputState=IS_HAD_27_91_53;
  906. } else if (ch==54) {
  907. inputState=IS_HAD_27_91_54;
  908. }
  909. } else if (inputState==IS_HAD_27_91_49) {
  910. inputState = IS_NONE;
  911. if (ch==126) { // Numpad Home
  912. jsiHandleHome();
  913. }
  914. } else if (inputState==IS_HAD_27_91_51) {
  915. inputState = IS_NONE;
  916. if (ch==126) { // Numpad (forwards) Delete
  917. jsiHandleDelete(false/*not backspace*/);
  918. }
  919. } else if (inputState==IS_HAD_27_91_52) {
  920. inputState = IS_NONE;
  921. if (ch==126) { // Numpad End
  922. jsiHandleEnd();
  923. }
  924. } else if (inputState==IS_HAD_27_91_53) {
  925. inputState = IS_NONE;
  926. if (ch==126) { // Page Up
  927. jsiHandlePageUpDown(0);
  928. }
  929. } else if (inputState==IS_HAD_27_91_54) {
  930. inputState = IS_NONE;
  931. if (ch==126) { // Page Down
  932. jsiHandlePageUpDown(1);
  933. }
  934. } else {
  935. inputState = IS_NONE;
  936. if (ch == 0x08 || ch == 0x7F /*delete*/) {
  937. jsiHandleDelete(true /*backspace*/);
  938. } else if (ch == '\n' && inputState == IS_HAD_R) {
  939. inputState = IS_NONE; // ignore \ r\n - we already handled it all on \r
  940. } else if (ch == '\r' || ch == '\n') {
  941. if (jsiAtEndOfInputLine()) { // at EOL so we need to figure out if we can execute or not
  942. if (ch == '\r') inputState = IS_HAD_R;
  943. if (jsiCountBracketsInInput()<=0) { // actually execute!
  944. if (jsiShowInputLine()) {
  945. jsiConsolePrintChar('\r');
  946. jsiConsolePrintChar('\n');
  947. }
  948. inputLineRemoved = true;
  949. // Get line to execute, and reset inputLine
  950. JsVar *lineToExecute = jsvStringTrimRight(inputLine);
  951. jsvUnLock(inputLine);
  952. inputLine = jsvNewFromEmptyString();
  953. inputCursorPos = 0;
  954. // execute!
  955. JsVar *v = jspEvaluateVar(&p, lineToExecute, 0);
  956. // add input line to history
  957. jsiHistoryAddLine(lineToExecute);
  958. jsvUnLock(lineToExecute);
  959. // print result
  960. if (echo) { // intentionally not using jsiShowInputLine()
  961. jsiConsolePrintChar('=');
  962. jsfPrintJSON(v);
  963. jsiConsolePrint("\n");
  964. }
  965. jsvUnLock(v);
  966. // console will be returned next time around the input loop
  967. } else {
  968. // Brackets aren't all closed, so we're going to append a newline
  969. // without executing
  970. if (jsiShowInputLine()) jsiConsolePrint("\n:");
  971. jsiIsAboutToEditInputLine();
  972. jsvAppendCharacter(inputLine, '\n');
  973. inputCursorPos++;
  974. }
  975. } else { // new line - but not at end of line!
  976. jsiIsAboutToEditInputLine();
  977. if (jsiShowInputLine()) jsiConsoleEraseStringVarFrom(inputLine, inputCursorPos, false/*no need to erase the char before*/); // erase all in front
  978. JsVar *v = jsvNewFromEmptyString();
  979. if (inputCursorPos>0) jsvAppendStringVar(v, inputLine, 0, inputCursorPos);
  980. jsvAppendCharacter(v, '\n');
  981. jsvAppendStringVar(v, inputLine, inputCursorPos, JSVAPPENDSTRINGVAR_MAXLENGTH); // add the rest
  982. jsvUnLock(inputLine);
  983. inputLine=v;
  984. if (jsiShowInputLine()) { // now print the rest
  985. jsiConsolePrintStringVarWithNewLineChar(inputLine, inputCursorPos, ':');
  986. jsiMoveCursorChar(inputLine, (int)jsvGetStringLength(inputLine), inputCursorPos+1); // move cursor back
  987. }
  988. inputCursorPos++;
  989. }
  990. } else if (ch>=32 || ch=='\t') {
  991. // Add the character to our input line
  992. jsiIsAboutToEditInputLine();
  993. int l = (int)jsvGetStringLength(inputLine);
  994. bool hasTab = ch=='\t';
  995. if (inputCursorPos>=l) {
  996. if (hasTab) jsvAppendString(inputLine, " ");
  997. else jsvAppendCharacter(inputLine, ch);
  998. } else {
  999. JsVar *v = jsvNewFromEmptyString();
  1000. if (inputCursorPos>0) jsvAppendStringVar(v, inputLine, 0, inputCursorPos);
  1001. if (hasTab) jsvAppendString(v, " ");
  1002. else jsvAppendCharacter(v, ch);
  1003. jsvAppendStringVar(v, inputLine, inputCursorPos, JSVAPPENDSTRINGVAR_MAXLENGTH); // add the rest
  1004. jsvUnLock(inputLine);
  1005. inputLine=v;
  1006. if (jsiShowInputLine()) jsiConsolePrintStringVarUntilEOL(inputLine, inputCursorPos, true/*and backup*/);
  1007. }
  1008. inputCursorPos += hasTab ? 4 : 1;
  1009. if (jsiShowInputLine()) {
  1010. if (hasTab) jsiConsolePrint(" ");
  1011. else jsiConsolePrintChar(ch);
  1012. }
  1013. }
  1014. }
  1015. }
  1016. void jsiQueueEvents(JsVarRef callbacks, JsVar *arg0, JsVar *arg1) { // array of functions or single function
  1017. if (!callbacks) return;
  1018. JsVar *callbackVar = jsvLock(callbacks);
  1019. // if it is a single callback, just add it
  1020. if (jsvIsFunction(callbackVar) || jsvIsString(callbackVar)) {
  1021. JsVar *event = jsvNewWithFlags(JSV_OBJECT);
  1022. if (event) { // Could be out of memory error!
  1023. jsvUnLock(jsvAddNamedChild(event, callbackVar, "func"));
  1024. if (arg0) jsvUnLock(jsvAddNamedChild(event, arg0, "arg0"));
  1025. if (arg1) jsvUnLock(jsvAddNamedChild(event, arg1, "arg1"));
  1026. jsvArrayPushAndUnLock(events, event);
  1027. }
  1028. jsvUnLock(callbackVar);
  1029. } else {
  1030. assert(jsvIsArray(callbackVar));
  1031. // go through all callbacks
  1032. JsVarRef next = callbackVar->firstChild;
  1033. jsvUnLock(callbackVar);
  1034. while (next) {
  1035. //jsPrint("Queue Event\n");
  1036. JsVar *child = jsvLock(next);
  1037. // for each callback...
  1038. JsVar *event = jsvNewWithFlags(JSV_OBJECT);
  1039. if (event) { // Could be out of memory error!
  1040. jsvUnLock(jsvAddNamedChild(event, child, "func"));
  1041. if (arg0) jsvUnLock(jsvAddNamedChild(event, arg0, "arg0"));
  1042. if (arg1) jsvUnLock(jsvAddNamedChild(event, arg1, "arg1"));
  1043. // add event to the events list
  1044. jsvArrayPushAndUnLock(events, event);
  1045. // go to next callback
  1046. }
  1047. next = child->nextSibling;
  1048. jsvUnLock(child);
  1049. }
  1050. }
  1051. }
  1052. void jsiQueueObjectCallbacks(JsVar *object, const char *callbackName, JsVar *arg0, JsVar *arg1) {
  1053. JsVar *callback = jsvObjectGetChild(object, callbackName, 0);
  1054. if (!callback) return;
  1055. jsiQueueEvents(jsvGetRef(callback), arg0, arg1);
  1056. jsvUnLock(callback);
  1057. }
  1058. void jsiExecuteEvents() {
  1059. bool hasEvents = !jsvArrayIsEmpty(events);
  1060. bool wasInterrupted = jspIsInterrupted();
  1061. if (hasEvents) jsiSetBusy(BUSY_INTERACTIVE, true);
  1062. while (!jsvArrayIsEmpty(events)) {
  1063. JsVar *event = jsvSkipNameAndUnLock(jsvArrayPopFirst(events));
  1064. // Get function to execute
  1065. JsVar *func = jsvObjectGetChild(event, "func", 0);
  1066. JsVar *args[2];
  1067. args[0] = jsvObjectGetChild(event, "arg0", 0);
  1068. args[1] = jsvObjectGetChild(event, "arg1", 0);
  1069. // free
  1070. jsvUnLock(event);
  1071. // now run..
  1072. if (func) {
  1073. if (jsvIsFunction(func))
  1074. jspExecuteFunction(&p, func, 0, 2, args);
  1075. else if (jsvIsString(func))
  1076. jsvUnLock(jspEvaluateVar(&p, func, 0));
  1077. else
  1078. jsError("Unknown type of callback in Event Queue");
  1079. }
  1080. //jsPrint("Event Done\n");
  1081. jsvUnLock(func);
  1082. jsvUnLock(args[0]);
  1083. jsvUnLock(args[1]);
  1084. }
  1085. if (hasEvents) {
  1086. jsiSetBusy(BUSY_INTERACTIVE, false);
  1087. if (!wasInterrupted && jspIsInterrupted())
  1088. interruptedDuringEvent = true;
  1089. }
  1090. }
  1091. void jsiExecuteEventCallback(JsVar *callbackVar, JsVar *arg0, JsVar *arg1) { // array of functions or single function
  1092. bool wasInterrupted = jspIsInterrupted();
  1093. JsVar *callbackNoNames = jsvSkipName(callbackVar);
  1094. if (callbackNoNames) {
  1095. if (jsvIsArray(callbackNoNames)) {
  1096. JsVarRef next = callbackNoNames->firstChild;
  1097. while (next) {
  1098. JsVar *child = jsvLock(next);
  1099. jsiExecuteEventCallback(child, arg0, arg1);
  1100. next = child->nextSibling;
  1101. jsvUnLock(child);
  1102. }
  1103. } else if (jsvIsFunction(callbackNoNames)) {
  1104. JsVar *args[2] = { arg0, arg1 };
  1105. JsVar *parent = 0;
  1106. jspExecuteFunction(&p, callbackNoNames, parent, 2, args);
  1107. } else if (jsvIsString(callbackNoNames))
  1108. jsvUnLock(jspEvaluateVar(&p, callbackNoNames, 0));
  1109. else
  1110. jsError("Unknown type of callback in Event Queue");
  1111. jsvUnLock(callbackNoNames);
  1112. }
  1113. if (!wasInterrupted && jspIsInterrupted())
  1114. interruptedDuringEvent = true;
  1115. }
  1116. bool jsiHasTimers() {
  1117. if (!timerArray) return false;
  1118. JsVar *timerArrayPtr = jsvLock(timerArray);
  1119. JsVarInt c = jsvGetArrayLength(timerArrayPtr);
  1120. jsvUnLock(timerArrayPtr);
  1121. return c>0;
  1122. }
  1123. void jsiPrintUnregisteredMessage(const char *desc) {
  1124. jsiConsolePrint("\n You must have registered Espruino in order to ");
  1125. jsiConsolePrint(desc);
  1126. jsiConsolePrint(".\n\n Please type register() for more information.\n\n");
  1127. }
  1128. void jsiIdle() {
  1129. // This is how many times we have been here and not done anything.
  1130. // It will be zeroed if we do stuff later
  1131. if (loopsIdling<255) loopsIdling++;
  1132. // Handle hardware-related idle stuff (like checking for pin events)
  1133. bool wasBusy = false;
  1134. IOEvent event;
  1135. while (jshPopIOEvent(&event)) {
  1136. jsiSetBusy(BUSY_INTERACTIVE, true);
  1137. wasBusy = true;
  1138. IOEventFlags eventType = IOEVENTFLAGS_GETTYPE(event.flags);
  1139. loopsIdling = 0; // because we're not idling
  1140. if (eventType == consoleDevice) {
  1141. int i, c = IOEVENTFLAGS_GETCHARS(event.flags);
  1142. jsiSetBusy(BUSY_INTERACTIVE, true);
  1143. for (i=0;i<c;i++) jsiHandleChar(event.data.chars[i]);
  1144. jsiSetBusy(BUSY_INTERACTIVE, false);
  1145. }
  1146. if (DEVICE_IS_USART(eventType)) {
  1147. // ------------------------------------------------------------------------ SERIAL CALLBACK
  1148. JsVar *usartClass = jsvSkipNameAndUnLock(jsiGetClassNameFromDevice(IOEVENTFLAGS_GETTYPE(event.flags)));
  1149. if (usartClass) {
  1150. JsVar *callback = jsvFindChildFromString(usartClass, USART_CALLBACK_NAME, false);
  1151. if (callback) {
  1152. int i, c = IOEVENTFLAGS_GETCHARS(event.flags);
  1153. // Part of hackish solution to 7 bit support on STM32
  1154. #ifdef STM32
  1155. unsigned char bytesize = 8;
  1156. unsigned char parity = 0;
  1157. JsVar *options = jsvObjectGetChild(usartClass, DEVICE_OPTIONS_NAME, 0);
  1158. if(jsvIsObject(options)) {
  1159. bytesize = (unsigned char)jsvGetIntegerAndUnLock(jsvObjectGetChild(options, "bytesize", 0));
  1160. JsVar *v = jsvObjectGetChild(options, "parity", 0);
  1161. if(jsvIsString(v)) {
  1162. parity = 0xFF;
  1163. char s[8] = "";
  1164. jsvGetString(v, s, sizeof(s) - 1);
  1165. if(!strcmp(s, "o") || !strcmp(s, "odd")) {
  1166. parity = 1;
  1167. }
  1168. else if(!strcmp(s, "e") || !strcmp(s, "even")) {
  1169. parity = 2;
  1170. }
  1171. }
  1172. else if(jsvIsInt(v)) {
  1173. parity = (unsigned char)jsvGetInteger(v);
  1174. }
  1175. jsvUnLock(v);
  1176. }
  1177. jsvUnLock(options);
  1178. #endif
  1179. for (i=0; i<c; i++) {
  1180. JsVar *data = jsvNewWithFlags(JSV_OBJECT);
  1181. if (data) {
  1182. JsVar *dataTime = jsvNewFromString("X");
  1183. #ifdef STM32
  1184. if(bytesize == 7 && parity > 0) {
  1185. dataTime->varData.str[0] = event.data.chars[i] & 0x7F;
  1186. }
  1187. else if(bytesize == 8 && parity > 0) {
  1188. dataTime->varData.str[0] = event.data.chars[i] & 0xFF;
  1189. }
  1190. else {
  1191. dataTime->varData.str[0] = event.data.chars[i];
  1192. }
  1193. #else
  1194. dataTime->varData.str[0] = event.data.chars[i];
  1195. #endif
  1196. if (dataTime) jsvUnLock(jsvAddNamedChild(data, dataTime, "data"));
  1197. jsvUnLock(dataTime);
  1198. }
  1199. jsiExecuteEventCallback(callback, data, 0);
  1200. jsvUnLock(data);
  1201. }
  1202. }
  1203. jsvUnLock(callback);
  1204. jsvUnLock(usartClass);
  1205. }
  1206. } else if (DEVICE_IS_EXTI(eventType)) { // ---------------------------------------------------------------- PIN WATCH
  1207. // we have an event... find out what it was for...
  1208. // Check everything in our Watch array
  1209. JsVar *watchArrayPtr = jsvLock(watchArray);
  1210. JsVarRef watch = watchArrayPtr->firstChild;
  1211. while (watch) {
  1212. JsVar *watchNamePtr = jsvLock(watch); // effectively the array index
  1213. JsVar *watchPin = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "pin", false));
  1214. Pin pin = jshGetPinFromVar(watchPin); // TODO: could be faster?
  1215. jsvUnLock(watchPin);
  1216. if (jshIsEventForPin(&event, pin)) {
  1217. bool pinIsHigh = (event.flags&EV_EXTI_IS_HIGH)!=0;
  1218. int watchEdge = (int)jsvGetIntegerAndUnLock(jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "edge", false)));
  1219. if (watchEdge==0 || (pinIsHigh && watchEdge>0) || (!pinIsHigh && watchEdge<0)) { // edge triggering
  1220. JsVar *watchCallback = jsvFindChildFromStringRef(watchNamePtr->firstChild, "callback", false);
  1221. bool watchRecurring = jsvGetBoolAndUnLock(jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "recur", false)));
  1222. JsVar *data = jsvNewWithFlags(JSV_OBJECT);
  1223. if (data) {
  1224. JsVar *dataTime = jsvNewFromFloat(jshGetMillisecondsFromTime(event.data.time)/1000);
  1225. if (dataTime) jsvUnLock(jsvAddNamedChild(data, dataTime, "time"));
  1226. jsvUnLock(dataTime);
  1227. JsVar *dataState = jsvNewFromBool(pinIsHigh);
  1228. if (dataState) jsvUnLock(jsvAddNamedChild(data, dataState, "state"));
  1229. jsvUnLock(dataState);
  1230. }
  1231. jsiExecuteEventCallback(watchCallback, data, 0);
  1232. jsvUnLock(data);
  1233. if (!watchRecurring) {
  1234. // free all
  1235. jsvRemoveChild(watchArrayPtr, watchNamePtr);
  1236. }
  1237. jsvUnLock(watchCallback);
  1238. }
  1239. }
  1240. watch = watchNamePtr->nextSibling;
  1241. jsvUnLock(watchNamePtr);
  1242. }
  1243. jsvUnLock(watchArrayPtr);
  1244. }
  1245. }
  1246. // Reset Flow control if it was set...
  1247. if (jshGetEventsUsed() < IOBUFFER_XON) {
  1248. int i;
  1249. for (i=0;i<USARTS;i++)
  1250. jshSetFlowControlXON(EV_SERIAL1+i, true);
  1251. }
  1252. // Check timers
  1253. JsSysTime minTimeUntilNext = JSSYSTIME_MAX;
  1254. JsSysTime time = jshGetSystemTime();
  1255. JsVar *timerArrayPtr = jsvLock(timerArray);
  1256. JsVarRef timer = timerArrayPtr->firstChild;
  1257. while (timer) {
  1258. JsVar *timerNamePtr = jsvLock(timer);
  1259. timer = timerNamePtr->nextSibling; // ptr to next
  1260. JsVar *timerTime = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(timerNamePtr->firstChild, "time", false));
  1261. JsSysTime timeUntilNext = jsvGetInteger(timerTime) - time;
  1262. if (timeUntilNext < minTimeUntilNext)
  1263. minTimeUntilNext = timeUntilNext;
  1264. if (timerTime && timeUntilNext<=0) {
  1265. // we're now doing work
  1266. jsiSetBusy(BUSY_INTERACTIVE, true);
  1267. wasBusy = true;
  1268. JsVar *timerCallback = jsvFindChildFromStringRef(timerNamePtr->firstChild, "callback", false);
  1269. JsVar *timerRecurring = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(timerNamePtr->firstChild, "recur", false));
  1270. JsVar *data = jsvNewWithFlags(JSV_OBJECT);
  1271. if (data) {
  1272. JsVar *dataTime = jsvNewFromFloat(jshGetMillisecondsFromTime(jsvGetInteger(timerTime))/1000);
  1273. if (dataTime) jsvUnLock(jsvAddNamedChild(data, dataTime, "time"));
  1274. jsvUnLock(dataTime);
  1275. }
  1276. jsiExecuteEventCallback(timerCallback, data, 0);
  1277. jsvUnLock(data);
  1278. if (jsvGetBool(timerRecurring)) {
  1279. JsVarFloat interval = jsvGetFloatAndUnLock(jsvSkipNameAndUnLock(jsvFindChildFromStringRef(timerNamePtr->firstChild, "interval", false)));
  1280. if (interval<=0)
  1281. jsvSetInteger(timerTime, time); // just set to current system time
  1282. else
  1283. jsvSetInteger(timerTime, jsvGetInteger(timerTime)+jshGetTimeFromMilliseconds(interval));
  1284. } else {
  1285. // free all
  1286. JsVar *foundChild = jsvFindChildFromVar(timerArrayPtr, timerNamePtr, false);
  1287. if (foundChild) {
  1288. // check it exists - could have been removed during jsiExecuteEventCallback!
  1289. jsvRemoveChild(timerArrayPtr, timerNamePtr);
  1290. jsvUnLock(foundChild);
  1291. }
  1292. }
  1293. jsvUnLock(timerCallback);
  1294. jsvUnLock(timerRecurring);
  1295. }
  1296. jsvUnLock(timerTime);
  1297. jsvUnLock(timerNamePtr);
  1298. }
  1299. jsvUnLock(timerArrayPtr);
  1300. // Check for network events
  1301. #ifdef USE_NET
  1302. httpIdle();
  1303. #endif
  1304. // Just in case we got any events to do and didn't clear loopsIdling before
  1305. if (wasBusy || !jsvArrayIsEmpty(events) )
  1306. loopsIdling = 0;
  1307. if (wasBusy)
  1308. jsiSetBusy(BUSY_INTERACTIVE, false);
  1309. // TODO: could now sort events by time?
  1310. // execute any outstanding events
  1311. if (!jspIsInterrupted()) {
  1312. jsiExecuteEvents();
  1313. }
  1314. if (interruptedDuringEvent) {
  1315. jspSetInterrupted(false);
  1316. interruptedDuringEvent = false;
  1317. jsiConsolePrint("Execution Interrupted during event processing - clearing all timers and watches.\n");
  1318. jswrap_interface_clearInterval(0);
  1319. jswrap_interface_clearWatch(0);
  1320. }
  1321. // check for TODOs
  1322. if (todo) {
  1323. jsiSetBusy(BUSY_INTERACTIVE, true);
  1324. if (todo & TODO_RESET) {
  1325. todo &= (TODOFlags)~TODO_RESET;
  1326. // shut down everything and start up again
  1327. jsiKill();
  1328. jsiInit(false); // don't autoload
  1329. }
  1330. if (todo & TODO_FLASH_SAVE) {
  1331. todo &= (TODOFlags)~TODO_FLASH_SAVE;
  1332. jsvGarbageCollect(); // nice to have everything all tidy!
  1333. jsiSoftKill();
  1334. jspSoftKill(&p);
  1335. jsvSoftKill();
  1336. jshSaveToFlash();
  1337. jsvSoftInit();
  1338. jspSoftInit(&p);
  1339. jsiSoftInit();
  1340. }
  1341. if (todo & TODO_FLASH_LOAD) {
  1342. todo &= (TODOFlags)~TODO_FLASH_LOAD;
  1343. jsiSoftKill();
  1344. jspSoftKill(&p);
  1345. jsvSoftKill();
  1346. jshLoadFromFlash();
  1347. jsvSoftInit();
  1348. jspSoftInit(&p);
  1349. jsiSoftInit();
  1350. }
  1351. jsiSetBusy(BUSY_INTERACTIVE, false);
  1352. }
  1353. /* if we've been around this loop, there is nothing to do, and
  1354. * we have a spare 10ms then let's do some Garbage Collection
  1355. * just in case. */
  1356. if (loopsIdling==1 &&
  1357. minTimeUntilNext > jshGetTimeFromMilliseconds(10)) {
  1358. jsiSetBusy(BUSY_INTERACTIVE, true);
  1359. jsvGarbageCollect();
  1360. jsiSetBusy(BUSY_INTERACTIVE, false);
  1361. }
  1362. // Go to sleep!
  1363. if (loopsIdling>1 && // once around the idle loop without having done any work already (just in case)
  1364. #ifdef USB
  1365. !jshIsUSBSERIALConnected() && // if USB is on, no point sleeping (later, sleep might be more drastic)
  1366. #endif
  1367. !jshHasEvents() && //no events have arrived in the mean time
  1368. !jshHasTransmitData()/* && //nothing left to send over serial?
  1369. minTimeUntilNext > SYSTICK_RANGE*5/4*/) { // we are sure we won't miss anything - leave a little leeway (SysTick will wake us up!)
  1370. jshSleep(minTimeUntilNext);
  1371. }
  1372. }
  1373. void jsiLoop() {
  1374. // idle stuff for hardware
  1375. jshIdle();
  1376. // Do general idle stuff
  1377. jsiIdle();
  1378. // Idle LCD
  1379. #ifdef USE_LCD
  1380. graphicsIdle();
  1381. #endif
  1382. if (jspIsInterrupted()) {
  1383. jspSetInterrupted(false);
  1384. jsiConsoleRemoveInputLine();
  1385. // clear input line
  1386. jsvUnLock(inputLine);
  1387. inputLine = jsvNewFromEmptyString();
  1388. }
  1389. // return console (if it was gone!)
  1390. jsiReturnInputLine();
  1391. }
  1392. /** Output extra functions defined in an object such that they can be copied to a new device */
  1393. void jsiDumpObjectState(JsVar *parentName, JsVar *parent) {
  1394. JsVarRef childRef = parent->firstChild;
  1395. while (childRef) {
  1396. JsVar *child = jsvLock(childRef);
  1397. JsVar *data = jsvSkipName(child);
  1398. if (jsvIsStringEqual(child, JSPARSE_PROTOTYPE_VAR)) {
  1399. JsVarRef protoRef = data->firstChild;
  1400. while (protoRef) {
  1401. JsVar *proto = jsvLock(protoRef);
  1402. jsiConsolePrintStringVar(parentName);
  1403. jsiConsolePrint(".prototype.");
  1404. jsiConsolePrintStringVar(proto);
  1405. jsiConsolePrint(" = ");
  1406. JsVar *protoData = jsvSkipName(proto);
  1407. jsfPrintJSON(protoData);
  1408. jsvUnLock(protoData);
  1409. jsiConsolePrint(";\n");
  1410. protoRef = proto->nextSibling;
  1411. jsvUnLock(proto);
  1412. }
  1413. } else {
  1414. jsiConsolePrintStringVar(parentName);
  1415. jsiConsolePrint(".");
  1416. jsiConsolePrintStringVar(child);
  1417. jsiConsolePrint(" = ");
  1418. jsfPrintJSON(data);
  1419. jsiConsolePrint(";\n");
  1420. }
  1421. jsvUnLock(data);
  1422. childRef = child->nextSibling;
  1423. jsvUnLock(child);
  1424. }
  1425. }
  1426. /** Output current interpreter state such that it can be copied to a new device */
  1427. void jsiDumpState() {
  1428. JsVarRef childRef = p.root->firstChild;
  1429. while (childRef) {
  1430. JsVar *child = jsvLock(childRef);
  1431. char childName[JSLEX_MAX_TOKEN_LENGTH];
  1432. jsvGetString(child, childName, JSLEX_MAX_TOKEN_LENGTH);
  1433. JsVar *data = jsvSkipName(child);
  1434. if (jspIsCreatedObject(&p, data) || jswIsBuiltInObject(childName)) {
  1435. jsiDumpObjectState(child, data);
  1436. } else if (jsvIsStringEqual(child, JSI_TIMERS_NAME)) {
  1437. // skip - done later
  1438. } else if (jsvIsStringEqual(child, JSI_WATCHES_NAME)) {
  1439. // skip - done later
  1440. } else if (child->varData.str[0]==JS_HIDDEN_CHAR ||
  1441. jshFromDeviceString(childName)!=EV_NONE) {
  1442. // skip - don't care about this stuff
  1443. } else if (!jsvIsNative(data)) { // just a variable/function!
  1444. if (jsvIsFunction(data)) {
  1445. // function-specific output
  1446. jsiConsolePrint("function ");
  1447. jsiConsolePrintStringVar(child);
  1448. jsfPrintJSONForFunction(data);
  1449. jsiConsolePrint("\n");
  1450. // print any prototypes we had
  1451. JsVar *proto = jsvObjectGetChild(data, JSPARSE_PROTOTYPE_VAR, 0);
  1452. if (proto) {
  1453. JsVarRef protoRef = proto->firstChild;
  1454. jsvUnLock(proto);
  1455. while (protoRef) {
  1456. JsVar *protoName = jsvLock(protoRef);
  1457. JsVar *protoData = jsvSkipName(protoName);
  1458. jsiConsolePrintStringVar(child);
  1459. jsiConsolePrint(".prototype.");
  1460. jsiConsolePrintStringVar(protoName);
  1461. jsiConsolePrint(" = ");
  1462. jsfPrintJSON(protoData);
  1463. jsiConsolePrint(";\n");
  1464. jsvUnLock(protoData);
  1465. protoRef = protoName->nextSibling;
  1466. jsvUnLock(protoName);
  1467. }
  1468. }
  1469. } else {
  1470. // normal variable definition
  1471. jsiConsolePrint("var ");
  1472. jsiConsolePrintStringVar(child);
  1473. jsiConsolePrint(" = ");
  1474. jsfPrintJSON(data);
  1475. jsiConsolePrint(";\n");
  1476. }
  1477. }
  1478. jsvUnLock(data);
  1479. childRef = child->nextSibling;
  1480. jsvUnLock(child);
  1481. }
  1482. // Now do timers
  1483. JsVar *timerArrayPtr = jsvLock(timerArray);
  1484. JsVarRef timerRef = timerArrayPtr->firstChild;
  1485. jsvUnLock(timerArrayPtr);
  1486. while (timerRef) {
  1487. JsVar *timerNamePtr = jsvLock(timerRef);
  1488. JsVar *timerCallback = jsvSkipOneNameAndUnLock(jsvFindChildFromStringRef(timerNamePtr->firstChild, "callback", false));
  1489. bool recur = jsvGetBoolAndUnLock(jsvSkipNameAndUnLock(jsvFindChildFromStringRef(timerNamePtr->firstChild, "recur", false)));
  1490. JsVar *timerInterval = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(timerNamePtr->firstChild, "interval", false));
  1491. jsiConsolePrint(recur ? "setInterval(" : "setTimeout(");
  1492. jsfPrintJSON(timerCallback);
  1493. jsiConsolePrint(", ");
  1494. jsfPrintJSON(timerInterval);
  1495. jsiConsolePrint(");\n");
  1496. jsvUnLock(timerInterval);
  1497. jsvUnLock(timerCallback);
  1498. // next
  1499. timerRef = timerNamePtr->nextSibling;
  1500. jsvUnLock(timerNamePtr);
  1501. }
  1502. // Now do watches
  1503. {
  1504. JsVar *watchArrayPtr = jsvLock(watchArray);
  1505. JsVarRef watchRef = watchArrayPtr->firstChild;
  1506. jsvUnLock(watchArrayPtr);
  1507. while (watchRef) {
  1508. JsVar *watchNamePtr = jsvLock(watchRef);
  1509. JsVar *watchCallback = jsvSkipOneNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "callback", false));
  1510. bool watchRecur = jsvGetBoolAndUnLock(jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "recur", false)));
  1511. int watchEdge = (int)jsvGetIntegerAndUnLock(jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "edge", false)));
  1512. JsVar *watchPin = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "pin", false));
  1513. jsiConsolePrint("setWatch(");
  1514. jsfPrintJSON(watchCallback);
  1515. jsiConsolePrint(", ");
  1516. jsfPrintJSON(watchPin);
  1517. jsiConsolePrint(", { repeat:");
  1518. jsiConsolePrint(watchRecur?"true":"false");
  1519. jsiConsolePrint(", edge:");
  1520. if (watchEdge<0) jsiConsolePrint("'falling'");
  1521. else if (watchEdge>0) jsiConsolePrint("'rising'");
  1522. else jsiConsolePrint("'both'");
  1523. jsiConsolePrint(" });\n");
  1524. jsvUnLock(watchPin);
  1525. jsvUnLock(watchCallback);
  1526. // next
  1527. watchRef = watchNamePtr->nextSibling;
  1528. jsvUnLock(watchNamePtr);
  1529. }
  1530. }
  1531. // and now serial
  1532. JsVar *str = jsvNewFromEmptyString();
  1533. jsiAppendHardwareInitialisation(str, true);
  1534. jsiConsolePrintStringVar(str);
  1535. jsvUnLock(str);
  1536. }
  1537. void jsiSetTodo(TODOFlags newTodo) {
  1538. todo = newTodo;
  1539. }
  1540. JsParse *jsiGetParser() {
  1541. return &p;
  1542. }