mirror of
https://github.com/espruino/Espruino.git
synced 2025-12-08 19:06:15 +00:00
Add a backtrace command to debugger, add console.trace command, set Error.stack in constructor (fix #2490)
Error.stack/stack dump now uses more standard file:line:col format
Out of memory errors now show a backtrace (previously it was very hard to track these down)
This commit is contained in:
parent
c1eb6941b6
commit
159239053c
13
.gdbinit
13
.gdbinit
@ -2,10 +2,10 @@ break jsAssertFail
|
|||||||
break jsError
|
break jsError
|
||||||
define jsvTrace
|
define jsvTrace
|
||||||
print jsvTrace($arg0, 0)
|
print jsvTrace($arg0, 0)
|
||||||
end
|
end
|
||||||
define whereami
|
define whereami
|
||||||
print jslPrintPosition(jsiConsolePrintString, 0, lex->tokenLastStart)
|
print jslPrintPosition(jsiConsolePrintString, 0, lex, lex->tokenLastStart)
|
||||||
print jslPrintTokenLineMarker(jsiConsolePrintString, 0, lex->tokenLastStart, 0)
|
print jslPrintTokenLineMarker(jsiConsolePrintString, 0, lex, lex->tokenLastStart, 0)
|
||||||
end
|
end
|
||||||
define typeof
|
define typeof
|
||||||
if (($arg0)->flags&JSV_VARTYPEMASK)>=JSV_NAME_STRING_0 && (($arg0)->flags&JSV_VARTYPEMASK)<=JSV_NAME_STRING_MAX
|
if (($arg0)->flags&JSV_VARTYPEMASK)>=JSV_NAME_STRING_0 && (($arg0)->flags&JSV_VARTYPEMASK)<=JSV_NAME_STRING_MAX
|
||||||
@ -25,7 +25,7 @@ end
|
|||||||
define asm
|
define asm
|
||||||
set disassemble-next-line on
|
set disassemble-next-line on
|
||||||
show disassemble-next-line
|
show disassemble-next-line
|
||||||
echo now use stepi
|
echo now use stepi
|
||||||
end
|
end
|
||||||
# Watchdog timer off for NRF52 devices
|
# Watchdog timer off for NRF52 devices
|
||||||
define wdt_off
|
define wdt_off
|
||||||
@ -56,9 +56,6 @@ define execflags
|
|||||||
if execInfo.execute&EXEC_ERROR
|
if execInfo.execute&EXEC_ERROR
|
||||||
printf "EXEC_ERROR\n"
|
printf "EXEC_ERROR\n"
|
||||||
end
|
end
|
||||||
if execInfo.execute&EXEC_ERROR_LINE_REPORTED
|
|
||||||
printf "EXEC_ERROR_LINE_REPORTED\n"
|
|
||||||
end
|
|
||||||
if execInfo.execute&EXEC_FOR_INIT
|
if execInfo.execute&EXEC_FOR_INIT
|
||||||
printf "EXEC_FOR_INIT\n"
|
printf "EXEC_FOR_INIT\n"
|
||||||
end
|
end
|
||||||
@ -82,7 +79,7 @@ end
|
|||||||
|
|
||||||
define hook-run
|
define hook-run
|
||||||
set $primask=0
|
set $primask=0
|
||||||
end
|
end
|
||||||
|
|
||||||
define hook-continue
|
define hook-continue
|
||||||
set $primask=0
|
set $primask=0
|
||||||
|
|||||||
@ -5,6 +5,9 @@
|
|||||||
Bangle.js: g.findFont now attempts to use Intl:2 if there's room. Also fix memory leak
|
Bangle.js: g.findFont now attempts to use Intl:2 if there's room. Also fix memory leak
|
||||||
nRF52840: Add E.getVDDH() method to get VDDH voltage
|
nRF52840: Add E.getVDDH() method to get VDDH voltage
|
||||||
Fix issue handling multi-line tempated strings pasted to REPL (2v26 regression)
|
Fix issue handling multi-line tempated strings pasted to REPL (2v26 regression)
|
||||||
|
Add a backtrace command to debugger, add console.trace command, set Error.stack in constructor (fix #2490)
|
||||||
|
Error.stack/stack dump now uses more standard file:line:col format
|
||||||
|
Out of memory errors now show a backtrace (previously it was very hard to track these down)
|
||||||
|
|
||||||
2v26 : nRF5x: ensure TIMER1_IRQHandler doesn't always wake idle loop up (fix #1900)
|
2v26 : nRF5x: ensure TIMER1_IRQHandler doesn't always wake idle loop up (fix #1900)
|
||||||
Puck.js: On v2.1 ensure Puck.mag behaves like other variants - just returning the last reading (avoids glitches when used with Puck.magOn)
|
Puck.js: On v2.1 ensure Puck.mag behaves like other variants - just returning the last reading (avoids glitches when used with Puck.magOn)
|
||||||
|
|||||||
@ -1414,8 +1414,10 @@ bool jsfLoadBootCodeFromFlash(bool isReset) {
|
|||||||
#endif
|
#endif
|
||||||
if (jsiStatus & JSIS_FIRST_BOOT) {
|
if (jsiStatus & JSIS_FIRST_BOOT) {
|
||||||
JsVar *code = jsfReadFile(jsfNameFromString(".bootPowerOn"),0,0);
|
JsVar *code = jsfReadFile(jsfNameFromString(".bootPowerOn"),0,0);
|
||||||
if (code)
|
if (code) {
|
||||||
jsvUnLock2(jspEvaluateVar(code,0,0), code);
|
jsvUnLock2(jspEvaluateVar(code,0,".bootPowerOn",0), code);
|
||||||
|
jsiCheckErrors();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// Load code in .boot0/1/2/3 UNLESS BTN1 IS HELD DOWN FOR BANGLE.JS ON FIRST BOOT (BTN3 for Dickens)
|
// Load code in .boot0/1/2/3 UNLESS BTN1 IS HELD DOWN FOR BANGLE.JS ON FIRST BOOT (BTN3 for Dickens)
|
||||||
@ -1434,14 +1436,17 @@ bool jsfLoadBootCodeFromFlash(bool isReset) {
|
|||||||
for (int i=0;i<4;i++) {
|
for (int i=0;i<4;i++) {
|
||||||
filename[5] = (char)('0'+i);
|
filename[5] = (char)('0'+i);
|
||||||
JsVar *code = jsfReadFile(jsfNameFromString(filename),0,0);
|
JsVar *code = jsfReadFile(jsfNameFromString(filename),0,0);
|
||||||
if (code)
|
if (code) {
|
||||||
jsvUnLock2(jspEvaluateVar(code,0,0), code);
|
jsvUnLock2(jspEvaluateVar(code,0,filename,0), code);
|
||||||
|
jsiCheckErrors();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Load normal boot code
|
// Load normal boot code
|
||||||
JsVar *code = jsfGetBootCodeFromFlash(isReset);
|
JsVar *code = jsfGetBootCodeFromFlash(isReset);
|
||||||
if (!code) return false;
|
if (!code) return false;
|
||||||
jsvUnLock2(jspEvaluateVar(code,0,0), code);
|
jsvUnLock2(jspEvaluateVar(code,0,"boot code",0), code);
|
||||||
|
jsiCheckErrors();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -137,7 +137,6 @@ JsErrorFlags lastJsErrorFlags = 0; ///< Compare with jsErrorFlags in order to re
|
|||||||
#ifdef USE_DEBUGGER
|
#ifdef USE_DEBUGGER
|
||||||
void jsiDebuggerLine(JsVar *line);
|
void jsiDebuggerLine(JsVar *line);
|
||||||
#endif
|
#endif
|
||||||
void jsiCheckErrors();
|
|
||||||
|
|
||||||
static void jsiPacketFileEnd();
|
static void jsiPacketFileEnd();
|
||||||
static void jsiPacketExit();
|
static void jsiPacketExit();
|
||||||
@ -535,11 +534,13 @@ void jsiSoftInit(bool hasBeenReset) {
|
|||||||
|
|
||||||
// Run 'boot code' - textual JS in flash
|
// Run 'boot code' - textual JS in flash
|
||||||
jsfLoadBootCodeFromFlash(hasBeenReset);
|
jsfLoadBootCodeFromFlash(hasBeenReset);
|
||||||
|
// jsiCheckErrors is performed internally
|
||||||
|
|
||||||
// Now run initialisation code
|
// Now run initialisation code
|
||||||
JsVar *initCode = jsvObjectGetChildIfExists(execInfo.hiddenRoot, JSI_INIT_CODE_NAME);
|
JsVar *initCode = jsvObjectGetChildIfExists(execInfo.hiddenRoot, JSI_INIT_CODE_NAME);
|
||||||
if (initCode) {
|
if (initCode) {
|
||||||
jsvUnLock2(jspEvaluateVar(initCode, 0, 0), initCode);
|
jsvUnLock2(jspEvaluateVar(initCode, 0, "initcode", 0), initCode);
|
||||||
|
jsiCheckErrors();
|
||||||
jsvObjectRemoveChild(execInfo.hiddenRoot, JSI_INIT_CODE_NAME);
|
jsvObjectRemoveChild(execInfo.hiddenRoot, JSI_INIT_CODE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -565,11 +566,13 @@ void jsiSoftInit(bool hasBeenReset) {
|
|||||||
|
|
||||||
// Execute `init` events on `E`
|
// Execute `init` events on `E`
|
||||||
jsiExecuteEventCallbackOn("E", INIT_CALLBACK_NAME, 0, 0);
|
jsiExecuteEventCallbackOn("E", INIT_CALLBACK_NAME, 0, 0);
|
||||||
|
jsiCheckErrors();
|
||||||
// Execute the `onInit` function
|
// Execute the `onInit` function
|
||||||
JsVar *onInit = jsvObjectGetChildIfExists(execInfo.root, JSI_ONINIT_NAME);
|
JsVar *onInit = jsvObjectGetChildIfExists(execInfo.root, JSI_ONINIT_NAME);
|
||||||
if (onInit) {
|
if (onInit) {
|
||||||
if (jsiEcho()) jsiConsolePrint("Running onInit()...\n");
|
if (jsiEcho()) jsiConsolePrint("Running onInit()...\n");
|
||||||
jsiExecuteEventCallback(0, onInit, 0, 0);
|
jsiExecuteEventCallback(0, onInit, 0, 0);
|
||||||
|
jsiCheckErrors();
|
||||||
jsvUnLock(onInit);
|
jsvUnLock(onInit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1528,7 +1531,7 @@ void jsiHandleNewLine(bool execute) {
|
|||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
// execute!
|
// execute!
|
||||||
JsVar *v = jspEvaluateVar(lineToExecute, 0, jsiLineNumberOffset);
|
JsVar *v = jspEvaluateVar(lineToExecute, 0, "REPL", jsiLineNumberOffset);
|
||||||
// add input line to history
|
// add input line to history
|
||||||
bool isEmpty = jsvIsEmptyString(lineToExecute);
|
bool isEmpty = jsvIsEmptyString(lineToExecute);
|
||||||
// Don't store history if we're not echoing back to the console (it probably wasn't typed by the user)
|
// Don't store history if we're not echoing back to the console (it probably wasn't typed by the user)
|
||||||
@ -2040,7 +2043,7 @@ static NO_INLINE bool jsiExecuteEventCallbackInner(JsVar *thisVar, JsVar *callba
|
|||||||
} else if (jsvIsFunction(callbackNoNames)) {
|
} else if (jsvIsFunction(callbackNoNames)) {
|
||||||
jsvUnLock(jspExecuteFunction(callbackNoNames, thisVar, (int)argCount, argPtr));
|
jsvUnLock(jspExecuteFunction(callbackNoNames, thisVar, (int)argCount, argPtr));
|
||||||
} else if (jsvIsString(callbackNoNames)) {
|
} else if (jsvIsString(callbackNoNames)) {
|
||||||
jsvUnLock(jspEvaluateVar(callbackNoNames, 0, 0));
|
jsvUnLock(jspEvaluateVar(callbackNoNames, 0, "event", 0));
|
||||||
} else
|
} else
|
||||||
jsError("Unknown type of callback in Event Queue");
|
jsError("Unknown type of callback in Event Queue");
|
||||||
return ok;
|
return ok;
|
||||||
@ -2146,7 +2149,7 @@ void jsiCtrlC() {
|
|||||||
/** Take an event for a UART and handle the characters we're getting, potentially
|
/** Take an event for a UART and handle the characters we're getting, potentially
|
||||||
* grabbing more characters as well if it's easy. If more character events are
|
* grabbing more characters as well if it's easy. If more character events are
|
||||||
* grabbed, the number of extra events (not characters) is returned */
|
* grabbed, the number of extra events (not characters) is returned */
|
||||||
int jsiHandleIOEventForSerial(JsVar *usartClass, IOEventFlags eventFlags, uint8_t *data, int length) {
|
int jsiHandleIOEventForSerial(JsVar *usartClass, IOEventFlags eventFlags, uint8_t *data, unsigned int length) {
|
||||||
int eventsHandled = length+2;
|
int eventsHandled = length+2;
|
||||||
JsVar *stringData = length ? jsvNewStringOfLength(length, (char*)data) : NULL;
|
JsVar *stringData = length ? jsvNewStringOfLength(length, (char*)data) : NULL;
|
||||||
if (stringData) {
|
if (stringData) {
|
||||||
@ -2178,7 +2181,7 @@ void jsiIdle() {
|
|||||||
bool wasBusy = false;
|
bool wasBusy = false;
|
||||||
IOEventFlags eventFlags;
|
IOEventFlags eventFlags;
|
||||||
uint8_t eventData[IOEVENT_MAX_LEN];
|
uint8_t eventData[IOEVENT_MAX_LEN];
|
||||||
int eventLen;
|
unsigned int eventLen;
|
||||||
// ensure we can't get totally swamped by having more events than we can process.
|
// ensure we can't get totally swamped by having more events than we can process.
|
||||||
// Just process what was in the event queue at the start
|
// Just process what was in the event queue at the start
|
||||||
int maxEvents = jshGetEventsUsed();
|
int maxEvents = jshGetEventsUsed();
|
||||||
@ -2564,8 +2567,8 @@ void jsiIdle() {
|
|||||||
jsiSemiInit(false, &filename); // don't autoload code
|
jsiSemiInit(false, &filename); // don't autoload code
|
||||||
// load the code we specified
|
// load the code we specified
|
||||||
JsVar *code = jsfReadFile(filename,0,0);
|
JsVar *code = jsfReadFile(filename,0,0);
|
||||||
if (code)
|
if (code) // only supply the filename if we're sure it's zero terminated
|
||||||
jsvUnLock2(jspEvaluateVar(code,0,0), code);
|
jsvUnLock2(jspEvaluateVar(code,0,filename.c[sizeof(filename.c)-1] ? filename.c : "load",0), code);
|
||||||
} else {
|
} else {
|
||||||
jsiSoftKill();
|
jsiSoftKill();
|
||||||
jspSoftKill();
|
jspSoftKill();
|
||||||
@ -2805,14 +2808,14 @@ void jsiDebuggerLoop() {
|
|||||||
itostr((JsVarInt)jslGetLineNumber() + (JsVarInt)lex->lineNumberOffset - 1, lineStr, 10);
|
itostr((JsVarInt)jslGetLineNumber() + (JsVarInt)lex->lineNumberOffset - 1, lineStr, 10);
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{ // FIXME: Maybe if executing from a Storage file we use line numbers within that file?
|
||||||
lineStr[0]=0;
|
lineStr[0]=0;
|
||||||
}
|
}
|
||||||
size_t lineLen = strlen(lineStr);
|
size_t lineLen = strlen(lineStr);
|
||||||
while (lineLen < sizeof(lineStr)-1) lineStr[lineLen++]=' ';
|
while (lineLen < sizeof(lineStr)-1) lineStr[lineLen++]=' ';
|
||||||
lineStr[lineLen] = 0;
|
lineStr[lineLen] = 0;
|
||||||
// print the line of code, prefixed by the line number, and with a pointer to the exact character in question
|
// print the line of code, prefixed by the line number, and with a pointer to the exact character in question
|
||||||
jslPrintTokenLineMarker(vcbprintf_callback_jsiConsolePrintString, 0, lex->tokenLastStart, lineStr);
|
jslPrintTokenLineMarker(vcbprintf_callback_jsiConsolePrintString, 0, lex, lex->tokenLastStart, lineStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!(jsiStatus & JSIS_EXIT_DEBUGGER) &&
|
while (!(jsiStatus & JSIS_EXIT_DEBUGGER) &&
|
||||||
@ -2822,7 +2825,7 @@ void jsiDebuggerLoop() {
|
|||||||
jshIdle();
|
jshIdle();
|
||||||
// If we have too many events (> half full) drain the queue
|
// If we have too many events (> half full) drain the queue
|
||||||
uint8_t eventData[IOEVENT_MAX_LEN];
|
uint8_t eventData[IOEVENT_MAX_LEN];
|
||||||
int eventLen;
|
unsigned int eventLen;
|
||||||
while (jshGetEventsUsed()>IOBUFFERMASK*1/2 &&
|
while (jshGetEventsUsed()>IOBUFFERMASK*1/2 &&
|
||||||
!(jsiStatus & JSIS_EXIT_DEBUGGER) &&
|
!(jsiStatus & JSIS_EXIT_DEBUGGER) &&
|
||||||
!(execInfo.execute & EXEC_CTRL_C_MASK)) {
|
!(execInfo.execute & EXEC_CTRL_C_MASK)) {
|
||||||
@ -2906,7 +2909,8 @@ void jsiDebuggerLine(JsVar *line) {
|
|||||||
"finish / f - finish execution of the function call\n"
|
"finish / f - finish execution of the function call\n"
|
||||||
"print ... / p ... - evaluate and print the next argument\n"
|
"print ... / p ... - evaluate and print the next argument\n"
|
||||||
"info locals / i l) - output local variables\n"
|
"info locals / i l) - output local variables\n"
|
||||||
"info scopechain / i s - output all variables in all scopes\n");
|
"info scopechain / i s - output all variables in all scopes\n"
|
||||||
|
"bt - print backtrace\n");
|
||||||
} else if (!strcmp(id,"quit") || !strcmp(id,"q")) {
|
} else if (!strcmp(id,"quit") || !strcmp(id,"q")) {
|
||||||
jsiStatus |= JSIS_EXIT_DEBUGGER;
|
jsiStatus |= JSIS_EXIT_DEBUGGER;
|
||||||
execInfo.execute |= EXEC_INTERRUPTED;
|
execInfo.execute |= EXEC_INTERRUPTED;
|
||||||
@ -2962,6 +2966,8 @@ void jsiDebuggerLine(JsVar *line) {
|
|||||||
} else {
|
} else {
|
||||||
jsiConsolePrint("Unknown command\n");
|
jsiConsolePrint("Unknown command\n");
|
||||||
}
|
}
|
||||||
|
} else if (!strcmp(id,"bt")) {
|
||||||
|
jslPrintStackTrace(vcbprintf_callback_jsiConsolePrintString, NULL, oldLex);
|
||||||
} else
|
} else
|
||||||
handled = false;
|
handled = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -63,6 +63,9 @@ bool jsiExecuteEventCallbackName(JsVar *obj, const char *cbName, unsigned int ar
|
|||||||
/// Utility version of jsiExecuteEventCallback for calling events on global variables
|
/// Utility version of jsiExecuteEventCallback for calling events on global variables
|
||||||
bool jsiExecuteEventCallbackOn(const char *objectName, const char *cbName, unsigned int argCount, JsVar **argPtr);
|
bool jsiExecuteEventCallbackOn(const char *objectName, const char *cbName, unsigned int argCount, JsVar **argPtr);
|
||||||
|
|
||||||
|
/// Check for and report/handle interpreter errors (can be called after executing JS code)
|
||||||
|
void jsiCheckErrors();
|
||||||
|
|
||||||
/// Create a timeout in JS to execute the given native function (outside of an IRQ). Returns the index
|
/// Create a timeout in JS to execute the given native function (outside of an IRQ). Returns the index
|
||||||
JsVar *jsiSetTimeout(void (*functionPtr)(void), JsVarFloat milliseconds);
|
JsVar *jsiSetTimeout(void (*functionPtr)(void), JsVarFloat milliseconds);
|
||||||
/// Clear a timeout in JS given the index returned by jsiSetTimeout
|
/// Clear a timeout in JS given the index returned by jsiSetTimeout
|
||||||
|
|||||||
28
src/jslex.c
28
src/jslex.c
@ -948,6 +948,8 @@ void jslInit(JsVar *var) {
|
|||||||
#ifndef ESPR_NO_LINE_NUMBERS
|
#ifndef ESPR_NO_LINE_NUMBERS
|
||||||
lex->lineNumberOffset = 0;
|
lex->lineNumberOffset = 0;
|
||||||
#endif
|
#endif
|
||||||
|
lex->functionName = NULL;
|
||||||
|
lex->lastLex = NULL;
|
||||||
// set up iterator
|
// set up iterator
|
||||||
jsvStringIteratorNew(&lex->it, lex->sourceVar, 0);
|
jsvStringIteratorNew(&lex->it, lex->sourceVar, 0);
|
||||||
jsvUnLock(lex->it.var); // see jslGetNextCh
|
jsvUnLock(lex->it.var); // see jslGetNextCh
|
||||||
@ -1502,7 +1504,7 @@ void jslPrintTokenisedString(JsVar *code, vcbprintf_callback user_callback, void
|
|||||||
jsvStringIteratorFree(&it);
|
jsvStringIteratorFree(&it);
|
||||||
}
|
}
|
||||||
|
|
||||||
void jslPrintPosition(vcbprintf_callback user_callback, void *user_data, size_t tokenPos) {
|
void jslPrintPosition(vcbprintf_callback user_callback, void *user_data, JsLex *lex, size_t tokenPos) {
|
||||||
size_t line,col;
|
size_t line,col;
|
||||||
#if !defined(SAVE_ON_FLASH) && !defined(ESPR_EMBED)
|
#if !defined(SAVE_ON_FLASH) && !defined(ESPR_EMBED)
|
||||||
if (jsvIsNativeString(lex->sourceVar) || jsvIsFlashString(lex->sourceVar)) {
|
if (jsvIsNativeString(lex->sourceVar) || jsvIsFlashString(lex->sourceVar)) {
|
||||||
@ -1513,7 +1515,7 @@ void jslPrintPosition(vcbprintf_callback user_callback, void *user_data, size_t
|
|||||||
JsVar *fileStr = jsvAddressToVar(fileAddr, jsfGetFileSize(&header));
|
JsVar *fileStr = jsvAddressToVar(fileAddr, jsfGetFileSize(&header));
|
||||||
jsvGetLineAndCol(fileStr, tokenPos + stringAddr - fileAddr, &line, &col);
|
jsvGetLineAndCol(fileStr, tokenPos + stringAddr - fileAddr, &line, &col);
|
||||||
JsVar *name = jsfVarFromName(header.name);
|
JsVar *name = jsfVarFromName(header.name);
|
||||||
cbprintf(user_callback, user_data,"line %d col %d in %v\n", line, col, name);
|
cbprintf(user_callback, user_data,"%v:%d:%d", name, line, col);
|
||||||
jsvUnLock2(fileStr,name);
|
jsvUnLock2(fileStr,name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1524,10 +1526,10 @@ void jslPrintPosition(vcbprintf_callback user_callback, void *user_data, size_t
|
|||||||
if (lex->lineNumberOffset)
|
if (lex->lineNumberOffset)
|
||||||
line += (size_t)lex->lineNumberOffset - 1;
|
line += (size_t)lex->lineNumberOffset - 1;
|
||||||
#endif
|
#endif
|
||||||
cbprintf(user_callback, user_data, "line %d col %d\n", line, col);
|
cbprintf(user_callback, user_data, ":%d:%d", line, col);
|
||||||
}
|
}
|
||||||
|
|
||||||
void jslPrintTokenLineMarker(vcbprintf_callback user_callback, void *user_data, size_t tokenPos, char *prefix) {
|
void jslPrintTokenLineMarker(vcbprintf_callback user_callback, void *user_data, JsLex *lex, size_t tokenPos, char *prefix) {
|
||||||
size_t line = 1,col = 1;
|
size_t line = 1,col = 1;
|
||||||
jsvGetLineAndCol(lex->sourceVar, tokenPos, &line, &col);
|
jsvGetLineAndCol(lex->sourceVar, tokenPos, &line, &col);
|
||||||
size_t startOfLine = jsvGetIndexFromLineAndCol(lex->sourceVar, line, 1);
|
size_t startOfLine = jsvGetIndexFromLineAndCol(lex->sourceVar, line, 1);
|
||||||
@ -1568,3 +1570,21 @@ void jslPrintTokenLineMarker(vcbprintf_callback user_callback, void *user_data,
|
|||||||
user_callback("^\n", user_data);
|
user_callback("^\n", user_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void jslPrintStackTrace(vcbprintf_callback user_callback, void *user_data, JsLex *lex) {
|
||||||
|
while (lex) {
|
||||||
|
user_callback(" at ", user_data);
|
||||||
|
if (lex->functionName) {
|
||||||
|
// can't use cbprintf here as it may try and allocate a var
|
||||||
|
// and we want to be able to use this when we're out of memory
|
||||||
|
char functionName[JSLEX_MAX_TOKEN_LENGTH];
|
||||||
|
jsvGetString(lex->functionName, functionName, sizeof(functionName));
|
||||||
|
user_callback(functionName, user_data);
|
||||||
|
user_callback(" (", user_data);
|
||||||
|
}
|
||||||
|
jslPrintPosition(user_callback, user_data, lex, lex->tokenLastStart);
|
||||||
|
user_callback(lex->functionName ? ")\n":"\n", user_data);
|
||||||
|
jslPrintTokenLineMarker(user_callback, user_data, lex, lex->tokenLastStart, 0);
|
||||||
|
|
||||||
|
lex = lex->lastLex; // go down to next lexer in list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
12
src/jslex.h
12
src/jslex.h
@ -174,6 +174,11 @@ typedef struct JsLex
|
|||||||
*/
|
*/
|
||||||
JsVar *sourceVar; // the actual string var
|
JsVar *sourceVar; // the actual string var
|
||||||
JsvStringIterator it; // Iterator for the string
|
JsvStringIterator it; // Iterator for the string
|
||||||
|
|
||||||
|
/// For stack traces - this is just a pointer to a function name if we have one (it's not 'owned')
|
||||||
|
JsVar *functionName;
|
||||||
|
/// For stack traces - this is just a pointer to the previous Lex on the stack
|
||||||
|
struct JsLex *lastLex;
|
||||||
} JsLex;
|
} JsLex;
|
||||||
|
|
||||||
// The lexer
|
// The lexer
|
||||||
@ -224,10 +229,13 @@ bool jslNeedSpaceBetween(unsigned char lastch, unsigned char ch);
|
|||||||
void jslPrintTokenisedString(JsVar *code, vcbprintf_callback user_callback, void *user_data);
|
void jslPrintTokenisedString(JsVar *code, vcbprintf_callback user_callback, void *user_data);
|
||||||
|
|
||||||
/// Print position in the form 'line X col Y'
|
/// Print position in the form 'line X col Y'
|
||||||
void jslPrintPosition(vcbprintf_callback user_callback, void *user_data, size_t tokenPos);
|
void jslPrintPosition(vcbprintf_callback user_callback, void *user_data, JsLex *lex, size_t tokenPos);
|
||||||
|
|
||||||
/** Print the line of source code at `tokenPos`, prefixed with the string 'prefix' (0=no string).
|
/** Print the line of source code at `tokenPos`, prefixed with the string 'prefix' (0=no string).
|
||||||
* Then, underneath it, print a '^' marker at the column tokenPos was at */
|
* Then, underneath it, print a '^' marker at the column tokenPos was at */
|
||||||
void jslPrintTokenLineMarker(vcbprintf_callback user_callback, void *user_data, size_t tokenPos, char *prefix);
|
void jslPrintTokenLineMarker(vcbprintf_callback user_callback, void *user_data, JsLex *lex, size_t tokenPos, char *prefix);
|
||||||
|
|
||||||
|
/** Prints a full stack trace to the current callback function */
|
||||||
|
void jslPrintStackTrace(vcbprintf_callback user_callback, void *user_data, JsLex *lex);
|
||||||
|
|
||||||
#endif /* JSLEX_H_ */
|
#endif /* JSLEX_H_ */
|
||||||
|
|||||||
@ -74,10 +74,8 @@ void jspSetInterrupted(bool interrupt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set the error flag - set lineReported if we've already output the line number
|
/// Set the error flag - set lineReported if we've already output the line number
|
||||||
void jspSetError(bool lineReported) {
|
void jspSetError() {
|
||||||
execInfo.execute = (execInfo.execute & (JsExecFlags)~EXEC_YES) | EXEC_ERROR;
|
execInfo.execute = (execInfo.execute & (JsExecFlags)~EXEC_YES) | EXEC_ERROR;
|
||||||
if (lineReported)
|
|
||||||
execInfo.execute |= EXEC_ERROR_LINE_REPORTED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool jspHasError() {
|
bool jspHasError() {
|
||||||
@ -100,8 +98,6 @@ void jspeiRemoveScope() {
|
|||||||
if (!execInfo.scopesVar || !jsvGetArrayLength(execInfo.scopesVar)) {
|
if (!execInfo.scopesVar || !jsvGetArrayLength(execInfo.scopesVar)) {
|
||||||
// This should never happen unless there's an interpreter error - no need to have an error message
|
// This should never happen unless there's an interpreter error - no need to have an error message
|
||||||
assert(0);
|
assert(0);
|
||||||
//jsExceptionHere(JSET_INTERNALERROR, "Too many scopes removed");
|
|
||||||
//jspSetError(false);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
jsvUnLock(jsvArrayPop(execInfo.scopesVar));
|
jsvUnLock(jsvArrayPop(execInfo.scopesVar));
|
||||||
@ -237,12 +233,11 @@ void jspSetNoExecute() {
|
|||||||
execInfo.execute = (execInfo.execute & (JsExecFlags)(int)~EXEC_RUN_MASK) | EXEC_NO;
|
execInfo.execute = (execInfo.execute & (JsExecFlags)(int)~EXEC_RUN_MASK) | EXEC_NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
void jspAppendStackTrace(JsVar *stackTrace) {
|
void jspAppendStackTrace(JsVar *stackTrace, JsLex *lex) {
|
||||||
JsvStringIterator it;
|
JsvStringIterator it;
|
||||||
jsvStringIteratorNew(&it, stackTrace, 0);
|
jsvStringIteratorNew(&it, stackTrace, 0);
|
||||||
jsvStringIteratorGotoEnd(&it);
|
jsvStringIteratorGotoEnd(&it);
|
||||||
jslPrintPosition((vcbprintf_callback)jsvStringIteratorPrintfCallback, &it, lex->tokenLastStart);
|
jslPrintStackTrace(jsvStringIteratorPrintfCallback, &it, lex);
|
||||||
jslPrintTokenLineMarker((vcbprintf_callback)jsvStringIteratorPrintfCallback, &it, lex->tokenLastStart, 0);
|
|
||||||
jsvStringIteratorFree(&it);
|
jsvStringIteratorFree(&it);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,18 +251,6 @@ void jspSetException(JsVar *value) {
|
|||||||
}
|
}
|
||||||
// Set the exception flag
|
// Set the exception flag
|
||||||
execInfo.execute = execInfo.execute | EXEC_EXCEPTION;
|
execInfo.execute = execInfo.execute | EXEC_EXCEPTION;
|
||||||
// Try and do a stack trace
|
|
||||||
if (lex) {
|
|
||||||
JsVar *stackTrace = jsvObjectGetChild(execInfo.hiddenRoot, JSPARSE_STACKTRACE_VAR, JSV_STRING_0);
|
|
||||||
if (stackTrace) {
|
|
||||||
jsvAppendPrintf(stackTrace, " at ");
|
|
||||||
jspAppendStackTrace(stackTrace);
|
|
||||||
jsvUnLock(stackTrace);
|
|
||||||
// stop us from printing the trace in the same block
|
|
||||||
execInfo.execute = execInfo.execute | EXEC_ERROR_LINE_REPORTED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return the reported exception if there was one (and clear it). May return undefined even if there was an exception - eg `throw undefined` */
|
/** Return the reported exception if there was one (and clear it). May return undefined even if there was an exception - eg `throw undefined` */
|
||||||
@ -276,13 +259,6 @@ JsVar *jspGetException() {
|
|||||||
if (exceptionName) {
|
if (exceptionName) {
|
||||||
JsVar *exception = jsvSkipName(exceptionName);
|
JsVar *exception = jsvSkipName(exceptionName);
|
||||||
jsvRemoveChildAndUnLock(execInfo.hiddenRoot, exceptionName);
|
jsvRemoveChildAndUnLock(execInfo.hiddenRoot, exceptionName);
|
||||||
|
|
||||||
JsVar *stack = jspGetStackTrace();
|
|
||||||
if (stack && jsvHasChildren(exception)) {
|
|
||||||
jsvObjectSetChild(exception, "stack", stack);
|
|
||||||
}
|
|
||||||
jsvUnLock(stack);
|
|
||||||
|
|
||||||
return exception;
|
return exception;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -311,7 +287,7 @@ NO_INLINE bool jspeFunctionArguments(JsVar *funcVar) {
|
|||||||
strcpy(&buf[1], jslGetTokenValueAsString());
|
strcpy(&buf[1], jslGetTokenValueAsString());
|
||||||
JsVar *param = jsvAddNamedChild(funcVar, 0, buf);
|
JsVar *param = jsvAddNamedChild(funcVar, 0, buf);
|
||||||
if (!param) { // out of memory
|
if (!param) { // out of memory
|
||||||
jspSetError(false);
|
jspSetError();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
param = jsvMakeFunctionParameter(param); // force this to be called a function parameter
|
param = jsvMakeFunctionParameter(param); // force this to be called a function parameter
|
||||||
@ -677,7 +653,7 @@ NO_INLINE JsVar *jspeFunctionCall(JsVar *function, JsVar *functionName, JsVar *t
|
|||||||
// OPT: Probably when calling a function ONCE, use it, otherwise when recursing, make new?
|
// OPT: Probably when calling a function ONCE, use it, otherwise when recursing, make new?
|
||||||
JsVar *functionRoot = jsvNewWithFlags(JSV_FUNCTION);
|
JsVar *functionRoot = jsvNewWithFlags(JSV_FUNCTION);
|
||||||
if (!functionRoot) { // out of memory
|
if (!functionRoot) { // out of memory
|
||||||
jspSetError(false);
|
jspSetError();
|
||||||
jsvUnLock(thisVar);
|
jsvUnLock(thisVar);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -834,11 +810,11 @@ NO_INLINE JsVar *jspeFunctionCall(JsVar *function, JsVar *functionName, JsVar *t
|
|||||||
execInfo.execute &= (JsExecFlags)~EXEC_DEBUGGER_NEXT_LINE;
|
execInfo.execute &= (JsExecFlags)~EXEC_DEBUGGER_NEXT_LINE;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
JsLex newLex;
|
JsLex newLex;
|
||||||
JsLex *oldLex = jslSetLex(&newLex);
|
JsLex *oldLex = jslSetLex(&newLex);
|
||||||
jslInit(functionCode);
|
jslInit(functionCode);
|
||||||
|
newLex.functionName = functionName;
|
||||||
|
newLex.lastLex = oldLex;
|
||||||
jsvUnLock(functionCode); // unlock function code here to reduce amount of locks needed during recursion
|
jsvUnLock(functionCode); // unlock function code here to reduce amount of locks needed during recursion
|
||||||
functionCode = 0;
|
functionCode = 0;
|
||||||
#ifndef ESPR_NO_LINE_NUMBERS
|
#ifndef ESPR_NO_LINE_NUMBERS
|
||||||
@ -889,9 +865,9 @@ NO_INLINE JsVar *jspeFunctionCall(JsVar *function, JsVar *functionName, JsVar *t
|
|||||||
#ifdef USE_DEBUGGER
|
#ifdef USE_DEBUGGER
|
||||||
bool calledDebugger = false;
|
bool calledDebugger = false;
|
||||||
if (execInfo.execute & EXEC_DEBUGGER_MASK) {
|
if (execInfo.execute & EXEC_DEBUGGER_MASK) {
|
||||||
jsiConsolePrint("Value returned is =");
|
jsiConsolePrintf(functionName?"Value returned from %v is =":"Value returned is =", functionName);
|
||||||
jsfPrintJSON(returnVar, JSON_LIMIT | JSON_SOME_NEWLINES | JSON_PRETTY | JSON_SHOW_DEVICES);
|
jsfPrintJSON(returnVar, JSON_LIMIT | JSON_SOME_NEWLINES | JSON_PRETTY | JSON_SHOW_DEVICES);
|
||||||
jsiConsolePrintChar('\n');
|
jsiConsolePrintString("\n"); // prints \r too
|
||||||
if (execInfo.execute & EXEC_DEBUGGER_FINISH_FUNCTION) {
|
if (execInfo.execute & EXEC_DEBUGGER_FINISH_FUNCTION) {
|
||||||
calledDebugger = true;
|
calledDebugger = true;
|
||||||
jsiDebuggerLoop();
|
jsiDebuggerLoop();
|
||||||
@ -904,19 +880,8 @@ NO_INLINE JsVar *jspeFunctionCall(JsVar *function, JsVar *functionName, JsVar *t
|
|||||||
jslKill();
|
jslKill();
|
||||||
jslSetLex(oldLex);
|
jslSetLex(oldLex);
|
||||||
|
|
||||||
if (hasError) {
|
if (hasError)
|
||||||
execInfo.execute |= hasError; // propogate error
|
execInfo.execute |= hasError; // propogate error
|
||||||
JsVar *stackTrace = jsvObjectGetChild(execInfo.hiddenRoot, JSPARSE_STACKTRACE_VAR, JSV_STRING_0);
|
|
||||||
if (stackTrace) {
|
|
||||||
jsvAppendPrintf(stackTrace, jsvIsString(functionName)?"in function %q called from ":
|
|
||||||
"in function called from ", functionName);
|
|
||||||
if (lex) {
|
|
||||||
jspAppendStackTrace(stackTrace);
|
|
||||||
} else
|
|
||||||
jsvAppendPrintf(stackTrace, "system\n");
|
|
||||||
jsvUnLock(stackTrace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return to old 'this' var. No need to unlock as we never locked before */
|
/* Return to old 'this' var. No need to unlock as we never locked before */
|
||||||
@ -1236,7 +1201,7 @@ NO_INLINE JsVar *jspeFactorFunctionCall() {
|
|||||||
|
|
||||||
if (lex->tk==LEX_R_NEW) {
|
if (lex->tk==LEX_R_NEW) {
|
||||||
jsExceptionHere(JSET_ERROR, "Nesting 'new' operators is unsupported");
|
jsExceptionHere(JSET_ERROR, "Nesting 'new' operators is unsupported");
|
||||||
jspSetError(false);
|
jspSetError();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1373,7 +1338,7 @@ NO_INLINE JsVar *jspeFactorObject() {
|
|||||||
if (JSP_SHOULD_EXECUTE) {
|
if (JSP_SHOULD_EXECUTE) {
|
||||||
JsVar *contents = jsvNewObject();
|
JsVar *contents = jsvNewObject();
|
||||||
if (!contents) { // out of memory
|
if (!contents) { // out of memory
|
||||||
jspSetError(false);
|
jspSetError();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/* JSON-style object definition */
|
/* JSON-style object definition */
|
||||||
@ -1466,7 +1431,7 @@ NO_INLINE JsVar *jspeFactorArray() {
|
|||||||
if (JSP_SHOULD_EXECUTE) {
|
if (JSP_SHOULD_EXECUTE) {
|
||||||
contents = jsvNewEmptyArray();
|
contents = jsvNewEmptyArray();
|
||||||
if (!contents) { // out of memory
|
if (!contents) { // out of memory
|
||||||
jspSetError(false);
|
jspSetError();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2325,17 +2290,6 @@ NO_INLINE void jspeBlockNoBrackets() {
|
|||||||
JsVar *a = jspeStatement();
|
JsVar *a = jspeStatement();
|
||||||
jsvCheckReferenceError(a);
|
jsvCheckReferenceError(a);
|
||||||
jsvUnLock(a);
|
jsvUnLock(a);
|
||||||
if (JSP_HAS_ERROR) {
|
|
||||||
if (lex && !(execInfo.execute&EXEC_ERROR_LINE_REPORTED)) {
|
|
||||||
execInfo.execute = (JsExecFlags)(execInfo.execute | EXEC_ERROR_LINE_REPORTED);
|
|
||||||
JsVar *stackTrace = jsvObjectGetChild(execInfo.hiddenRoot, JSPARSE_STACKTRACE_VAR, JSV_STRING_0);
|
|
||||||
if (stackTrace) {
|
|
||||||
jsvAppendPrintf(stackTrace, "at ");
|
|
||||||
jspAppendStackTrace(stackTrace);
|
|
||||||
jsvUnLock(stackTrace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (JSP_SHOULDNT_PARSE)
|
if (JSP_SHOULDNT_PARSE)
|
||||||
break;
|
break;
|
||||||
if (!JSP_SHOULD_EXECUTE) {
|
if (!JSP_SHOULD_EXECUTE) {
|
||||||
@ -2415,7 +2369,7 @@ NO_INLINE JsVar *jspeStatementVar() {
|
|||||||
jsvUnLock(scope);
|
jsvUnLock(scope);
|
||||||
#endif
|
#endif
|
||||||
if (!a) { // out of memory
|
if (!a) { // out of memory
|
||||||
jspSetError(false);
|
jspSetError();
|
||||||
return lastDefined;
|
return lastDefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2909,7 +2863,7 @@ NO_INLINE JsVar *jspeStatementTry() {
|
|||||||
}
|
}
|
||||||
if (shouldExecuteBefore) {
|
if (shouldExecuteBefore) {
|
||||||
// Now clear the exception flag (it's handled - we hope!)
|
// Now clear the exception flag (it's handled - we hope!)
|
||||||
execInfo.execute = execInfo.execute & (JsExecFlags)~(EXEC_EXCEPTION|EXEC_ERROR_LINE_REPORTED);
|
execInfo.execute = execInfo.execute & (JsExecFlags)~EXEC_EXCEPTION;
|
||||||
jsvUnLock(exception);
|
jsvUnLock(exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3287,17 +3241,18 @@ JsVar *jspEvaluateExpressionVar(JsVar *str) {
|
|||||||
|
|
||||||
/** Execute code form a variable and return the result. If lineNumberOffset
|
/** Execute code form a variable and return the result. If lineNumberOffset
|
||||||
* is nonzero it's added to the line numbers that get reported for errors/debug */
|
* is nonzero it's added to the line numbers that get reported for errors/debug */
|
||||||
JsVar *jspEvaluateVar(JsVar *str, JsVar *scope, uint16_t lineNumberOffset) {
|
JsVar *jspEvaluateVar(JsVar *str, JsVar *scope, const char *stackTraceName, uint16_t lineNumberOffset) {
|
||||||
JsLex lex;
|
JsLex lex;
|
||||||
|
|
||||||
assert(jsvIsString(str));
|
assert(jsvIsString(str));
|
||||||
JsLex *oldLex = jslSetLex(&lex);
|
JsLex *oldLex = jslSetLex(&lex);
|
||||||
jslInit(str);
|
jslInit(str);
|
||||||
|
lex.lastLex = oldLex;
|
||||||
|
lex.functionName = stackTraceName?jsvNewFromString(stackTraceName):0;
|
||||||
#ifndef ESPR_NO_LINE_NUMBERS
|
#ifndef ESPR_NO_LINE_NUMBERS
|
||||||
lex.lineNumberOffset = lineNumberOffset;
|
lex.lineNumberOffset = lineNumberOffset;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
JsExecInfo oldExecInfo = execInfo;
|
JsExecInfo oldExecInfo = execInfo;
|
||||||
execInfo.execute = EXEC_YES;
|
execInfo.execute = EXEC_YES;
|
||||||
if (scope) {
|
if (scope) {
|
||||||
@ -3316,6 +3271,7 @@ JsVar *jspEvaluateVar(JsVar *str, JsVar *scope, uint16_t lineNumberOffset) {
|
|||||||
// clean up
|
// clean up
|
||||||
if (scope) jspeiClearScopes();
|
if (scope) jspeiClearScopes();
|
||||||
jslKill();
|
jslKill();
|
||||||
|
jsvUnLock(lex.functionName);
|
||||||
jslSetLex(oldLex);
|
jslSetLex(oldLex);
|
||||||
|
|
||||||
// restore state and execInfo (keep error flags & ctrl-c)
|
// restore state and execInfo (keep error flags & ctrl-c)
|
||||||
@ -3342,7 +3298,7 @@ JsVar *jspEvaluate(const char *str, bool stringIsStatic) {
|
|||||||
|
|
||||||
JsVar *v = 0;
|
JsVar *v = 0;
|
||||||
if (!jsvIsMemoryFull())
|
if (!jsvIsMemoryFull())
|
||||||
v = jspEvaluateVar(evCode, 0, 0);
|
v = jspEvaluateVar(evCode, 0, "[raw]", 0);
|
||||||
jsvUnLock(evCode);
|
jsvUnLock(evCode);
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
@ -3423,7 +3379,7 @@ JsVar *jspEvaluateModule(JsVar *moduleContents) {
|
|||||||
execInfo.blockCount = 0;
|
execInfo.blockCount = 0;
|
||||||
#endif
|
#endif
|
||||||
execInfo.thisVar = scopeExports; // set 'this' variable to exports
|
execInfo.thisVar = scopeExports; // set 'this' variable to exports
|
||||||
jsvUnLock(jspEvaluateVar(moduleContents, scope, 0));
|
jsvUnLock(jspEvaluateVar(moduleContents, scope, "module", 0));
|
||||||
#ifndef ESPR_NO_LET_SCOPING
|
#ifndef ESPR_NO_LET_SCOPING
|
||||||
assert(execInfo.blockCount==0);
|
assert(execInfo.blockCount==0);
|
||||||
assert(execInfo.blockScope==0);
|
assert(execInfo.blockScope==0);
|
||||||
|
|||||||
@ -52,19 +52,21 @@ void jspSetInterrupted(bool interrupt);
|
|||||||
/// Has there been an error during parsing
|
/// Has there been an error during parsing
|
||||||
bool jspHasError();
|
bool jspHasError();
|
||||||
/// Set the error flag - set lineReported if we've already output the line number
|
/// Set the error flag - set lineReported if we've already output the line number
|
||||||
void jspSetError(bool lineReported);
|
void jspSetError();
|
||||||
/// We had an exception (argument is the exception's value)
|
/// We had an exception (argument is the exception's value)
|
||||||
void jspSetException(JsVar *value);
|
void jspSetException(JsVar *value);
|
||||||
/** Return the reported exception if there was one (and clear it). May return undefined even if there was an exception - eg `throw undefined` */
|
/** Return the reported exception if there was one (and clear it). May return undefined even if there was an exception - eg `throw undefined` */
|
||||||
JsVar *jspGetException();
|
JsVar *jspGetException();
|
||||||
/** Return a stack trace string if there was one (and clear it) */
|
/** Return a stack trace string if there was one (and clear it) */
|
||||||
JsVar *jspGetStackTrace();
|
JsVar *jspGetStackTrace();
|
||||||
|
/** Append a line marker for the current lex instanec to the given string */
|
||||||
|
void jspAppendStackTrace(JsVar *stackTrace, JsLex *lex);
|
||||||
|
|
||||||
/** Evaluate the given variable as an expression (in current scope) */
|
/** Evaluate the given variable as an expression (in current scope) */
|
||||||
JsVar *jspEvaluateExpressionVar(JsVar *str);
|
JsVar *jspEvaluateExpressionVar(JsVar *str);
|
||||||
/** Execute code form a variable and return the result. If lineNumberOffset
|
/** Execute code form a variable and return the result. If lineNumberOffset
|
||||||
* is nonzero it's added to the line numbers that get reported for errors/debug */
|
* is nonzero it's added to the line numbers that get reported for errors/debug */
|
||||||
JsVar *jspEvaluateVar(JsVar *str, JsVar *scope, uint16_t lineNumberOffset);
|
JsVar *jspEvaluateVar(JsVar *str, JsVar *scope, const char *stackTraceName, uint16_t lineNumberOffset);
|
||||||
/** Execute code form a string and return the result.
|
/** Execute code form a string and return the result.
|
||||||
* You should only set stringIsStatic if the string will hang around for
|
* You should only set stringIsStatic if the string will hang around for
|
||||||
* the life of the interpreter, as then the interpreter will use a pointer
|
* the life of the interpreter, as then the interpreter will use a pointer
|
||||||
@ -97,7 +99,7 @@ typedef enum {
|
|||||||
EXEC_INTERRUPTED = 16, ///< true if execution has been interrupted
|
EXEC_INTERRUPTED = 16, ///< true if execution has been interrupted
|
||||||
EXEC_EXCEPTION = 32, ///< we had an exception, so don't execute until we hit a try/catch block
|
EXEC_EXCEPTION = 32, ///< we had an exception, so don't execute until we hit a try/catch block
|
||||||
EXEC_ERROR = 64,
|
EXEC_ERROR = 64,
|
||||||
EXEC_ERROR_LINE_REPORTED = 128, ///< if an error has been reported, set this so we don't do it too much (EXEC_ERROR will STILL be set)
|
// 128 is free now
|
||||||
|
|
||||||
EXEC_FOR_INIT = 256, ///< when in for initialiser parsing - hack to avoid getting confused about multiple use for IN
|
EXEC_FOR_INIT = 256, ///< when in for initialiser parsing - hack to avoid getting confused about multiple use for IN
|
||||||
EXEC_IN_LOOP = 512, ///< when in a loop, set this - we can then block break/continue outside it
|
EXEC_IN_LOOP = 512, ///< when in a loop, set this - we can then block break/continue outside it
|
||||||
|
|||||||
@ -259,7 +259,7 @@ NO_INLINE void jsExceptionHere(JsExceptionType type, const char *fmt, ...) {
|
|||||||
|
|
||||||
JsVar *var = jsvNewFromEmptyString();
|
JsVar *var = jsvNewFromEmptyString();
|
||||||
if (!var) {
|
if (!var) {
|
||||||
jspSetError(false);
|
jspSetError();
|
||||||
return; // out of memory
|
return; // out of memory
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -337,7 +337,7 @@ NO_INLINE void jsExceptionHere_flash(JsExceptionType type, const char *ffmt, ...
|
|||||||
|
|
||||||
JsVar *var = jsvNewFromEmptyString();
|
JsVar *var = jsvNewFromEmptyString();
|
||||||
if (!var) {
|
if (!var) {
|
||||||
jspSetError(false);
|
jspSetError();
|
||||||
return; // out of memory
|
return; // out of memory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
#include "jswrap_object.h" // for jswrap_object_toString
|
#include "jswrap_object.h" // for jswrap_object_toString
|
||||||
#include "jswrap_arraybuffer.h" // for jsvNewTypedArray
|
#include "jswrap_arraybuffer.h" // for jsvNewTypedArray
|
||||||
#include "jswrap_dataview.h" // for jsvNewDataViewWithData
|
#include "jswrap_dataview.h" // for jsvNewDataViewWithData
|
||||||
|
#include "jswrap_functions.h" // jswrap_console_trace
|
||||||
#if defined(ESPR_JIT) && defined(LINUX)
|
#if defined(ESPR_JIT) && defined(LINUX)
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#endif
|
#endif
|
||||||
@ -645,6 +646,13 @@ JsVar *jsvNewWithFlags(JsVarFlags flags) {
|
|||||||
return jsvNewWithFlags(flags);
|
return jsvNewWithFlags(flags);
|
||||||
#else
|
#else
|
||||||
// On a micro, we're screwed.
|
// On a micro, we're screwed.
|
||||||
|
#ifndef SAVE_ON_FLASH
|
||||||
|
if (!(jsErrorFlags & JSERR_MEMORY)) { // try and print a stack trace (if not already out of memory) - we should be able to do this without allocation
|
||||||
|
jsErrorFlags |= JSERR_MEMORY;
|
||||||
|
jsiConsolePrint("OUT OF MEMORY");
|
||||||
|
jswrap_console_trace(NULL);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
jsErrorFlags |= JSERR_MEMORY;
|
jsErrorFlags |= JSERR_MEMORY;
|
||||||
jspSetInterrupted(true);
|
jspSetInterrupted(true);
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -55,6 +55,15 @@ JsVar *_jswrap_error_constructor(JsVar *msg, char *type) {
|
|||||||
if (msg)
|
if (msg)
|
||||||
jsvObjectSetChildAndUnLock(d, "message", jsvAsString(msg));
|
jsvObjectSetChildAndUnLock(d, "message", jsvAsString(msg));
|
||||||
jsvObjectSetChildAndUnLock(d, "type", jsvNewFromString(type));
|
jsvObjectSetChildAndUnLock(d, "type", jsvNewFromString(type));
|
||||||
|
// add stack trace
|
||||||
|
if (lex) {
|
||||||
|
JsVar *stackTrace = jsvNewFromEmptyString();
|
||||||
|
if (stackTrace) { // memory?
|
||||||
|
jspAppendStackTrace(stackTrace, lex);
|
||||||
|
jsvObjectSetChildAndUnLock(d, "stack", stackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -163,7 +163,7 @@ Evaluate a string containing JavaScript code
|
|||||||
JsVar *jswrap_eval(JsVar *v) {
|
JsVar *jswrap_eval(JsVar *v) {
|
||||||
if (!v) return 0;
|
if (!v) return 0;
|
||||||
JsVar *s = jsvAsString(v); // get as a string
|
JsVar *s = jsvAsString(v); // get as a string
|
||||||
JsVar *result = jspEvaluateVar(s, 0, 0); // don't set scope, so we use the current scope
|
JsVar *result = jspEvaluateVar(s, 0, "eval", 0); // don't set scope, so we use the current scope
|
||||||
jsvUnLock(s);
|
jsvUnLock(s);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -660,4 +660,20 @@ Implemented in Espruino as an alias of `console.log`
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
Implemented in Espruino as an alias of `console.log`
|
Implemented in Espruino as an alias of `console.log`
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*JSON{
|
||||||
|
"type" : "staticmethod",
|
||||||
|
"class" : "console",
|
||||||
|
"name" : "trace",
|
||||||
|
"ifndef" : "SAVE_ON_FLASH",
|
||||||
|
"generate" : "jswrap_console_trace",
|
||||||
|
"params" : [
|
||||||
|
["text","JsVarArray","One or more arguments to print"]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
void jswrap_console_trace(JsVar *v) {
|
||||||
|
if (v) jswrap_print(v);
|
||||||
|
jslPrintStackTrace(vcbprintf_callback_jsiConsolePrintString, NULL, lex);
|
||||||
|
}
|
||||||
@ -32,6 +32,7 @@ JsVar *jswrap_decodeURIComponent(JsVar *arg);
|
|||||||
|
|
||||||
void jswrap_trace(JsVar *root);
|
void jswrap_trace(JsVar *root);
|
||||||
void jswrap_print(JsVar *v);
|
void jswrap_print(JsVar *v);
|
||||||
|
void jswrap_console_trace(JsVar *v);
|
||||||
|
|
||||||
|
|
||||||
#endif // JSWRAP_FUNCTIONS_H_
|
#endif // JSWRAP_FUNCTIONS_H_
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user