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
|
||||
define jsvTrace
|
||||
print jsvTrace($arg0, 0)
|
||||
end
|
||||
end
|
||||
define whereami
|
||||
print jslPrintPosition(jsiConsolePrintString, 0, lex->tokenLastStart)
|
||||
print jslPrintTokenLineMarker(jsiConsolePrintString, 0, lex->tokenLastStart, 0)
|
||||
print jslPrintPosition(jsiConsolePrintString, 0, lex, lex->tokenLastStart)
|
||||
print jslPrintTokenLineMarker(jsiConsolePrintString, 0, lex, lex->tokenLastStart, 0)
|
||||
end
|
||||
define typeof
|
||||
if (($arg0)->flags&JSV_VARTYPEMASK)>=JSV_NAME_STRING_0 && (($arg0)->flags&JSV_VARTYPEMASK)<=JSV_NAME_STRING_MAX
|
||||
@ -25,7 +25,7 @@ end
|
||||
define asm
|
||||
set disassemble-next-line on
|
||||
show disassemble-next-line
|
||||
echo now use stepi
|
||||
echo now use stepi
|
||||
end
|
||||
# Watchdog timer off for NRF52 devices
|
||||
define wdt_off
|
||||
@ -56,9 +56,6 @@ define execflags
|
||||
if execInfo.execute&EXEC_ERROR
|
||||
printf "EXEC_ERROR\n"
|
||||
end
|
||||
if execInfo.execute&EXEC_ERROR_LINE_REPORTED
|
||||
printf "EXEC_ERROR_LINE_REPORTED\n"
|
||||
end
|
||||
if execInfo.execute&EXEC_FOR_INIT
|
||||
printf "EXEC_FOR_INIT\n"
|
||||
end
|
||||
@ -82,7 +79,7 @@ end
|
||||
|
||||
define hook-run
|
||||
set $primask=0
|
||||
end
|
||||
end
|
||||
|
||||
define hook-continue
|
||||
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
|
||||
nRF52840: Add E.getVDDH() method to get VDDH voltage
|
||||
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)
|
||||
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
|
||||
if (jsiStatus & JSIS_FIRST_BOOT) {
|
||||
JsVar *code = jsfReadFile(jsfNameFromString(".bootPowerOn"),0,0);
|
||||
if (code)
|
||||
jsvUnLock2(jspEvaluateVar(code,0,0), code);
|
||||
if (code) {
|
||||
jsvUnLock2(jspEvaluateVar(code,0,".bootPowerOn",0), code);
|
||||
jsiCheckErrors();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// 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++) {
|
||||
filename[5] = (char)('0'+i);
|
||||
JsVar *code = jsfReadFile(jsfNameFromString(filename),0,0);
|
||||
if (code)
|
||||
jsvUnLock2(jspEvaluateVar(code,0,0), code);
|
||||
if (code) {
|
||||
jsvUnLock2(jspEvaluateVar(code,0,filename,0), code);
|
||||
jsiCheckErrors();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Load normal boot code
|
||||
JsVar *code = jsfGetBootCodeFromFlash(isReset);
|
||||
if (!code) return false;
|
||||
jsvUnLock2(jspEvaluateVar(code,0,0), code);
|
||||
jsvUnLock2(jspEvaluateVar(code,0,"boot code",0), code);
|
||||
jsiCheckErrors();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -137,7 +137,6 @@ JsErrorFlags lastJsErrorFlags = 0; ///< Compare with jsErrorFlags in order to re
|
||||
#ifdef USE_DEBUGGER
|
||||
void jsiDebuggerLine(JsVar *line);
|
||||
#endif
|
||||
void jsiCheckErrors();
|
||||
|
||||
static void jsiPacketFileEnd();
|
||||
static void jsiPacketExit();
|
||||
@ -535,11 +534,13 @@ void jsiSoftInit(bool hasBeenReset) {
|
||||
|
||||
// Run 'boot code' - textual JS in flash
|
||||
jsfLoadBootCodeFromFlash(hasBeenReset);
|
||||
// jsiCheckErrors is performed internally
|
||||
|
||||
// Now run initialisation code
|
||||
JsVar *initCode = jsvObjectGetChildIfExists(execInfo.hiddenRoot, JSI_INIT_CODE_NAME);
|
||||
if (initCode) {
|
||||
jsvUnLock2(jspEvaluateVar(initCode, 0, 0), initCode);
|
||||
jsvUnLock2(jspEvaluateVar(initCode, 0, "initcode", 0), initCode);
|
||||
jsiCheckErrors();
|
||||
jsvObjectRemoveChild(execInfo.hiddenRoot, JSI_INIT_CODE_NAME);
|
||||
}
|
||||
|
||||
@ -565,11 +566,13 @@ void jsiSoftInit(bool hasBeenReset) {
|
||||
|
||||
// Execute `init` events on `E`
|
||||
jsiExecuteEventCallbackOn("E", INIT_CALLBACK_NAME, 0, 0);
|
||||
jsiCheckErrors();
|
||||
// Execute the `onInit` function
|
||||
JsVar *onInit = jsvObjectGetChildIfExists(execInfo.root, JSI_ONINIT_NAME);
|
||||
if (onInit) {
|
||||
if (jsiEcho()) jsiConsolePrint("Running onInit()...\n");
|
||||
jsiExecuteEventCallback(0, onInit, 0, 0);
|
||||
jsiCheckErrors();
|
||||
jsvUnLock(onInit);
|
||||
}
|
||||
}
|
||||
@ -1528,7 +1531,7 @@ void jsiHandleNewLine(bool execute) {
|
||||
#endif
|
||||
{
|
||||
// execute!
|
||||
JsVar *v = jspEvaluateVar(lineToExecute, 0, jsiLineNumberOffset);
|
||||
JsVar *v = jspEvaluateVar(lineToExecute, 0, "REPL", jsiLineNumberOffset);
|
||||
// add input line to history
|
||||
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)
|
||||
@ -2040,7 +2043,7 @@ static NO_INLINE bool jsiExecuteEventCallbackInner(JsVar *thisVar, JsVar *callba
|
||||
} else if (jsvIsFunction(callbackNoNames)) {
|
||||
jsvUnLock(jspExecuteFunction(callbackNoNames, thisVar, (int)argCount, argPtr));
|
||||
} else if (jsvIsString(callbackNoNames)) {
|
||||
jsvUnLock(jspEvaluateVar(callbackNoNames, 0, 0));
|
||||
jsvUnLock(jspEvaluateVar(callbackNoNames, 0, "event", 0));
|
||||
} else
|
||||
jsError("Unknown type of callback in Event Queue");
|
||||
return ok;
|
||||
@ -2146,7 +2149,7 @@ void jsiCtrlC() {
|
||||
/** 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
|
||||
* 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;
|
||||
JsVar *stringData = length ? jsvNewStringOfLength(length, (char*)data) : NULL;
|
||||
if (stringData) {
|
||||
@ -2178,7 +2181,7 @@ void jsiIdle() {
|
||||
bool wasBusy = false;
|
||||
IOEventFlags eventFlags;
|
||||
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.
|
||||
// Just process what was in the event queue at the start
|
||||
int maxEvents = jshGetEventsUsed();
|
||||
@ -2564,8 +2567,8 @@ void jsiIdle() {
|
||||
jsiSemiInit(false, &filename); // don't autoload code
|
||||
// load the code we specified
|
||||
JsVar *code = jsfReadFile(filename,0,0);
|
||||
if (code)
|
||||
jsvUnLock2(jspEvaluateVar(code,0,0), code);
|
||||
if (code) // only supply the filename if we're sure it's zero terminated
|
||||
jsvUnLock2(jspEvaluateVar(code,0,filename.c[sizeof(filename.c)-1] ? filename.c : "load",0), code);
|
||||
} else {
|
||||
jsiSoftKill();
|
||||
jspSoftKill();
|
||||
@ -2805,14 +2808,14 @@ void jsiDebuggerLoop() {
|
||||
itostr((JsVarInt)jslGetLineNumber() + (JsVarInt)lex->lineNumberOffset - 1, lineStr, 10);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
{ // FIXME: Maybe if executing from a Storage file we use line numbers within that file?
|
||||
lineStr[0]=0;
|
||||
}
|
||||
size_t lineLen = strlen(lineStr);
|
||||
while (lineLen < sizeof(lineStr)-1) lineStr[lineLen++]=' ';
|
||||
lineStr[lineLen] = 0;
|
||||
// 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) &&
|
||||
@ -2822,7 +2825,7 @@ void jsiDebuggerLoop() {
|
||||
jshIdle();
|
||||
// If we have too many events (> half full) drain the queue
|
||||
uint8_t eventData[IOEVENT_MAX_LEN];
|
||||
int eventLen;
|
||||
unsigned int eventLen;
|
||||
while (jshGetEventsUsed()>IOBUFFERMASK*1/2 &&
|
||||
!(jsiStatus & JSIS_EXIT_DEBUGGER) &&
|
||||
!(execInfo.execute & EXEC_CTRL_C_MASK)) {
|
||||
@ -2906,7 +2909,8 @@ void jsiDebuggerLine(JsVar *line) {
|
||||
"finish / f - finish execution of the function call\n"
|
||||
"print ... / p ... - evaluate and print the next argument\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")) {
|
||||
jsiStatus |= JSIS_EXIT_DEBUGGER;
|
||||
execInfo.execute |= EXEC_INTERRUPTED;
|
||||
@ -2962,6 +2966,8 @@ void jsiDebuggerLine(JsVar *line) {
|
||||
} else {
|
||||
jsiConsolePrint("Unknown command\n");
|
||||
}
|
||||
} else if (!strcmp(id,"bt")) {
|
||||
jslPrintStackTrace(vcbprintf_callback_jsiConsolePrintString, NULL, oldLex);
|
||||
} else
|
||||
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
|
||||
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
|
||||
JsVar *jsiSetTimeout(void (*functionPtr)(void), JsVarFloat milliseconds);
|
||||
/// 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
|
||||
lex->lineNumberOffset = 0;
|
||||
#endif
|
||||
lex->functionName = NULL;
|
||||
lex->lastLex = NULL;
|
||||
// set up iterator
|
||||
jsvStringIteratorNew(&lex->it, lex->sourceVar, 0);
|
||||
jsvUnLock(lex->it.var); // see jslGetNextCh
|
||||
@ -1502,7 +1504,7 @@ void jslPrintTokenisedString(JsVar *code, vcbprintf_callback user_callback, void
|
||||
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;
|
||||
#if !defined(SAVE_ON_FLASH) && !defined(ESPR_EMBED)
|
||||
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));
|
||||
jsvGetLineAndCol(fileStr, tokenPos + stringAddr - fileAddr, &line, &col);
|
||||
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);
|
||||
return;
|
||||
}
|
||||
@ -1524,10 +1526,10 @@ void jslPrintPosition(vcbprintf_callback user_callback, void *user_data, size_t
|
||||
if (lex->lineNumberOffset)
|
||||
line += (size_t)lex->lineNumberOffset - 1;
|
||||
#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;
|
||||
jsvGetLineAndCol(lex->sourceVar, tokenPos, &line, &col);
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
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;
|
||||
|
||||
// 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);
|
||||
|
||||
/// 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).
|
||||
* 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_ */
|
||||
|
||||
@ -74,10 +74,8 @@ void jspSetInterrupted(bool interrupt) {
|
||||
}
|
||||
|
||||
/// 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;
|
||||
if (lineReported)
|
||||
execInfo.execute |= EXEC_ERROR_LINE_REPORTED;
|
||||
}
|
||||
|
||||
bool jspHasError() {
|
||||
@ -100,8 +98,6 @@ void jspeiRemoveScope() {
|
||||
if (!execInfo.scopesVar || !jsvGetArrayLength(execInfo.scopesVar)) {
|
||||
// This should never happen unless there's an interpreter error - no need to have an error message
|
||||
assert(0);
|
||||
//jsExceptionHere(JSET_INTERNALERROR, "Too many scopes removed");
|
||||
//jspSetError(false);
|
||||
return;
|
||||
}
|
||||
jsvUnLock(jsvArrayPop(execInfo.scopesVar));
|
||||
@ -237,12 +233,11 @@ void jspSetNoExecute() {
|
||||
execInfo.execute = (execInfo.execute & (JsExecFlags)(int)~EXEC_RUN_MASK) | EXEC_NO;
|
||||
}
|
||||
|
||||
void jspAppendStackTrace(JsVar *stackTrace) {
|
||||
void jspAppendStackTrace(JsVar *stackTrace, JsLex *lex) {
|
||||
JsvStringIterator it;
|
||||
jsvStringIteratorNew(&it, stackTrace, 0);
|
||||
jsvStringIteratorGotoEnd(&it);
|
||||
jslPrintPosition((vcbprintf_callback)jsvStringIteratorPrintfCallback, &it, lex->tokenLastStart);
|
||||
jslPrintTokenLineMarker((vcbprintf_callback)jsvStringIteratorPrintfCallback, &it, lex->tokenLastStart, 0);
|
||||
jslPrintStackTrace(jsvStringIteratorPrintfCallback, &it, lex);
|
||||
jsvStringIteratorFree(&it);
|
||||
}
|
||||
|
||||
@ -256,18 +251,6 @@ void jspSetException(JsVar *value) {
|
||||
}
|
||||
// Set the exception flag
|
||||
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` */
|
||||
@ -276,13 +259,6 @@ JsVar *jspGetException() {
|
||||
if (exceptionName) {
|
||||
JsVar *exception = jsvSkipName(exceptionName);
|
||||
jsvRemoveChildAndUnLock(execInfo.hiddenRoot, exceptionName);
|
||||
|
||||
JsVar *stack = jspGetStackTrace();
|
||||
if (stack && jsvHasChildren(exception)) {
|
||||
jsvObjectSetChild(exception, "stack", stack);
|
||||
}
|
||||
jsvUnLock(stack);
|
||||
|
||||
return exception;
|
||||
}
|
||||
return 0;
|
||||
@ -311,7 +287,7 @@ NO_INLINE bool jspeFunctionArguments(JsVar *funcVar) {
|
||||
strcpy(&buf[1], jslGetTokenValueAsString());
|
||||
JsVar *param = jsvAddNamedChild(funcVar, 0, buf);
|
||||
if (!param) { // out of memory
|
||||
jspSetError(false);
|
||||
jspSetError();
|
||||
return false;
|
||||
}
|
||||
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?
|
||||
JsVar *functionRoot = jsvNewWithFlags(JSV_FUNCTION);
|
||||
if (!functionRoot) { // out of memory
|
||||
jspSetError(false);
|
||||
jspSetError();
|
||||
jsvUnLock(thisVar);
|
||||
return 0;
|
||||
}
|
||||
@ -834,11 +810,11 @@ NO_INLINE JsVar *jspeFunctionCall(JsVar *function, JsVar *functionName, JsVar *t
|
||||
execInfo.execute &= (JsExecFlags)~EXEC_DEBUGGER_NEXT_LINE;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
JsLex newLex;
|
||||
JsLex *oldLex = jslSetLex(&newLex);
|
||||
jslInit(functionCode);
|
||||
newLex.functionName = functionName;
|
||||
newLex.lastLex = oldLex;
|
||||
jsvUnLock(functionCode); // unlock function code here to reduce amount of locks needed during recursion
|
||||
functionCode = 0;
|
||||
#ifndef ESPR_NO_LINE_NUMBERS
|
||||
@ -889,9 +865,9 @@ NO_INLINE JsVar *jspeFunctionCall(JsVar *function, JsVar *functionName, JsVar *t
|
||||
#ifdef USE_DEBUGGER
|
||||
bool calledDebugger = false;
|
||||
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);
|
||||
jsiConsolePrintChar('\n');
|
||||
jsiConsolePrintString("\n"); // prints \r too
|
||||
if (execInfo.execute & EXEC_DEBUGGER_FINISH_FUNCTION) {
|
||||
calledDebugger = true;
|
||||
jsiDebuggerLoop();
|
||||
@ -904,19 +880,8 @@ NO_INLINE JsVar *jspeFunctionCall(JsVar *function, JsVar *functionName, JsVar *t
|
||||
jslKill();
|
||||
jslSetLex(oldLex);
|
||||
|
||||
if (hasError) {
|
||||
if (hasError)
|
||||
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 */
|
||||
@ -1236,7 +1201,7 @@ NO_INLINE JsVar *jspeFactorFunctionCall() {
|
||||
|
||||
if (lex->tk==LEX_R_NEW) {
|
||||
jsExceptionHere(JSET_ERROR, "Nesting 'new' operators is unsupported");
|
||||
jspSetError(false);
|
||||
jspSetError();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -1373,7 +1338,7 @@ NO_INLINE JsVar *jspeFactorObject() {
|
||||
if (JSP_SHOULD_EXECUTE) {
|
||||
JsVar *contents = jsvNewObject();
|
||||
if (!contents) { // out of memory
|
||||
jspSetError(false);
|
||||
jspSetError();
|
||||
return 0;
|
||||
}
|
||||
/* JSON-style object definition */
|
||||
@ -1466,7 +1431,7 @@ NO_INLINE JsVar *jspeFactorArray() {
|
||||
if (JSP_SHOULD_EXECUTE) {
|
||||
contents = jsvNewEmptyArray();
|
||||
if (!contents) { // out of memory
|
||||
jspSetError(false);
|
||||
jspSetError();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -2325,17 +2290,6 @@ NO_INLINE void jspeBlockNoBrackets() {
|
||||
JsVar *a = jspeStatement();
|
||||
jsvCheckReferenceError(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)
|
||||
break;
|
||||
if (!JSP_SHOULD_EXECUTE) {
|
||||
@ -2415,7 +2369,7 @@ NO_INLINE JsVar *jspeStatementVar() {
|
||||
jsvUnLock(scope);
|
||||
#endif
|
||||
if (!a) { // out of memory
|
||||
jspSetError(false);
|
||||
jspSetError();
|
||||
return lastDefined;
|
||||
}
|
||||
}
|
||||
@ -2909,7 +2863,7 @@ NO_INLINE JsVar *jspeStatementTry() {
|
||||
}
|
||||
if (shouldExecuteBefore) {
|
||||
// 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);
|
||||
}
|
||||
|
||||
@ -3287,17 +3241,18 @@ JsVar *jspEvaluateExpressionVar(JsVar *str) {
|
||||
|
||||
/** 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 */
|
||||
JsVar *jspEvaluateVar(JsVar *str, JsVar *scope, uint16_t lineNumberOffset) {
|
||||
JsVar *jspEvaluateVar(JsVar *str, JsVar *scope, const char *stackTraceName, uint16_t lineNumberOffset) {
|
||||
JsLex lex;
|
||||
|
||||
assert(jsvIsString(str));
|
||||
JsLex *oldLex = jslSetLex(&lex);
|
||||
jslInit(str);
|
||||
lex.lastLex = oldLex;
|
||||
lex.functionName = stackTraceName?jsvNewFromString(stackTraceName):0;
|
||||
#ifndef ESPR_NO_LINE_NUMBERS
|
||||
lex.lineNumberOffset = lineNumberOffset;
|
||||
#endif
|
||||
|
||||
|
||||
JsExecInfo oldExecInfo = execInfo;
|
||||
execInfo.execute = EXEC_YES;
|
||||
if (scope) {
|
||||
@ -3316,6 +3271,7 @@ JsVar *jspEvaluateVar(JsVar *str, JsVar *scope, uint16_t lineNumberOffset) {
|
||||
// clean up
|
||||
if (scope) jspeiClearScopes();
|
||||
jslKill();
|
||||
jsvUnLock(lex.functionName);
|
||||
jslSetLex(oldLex);
|
||||
|
||||
// restore state and execInfo (keep error flags & ctrl-c)
|
||||
@ -3342,7 +3298,7 @@ JsVar *jspEvaluate(const char *str, bool stringIsStatic) {
|
||||
|
||||
JsVar *v = 0;
|
||||
if (!jsvIsMemoryFull())
|
||||
v = jspEvaluateVar(evCode, 0, 0);
|
||||
v = jspEvaluateVar(evCode, 0, "[raw]", 0);
|
||||
jsvUnLock(evCode);
|
||||
|
||||
return v;
|
||||
@ -3423,7 +3379,7 @@ JsVar *jspEvaluateModule(JsVar *moduleContents) {
|
||||
execInfo.blockCount = 0;
|
||||
#endif
|
||||
execInfo.thisVar = scopeExports; // set 'this' variable to exports
|
||||
jsvUnLock(jspEvaluateVar(moduleContents, scope, 0));
|
||||
jsvUnLock(jspEvaluateVar(moduleContents, scope, "module", 0));
|
||||
#ifndef ESPR_NO_LET_SCOPING
|
||||
assert(execInfo.blockCount==0);
|
||||
assert(execInfo.blockScope==0);
|
||||
|
||||
@ -52,19 +52,21 @@ void jspSetInterrupted(bool interrupt);
|
||||
/// Has there been an error during parsing
|
||||
bool jspHasError();
|
||||
/// 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)
|
||||
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` */
|
||||
JsVar *jspGetException();
|
||||
/** Return a stack trace string if there was one (and clear it) */
|
||||
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) */
|
||||
JsVar *jspEvaluateExpressionVar(JsVar *str);
|
||||
/** 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 */
|
||||
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.
|
||||
* 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
|
||||
@ -97,7 +99,7 @@ typedef enum {
|
||||
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_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_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();
|
||||
if (!var) {
|
||||
jspSetError(false);
|
||||
jspSetError();
|
||||
return; // out of memory
|
||||
}
|
||||
|
||||
@ -337,7 +337,7 @@ NO_INLINE void jsExceptionHere_flash(JsExceptionType type, const char *ffmt, ...
|
||||
|
||||
JsVar *var = jsvNewFromEmptyString();
|
||||
if (!var) {
|
||||
jspSetError(false);
|
||||
jspSetError();
|
||||
return; // out of memory
|
||||
}
|
||||
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
#include "jswrap_object.h" // for jswrap_object_toString
|
||||
#include "jswrap_arraybuffer.h" // for jsvNewTypedArray
|
||||
#include "jswrap_dataview.h" // for jsvNewDataViewWithData
|
||||
#include "jswrap_functions.h" // jswrap_console_trace
|
||||
#if defined(ESPR_JIT) && defined(LINUX)
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
@ -645,6 +646,13 @@ JsVar *jsvNewWithFlags(JsVarFlags flags) {
|
||||
return jsvNewWithFlags(flags);
|
||||
#else
|
||||
// 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;
|
||||
jspSetInterrupted(true);
|
||||
return 0;
|
||||
|
||||
@ -55,6 +55,15 @@ JsVar *_jswrap_error_constructor(JsVar *msg, char *type) {
|
||||
if (msg)
|
||||
jsvObjectSetChildAndUnLock(d, "message", jsvAsString(msg));
|
||||
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;
|
||||
}
|
||||
|
||||
@ -163,7 +163,7 @@ Evaluate a string containing JavaScript code
|
||||
JsVar *jswrap_eval(JsVar *v) {
|
||||
if (!v) return 0;
|
||||
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);
|
||||
return result;
|
||||
}
|
||||
@ -660,4 +660,20 @@ 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_print(JsVar *v);
|
||||
void jswrap_console_trace(JsVar *v);
|
||||
|
||||
|
||||
#endif // JSWRAP_FUNCTIONS_H_
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user