diff --git a/src/api/EscargotPublic.cpp b/src/api/EscargotPublic.cpp index 15d955c95..82d309fd5 100644 --- a/src/api/EscargotPublic.cpp +++ b/src/api/EscargotPublic.cpp @@ -1986,7 +1986,7 @@ class DebuggerC : public Debugger { public: virtual void init(const char* options, Context* context) override {} virtual void parseCompleted(String* source, String* srcName, size_t originLineOffset, String* error = nullptr) override; - virtual void stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state) override; + virtual bool stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state) override; virtual void byteCodeReleaseNotification(ByteCodeBlock* byteCodeBlock) override; virtual void exceptionCaught(String* message, SavedStackTraceDataVector& exceptionTrace) override; virtual void consoleOut(String* output) override; @@ -2057,7 +2057,7 @@ static LexicalEnvironment* getFunctionLexEnv(ExecutionState* state) return nullptr; } -void DebuggerC::stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state) +bool DebuggerC::stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state) { DebuggerOperationsRef::BreakpointOperations operations(reinterpret_cast(byteCodeBlock), toRef(state), offset); @@ -2092,6 +2092,7 @@ void DebuggerC::stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, break; } } + return false; } void DebuggerC::byteCodeReleaseNotification(ByteCodeBlock* byteCodeBlock) @@ -3442,6 +3443,24 @@ bool ContextRef::isWaitBeforeExit() #endif /* ESCARGOT_DEBUGGER */ } +bool ContextRef::isDebuggerRestartTrue() +{ +#ifdef ESCARGOT_DEBUGGER + return isDebuggerRunning() && toImpl(this)->debugger()->getRestart(); +#else /* !ESCARGOT_DEBUGGER */ + return false; +#endif /* ESCARGOT_DEBUGGER */ +} + +void ContextRef::setDebuggerRestart() +{ +#ifdef ESCARGOT_DEBUGGER + if (isDebuggerRunning()) { + toImpl(this)->debugger()->setRestart(false); + } +#endif /* ESCARGOT_DEBUGGER */ +} + void ContextRef::printDebugger(StringRef* output) { #ifdef ESCARGOT_DEBUGGER diff --git a/src/api/EscargotPublic.h b/src/api/EscargotPublic.h index 16d6e280a..688b21e6e 100644 --- a/src/api/EscargotPublic.h +++ b/src/api/EscargotPublic.h @@ -957,9 +957,11 @@ class ESCARGOT_EXPORT ContextRef { bool initDebugger(const char* options); bool isDebuggerRunning(); bool isWaitBeforeExit(); + bool isDebuggerRestartTrue(); void printDebugger(StringRef* output); void pumpDebuggerEvents(); void setAsAlwaysStopState(); + void setDebuggerRestart(); StringRef* getClientSource(StringRef** sourceName); typedef OptionalRef (*VirtualIdentifierCallback)(ExecutionStateRef* state, ValueRef* name); diff --git a/src/debugger/Debugger.h b/src/debugger/Debugger.h index 025e1e251..c39d0217b 100644 --- a/src/debugger/Debugger.h +++ b/src/debugger/Debugger.h @@ -142,7 +142,7 @@ class Debugger : public gc { return m_activeSavedStackTrace; } - inline void processDisabledBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state) + inline bool processDisabledBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state) { if (m_stopState != ESCARGOT_DEBUGGER_ALWAYS_STOP && m_stopState != state) { m_delay--; @@ -154,6 +154,8 @@ class Debugger : public gc { if (m_stopState == ESCARGOT_DEBUGGER_ALWAYS_STOP || m_stopState == state) { stopAtBreakpoint(byteCodeBlock, offset, state); } + + return m_restartDebugging; } static inline void updateStopState(Debugger* debugger, ExecutionState* state, ExecutionState* newState) @@ -189,7 +191,7 @@ class Debugger : public gc { virtual void init(const char* options, Context* context) = 0; virtual void parseCompleted(String* source, String* srcName, size_t originLineOffset, String* error = nullptr) = 0; - virtual void stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state) = 0; + virtual bool stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state) = 0; virtual void byteCodeReleaseNotification(ByteCodeBlock* byteCodeBlock) = 0; virtual void exceptionCaught(String* message, SavedStackTraceDataVector& exceptionTrace) = 0; virtual void consoleOut(String* output) = 0; @@ -210,6 +212,16 @@ class Debugger : public gc { Vector> m_activeObjects; + bool getRestart() + { + return m_restartDebugging; + } + + void setRestart(bool b) + { + m_restartDebugging = b; + } + protected: Debugger() : m_delay(ESCARGOT_DEBUGGER_MESSAGE_PROCESS_DELAY) @@ -234,6 +246,7 @@ class Debugger : public gc { ExecutionState* m_stopState; std::vector m_breakpointLocationsVector; Vector> m_releasedFunctions; + bool m_restartDebugging; private: Context* m_context; diff --git a/src/debugger/DebuggerDevtools.cpp b/src/debugger/DebuggerDevtools.cpp index 700de1b30..032db03b7 100644 --- a/src/debugger/DebuggerDevtools.cpp +++ b/src/debugger/DebuggerDevtools.cpp @@ -258,14 +258,14 @@ void DebuggerDevtools::sendPausedEvent(ByteCodeBlock* byteCodeBlock, const uint3 sendMessage(msg, msg.length()); } -void DebuggerDevtools::stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state) +bool DebuggerDevtools::stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state) { if (m_stopState == ESCARGOT_DEBUGGER_IN_EVAL_MODE) { m_delay--; if (m_delay == 0) { processEvents(state, byteCodeBlock); } - return; + return false; } sendPausedEvent(byteCodeBlock, offset, state, !m_startBreakpoint); @@ -275,7 +275,7 @@ void DebuggerDevtools::stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t o } if (!enabled()) { - return; + return false; } ASSERT(m_activeObjects.empty()); @@ -286,6 +286,8 @@ void DebuggerDevtools::stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t o m_activeObjects.clear(); m_delay = ESCARGOT_DEBUGGER_MESSAGE_PROCESS_DELAY; + + return false; } void DebuggerDevtools::byteCodeReleaseNotification(ByteCodeBlock* byteCodeBlock) diff --git a/src/debugger/DebuggerDevtools.h b/src/debugger/DebuggerDevtools.h index a4ae6e41b..26ed9f3c2 100644 --- a/src/debugger/DebuggerDevtools.h +++ b/src/debugger/DebuggerDevtools.h @@ -55,7 +55,7 @@ class DebuggerDevtools : public DebuggerTcp { bool skipSourceCode(String* srcName) const override; void parseCompleted(String* source, String* srcName, size_t originLineOffset, String* error = nullptr) override; - void stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state) override; + bool stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state) override; void byteCodeReleaseNotification(ByteCodeBlock* byteCodeBlock) override; void exceptionCaught(String* message, SavedStackTraceDataVector& exceptionTrace) override; void consoleOut(String* output) override; diff --git a/src/debugger/DebuggerEscargot.cpp b/src/debugger/DebuggerEscargot.cpp index f76fe4f1c..36cf78da7 100644 --- a/src/debugger/DebuggerEscargot.cpp +++ b/src/debugger/DebuggerEscargot.cpp @@ -219,14 +219,14 @@ void DebuggerEscargot::sendVariableObjectInfo(uint8_t subType, Object* object) send(ESCARGOT_MESSAGE_VARIABLE, &variableObjectInfo, sizeof(VariableObjectInfo)); } -void DebuggerEscargot::stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state) +bool DebuggerEscargot::stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state) { if (m_stopState == ESCARGOT_DEBUGGER_IN_EVAL_MODE) { m_delay--; if (m_delay == 0) { processEvents(state, byteCodeBlock); } - return; + return m_restartDebugging; } BreakpointOffset breakpointOffset; @@ -238,7 +238,7 @@ void DebuggerEscargot::stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t o send(ESCARGOT_MESSAGE_BREAKPOINT_HIT, &breakpointOffset, sizeof(BreakpointOffset)); if (!enabled()) { - return; + return m_restartDebugging; } ASSERT(m_activeObjects.size() == 0); @@ -249,6 +249,8 @@ void DebuggerEscargot::stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t o m_activeObjects.clear(); m_delay = ESCARGOT_DEBUGGER_MESSAGE_PROCESS_DELAY; + + return m_restartDebugging; } void DebuggerEscargot::byteCodeReleaseNotification(ByteCodeBlock* byteCodeBlock) @@ -899,6 +901,10 @@ bool DebuggerEscargot::processEvents(ExecutionState* state, Optionalm_resultIndex] = callee.asPointerValue()->call(*state, Value(), code->m_argumentCount, ®isterFile[code->m_argumentsStartIndex]); +#ifdef ESCARGOT_DEBUGGER + if (state->context()->debuggerEnabled()) { + if (state->context()->debugger()->getRestart()) { + return Value(true); + } + } +#endif /* ESCARGOT_DEBUGGER */ + ADD_PROGRAM_COUNTER(Call); NEXT_INSTRUCTION(); } @@ -1715,7 +1723,10 @@ Value Interpreter::interpret(ExecutionState* state, ByteCodeBlock* byteCodeBlock : { if (state->context()->debuggerEnabled()) { - state->context()->debugger()->processDisabledBreakpoint(byteCodeBlock, (uint32_t)(programCounter - (size_t)byteCodeBlock->m_code.data()), state); + bool restart = state->context()->debugger()->processDisabledBreakpoint(byteCodeBlock, (uint32_t)(programCounter - (size_t)byteCodeBlock->m_code.data()), state); + if (restart) { + return Value(true); + } } ADD_PROGRAM_COUNTER(BreakpointDisabled); @@ -1726,7 +1737,10 @@ Value Interpreter::interpret(ExecutionState* state, ByteCodeBlock* byteCodeBlock : { if (state->context()->debuggerEnabled()) { - state->context()->debugger()->stopAtBreakpoint(byteCodeBlock, (uint32_t)(programCounter - (size_t)byteCodeBlock->m_code.data()), state); + bool restart = state->context()->debugger()->stopAtBreakpoint(byteCodeBlock, (uint32_t)(programCounter - (size_t)byteCodeBlock->m_code.data()), state); + if (restart) { + return Value(true); + } } ADD_PROGRAM_COUNTER(BreakpointEnabled); diff --git a/src/shell/Shell.cpp b/src/shell/Shell.cpp index 5cab4b239..3edba6686 100644 --- a/src/shell/Shell.cpp +++ b/src/shell/Shell.cpp @@ -898,48 +898,61 @@ static bool evalScript(ContextRef* context, StringRef* source, StringRef* srcNam isModule = isModule || true; } - auto scriptInitializeResult = context->scriptParser()->initializeScript(source, srcName, isModule); - if (!scriptInitializeResult.script) { - fprintf(stderr, "Script parsing error: "); - switch (scriptInitializeResult.parseErrorCode) { - case Escargot::ErrorObjectRef::Code::SyntaxError: - fprintf(stderr, "SyntaxError"); - break; - case Escargot::ErrorObjectRef::Code::EvalError: - fprintf(stderr, "EvalError"); - break; - case Escargot::ErrorObjectRef::Code::RangeError: - fprintf(stderr, "RangeError"); - break; - case Escargot::ErrorObjectRef::Code::ReferenceError: - fprintf(stderr, "ReferenceError"); - break; - case Escargot::ErrorObjectRef::Code::TypeError: - fprintf(stderr, "TypeError"); - break; - case Escargot::ErrorObjectRef::Code::URIError: - fprintf(stderr, "URIError"); - break; - default: - break; + + bool shouldRestart = false; + do { + if (shouldRestart) { + source = Evaluator::execute(context, [](ExecutionStateRef* state, StringRef* str) -> ValueRef* { return builtinHelperFileRead(state, str->toStdUTF8String().c_str(), "read").get(); }, srcName).result->asString(); + } + shouldRestart = false; + + auto scriptInitializeResult = context->scriptParser()->initializeScript(source, srcName, isModule); + if (!scriptInitializeResult.script) { + fprintf(stderr, "Script parsing error: "); + switch (scriptInitializeResult.parseErrorCode) { + case Escargot::ErrorObjectRef::Code::SyntaxError: + fprintf(stderr, "SyntaxError"); + break; + case Escargot::ErrorObjectRef::Code::EvalError: + fprintf(stderr, "EvalError"); + break; + case Escargot::ErrorObjectRef::Code::RangeError: + fprintf(stderr, "RangeError"); + break; + case Escargot::ErrorObjectRef::Code::ReferenceError: + fprintf(stderr, "ReferenceError"); + break; + case Escargot::ErrorObjectRef::Code::TypeError: + fprintf(stderr, "TypeError"); + break; + case Escargot::ErrorObjectRef::Code::URIError: + fprintf(stderr, "URIError"); + break; + default: + break; + } + fprintf(stderr, ": %s\n", scriptInitializeResult.parseErrorMessage->toStdUTF8String().data()); + return false; } - fprintf(stderr, ": %s\n", scriptInitializeResult.parseErrorMessage->toStdUTF8String().data()); - return false; - } - auto evalResult = Evaluator::execute(context, [](ExecutionStateRef* state, ScriptRef* script) -> ValueRef* { return script->execute(state); }, scriptInitializeResult.script.get()); + auto evalResult = Evaluator::execute(context, [](ExecutionStateRef* state, ScriptRef* script) -> ValueRef* { return script->execute(state); }, scriptInitializeResult.script.get()); + if (!evalResult.isSuccessful()) { + fprintf(stderr, "Uncaught %s:\n", evalResult.resultOrErrorToString(context)->toStdUTF8String().data()); + for (size_t i = 0; i < evalResult.stackTrace.size(); i++) { + fprintf(stderr, "%s (%d:%d)\n", evalResult.stackTrace[i].srcName->toStdUTF8String().data(), (int)evalResult.stackTrace[i].loc.line, (int)evalResult.stackTrace[i].loc.column); + } + return false; + } - if (!evalResult.isSuccessful()) { - fprintf(stderr, "Uncaught %s:\n", evalResult.resultOrErrorToString(context)->toStdUTF8String().data()); - for (size_t i = 0; i < evalResult.stackTrace.size(); i++) { - fprintf(stderr, "%s (%d:%d)\n", evalResult.stackTrace[i].srcName->toStdUTF8String().data(), (int)evalResult.stackTrace[i].loc.line, (int)evalResult.stackTrace[i].loc.column); + if (context->isDebuggerRestartTrue()) { + shouldRestart = true; + context->setDebuggerRestart(); } - return false; - } - if (shouldPrintScriptResult) { - puts(evalResult.resultOrErrorToString(context)->toStdUTF8String().data()); - } + if (shouldPrintScriptResult) { + puts(evalResult.resultOrErrorToString(context)->toStdUTF8String().data()); + } + } while (shouldRestart); bool result = true; while (context->vmInstance()->hasPendingJob() || context->vmInstance()->hasPendingJobFromAnotherThread()) { diff --git a/tools/debugger/debugger.py b/tools/debugger/debugger.py index b51111d05..f1d5b6c8f 100755 --- a/tools/debugger/debugger.py +++ b/tools/debugger/debugger.py @@ -228,6 +228,12 @@ def do_dump(self, args): else: pprint(self.debugger.function_list) + def do_restart(self, args): + """ Restart current debugging from the start of the file """ + self.debugger.send_restart() + self.debugger.step() + self.stop = True + do_re = do_restart def _scroll_direction(debugger, direction): """ Helper function for do_scroll """ diff --git a/tools/debugger/debugger_core.py b/tools/debugger/debugger_core.py index 337d041c5..8ce537fcf 100755 --- a/tools/debugger/debugger_core.py +++ b/tools/debugger/debugger_core.py @@ -85,32 +85,33 @@ ESCARGOT_MESSAGE_STEP = 3 ESCARGOT_MESSAGE_NEXT = 4 ESCARGOT_MESSAGE_FINISH = 5 -ESCARGOT_MESSAGE_EVAL_8BIT_START = 6 -ESCARGOT_MESSAGE_EVAL_8BIT = 7 -ESCARGOT_MESSAGE_EVAL_16BIT_START = 8 -ESCARGOT_MESSAGE_EVAL_16BIT = 9 -ESCARGOT_MESSAGE_EVAL_WITHOUT_STOP_8BIT_START = 10 -ESCARGOT_MESSAGE_EVAL_WITHOUT_STOP_8BIT = 11 -ESCARGOT_MESSAGE_EVAL_WITHOUT_STOP_16BIT_START = 12 -ESCARGOT_MESSAGE_EVAL_WITHOUT_STOP_16BIT = 13 -ESCARGOT_MESSAGE_WATCH_8BIT_START = 14 -ESCARGOT_MESSAGE_WATCH_8BIT = 15 -ESCARGOT_MESSAGE_WATCH_16BIT_START = 16 -ESCARGOT_MESSAGE_WATCH_16BIT = 17 -ESCARGOT_MESSAGE_GET_BACKTRACE = 18 -ESCARGOT_MESSAGE_GET_SCOPE_CHAIN = 19 -ESCARGOT_MESSAGE_GET_SCOPE_VARIABLES = 20 -ESCARGOT_MESSAGE_GET_OBJECT = 21 -ESCARGOT_DEBUGGER_CLIENT_SOURCE_8BIT_START = 22 -ESCARGOT_DEBUGGER_CLIENT_SOURCE_8BIT = 23 -ESCARGOT_DEBUGGER_CLIENT_SOURCE_16BIT_START = 24 -ESCARGOT_DEBUGGER_CLIENT_SOURCE_16BIT = 25 -ESCARGOT_DEBUGGER_THERE_WAS_NO_SOURCE = 26 -ESCARGOT_DEBUGGER_PENDING_CONFIG = 27 -ESCARGOT_DEBUGGER_PENDING_RESUME = 28 -ESCARGOT_DEBUGGER_WAIT_BEFORE_EXIT = 29 -ESCARGOT_DEBUGGER_TAKE_HEAP_SNAPSHOT = 30 -ESCARGOT_DEBUGGER_STOP = 31 +ESCARGOT_MESSAGE_RESTART = 6 +ESCARGOT_MESSAGE_EVAL_8BIT_START = 7 +ESCARGOT_MESSAGE_EVAL_8BIT = 8 +ESCARGOT_MESSAGE_EVAL_16BIT_START = 9 +ESCARGOT_MESSAGE_EVAL_16BIT = 10 +ESCARGOT_MESSAGE_EVAL_WITHOUT_STOP_8BIT_START = 11 +ESCARGOT_MESSAGE_EVAL_WITHOUT_STOP_8BIT = 12 +ESCARGOT_MESSAGE_EVAL_WITHOUT_STOP_16BIT_START = 13 +ESCARGOT_MESSAGE_EVAL_WITHOUT_STOP_16BIT = 14 +ESCARGOT_MESSAGE_WATCH_8BIT_START = 15 +ESCARGOT_MESSAGE_WATCH_8BIT = 16 +ESCARGOT_MESSAGE_WATCH_16BIT_START = 17 +ESCARGOT_MESSAGE_WATCH_16BIT = 18 +ESCARGOT_MESSAGE_GET_BACKTRACE = 19 +ESCARGOT_MESSAGE_GET_SCOPE_CHAIN = 20 +ESCARGOT_MESSAGE_GET_SCOPE_VARIABLES = 21 +ESCARGOT_MESSAGE_GET_OBJECT = 22 +ESCARGOT_DEBUGGER_CLIENT_SOURCE_8BIT_START = 23 +ESCARGOT_DEBUGGER_CLIENT_SOURCE_8BIT = 24 +ESCARGOT_DEBUGGER_CLIENT_SOURCE_16BIT_START = 25 +ESCARGOT_DEBUGGER_CLIENT_SOURCE_16BIT = 26 +ESCARGOT_DEBUGGER_THERE_WAS_NO_SOURCE = 27 +ESCARGOT_DEBUGGER_PENDING_CONFIG = 28 +ESCARGOT_DEBUGGER_PENDING_RESUME = 29 +ESCARGOT_DEBUGGER_WAIT_BEFORE_EXIT = 30 +ESCARGOT_DEBUGGER_TAKE_HEAP_SNAPSHOT = 31 +ESCARGOT_DEBUGGER_STOP = 32 # Environment record types @@ -638,6 +639,10 @@ def _exec_command(self, command_id): command_id) self.channel.send_message(self.byte_order, message) + def send_restart(self): + self.prompt = False + self._exec_command(ESCARGOT_MESSAGE_RESTART) + def do_continue(self): self.prompt = False self._exec_command(ESCARGOT_MESSAGE_CONTINUE) diff --git a/tools/debugger/tests/do_restart.cmd b/tools/debugger/tests/do_restart.cmd new file mode 100644 index 000000000..cb6b7c035 --- /dev/null +++ b/tools/debugger/tests/do_restart.cmd @@ -0,0 +1,6 @@ +s +restart +n +s +re +c diff --git a/tools/debugger/tests/do_restart.expected b/tools/debugger/tests/do_restart.expected new file mode 100644 index 000000000..fdf997480 --- /dev/null +++ b/tools/debugger/tests/do_restart.expected @@ -0,0 +1,18 @@ +Connecting to: localhost:6501 +Connection created!!! +Stopped at tools/debugger/tests/do_restart.js:9 +(escargot-debugger) s +Stopped at tools/debugger/tests/do_restart.js:2 (in hello() at line:1, col:1) +(escargot-debugger) restart +Stopped at tools/debugger/tests/do_restart.js:9 +(escargot-debugger) n +Print: Hello world! +Stopped at tools/debugger/tests/do_restart.js:10 +(escargot-debugger) s +Stopped at tools/debugger/tests/do_restart.js:6 (in bye() at line:5, col:1) +(escargot-debugger) re +Stopped at tools/debugger/tests/do_restart.js:9 +(escargot-debugger) c +Print: Hello world! +Print: Bye world! +Connection closed. diff --git a/tools/debugger/tests/do_restart.js b/tools/debugger/tests/do_restart.js new file mode 100644 index 000000000..7120b3a31 --- /dev/null +++ b/tools/debugger/tests/do_restart.js @@ -0,0 +1,10 @@ +function hello() { + print("Hello world!"); +} + +function bye() { + print("Bye world!"); +} + +hello(); +bye();