From c9f14ba1b4a329e0fffd590414b285e280f6d0ee Mon Sep 17 00:00:00 2001 From: "rginda%netscape.com" Date: Sat, 29 Apr 2000 00:23:06 +0000 Subject: [PATCH] somewhat weak debugger console hookup. build shouldn't *look* different yet. Moved Context::interpret() local vars into Context private, added accessors for stuff, using JavaScript::Lexer to lex debugger commands. Fixed sign comparison warning in icg.cpp --- js/js2/Makefile | 3 +- js/js2/debugger.cpp | 151 ++++++++++++++++++++++++++++++++++++ js/js2/debugger.h | 26 +++++++ js/js2/icodegenerator.cpp | 2 +- js/js2/interpreter.cpp | 139 ++++++++++++++++++++------------- js/js2/interpreter.h | 36 +++++++-- js/js2/js2.cpp | 56 ++++++++++--- js/js2/vmtypes.h | 4 +- js2/src/Makefile | 3 +- js2/src/debugger.cpp | 151 ++++++++++++++++++++++++++++++++++++ js2/src/debugger.h | 26 +++++++ js2/src/icodegenerator.cpp | 2 +- js2/src/interpreter.cpp | 139 ++++++++++++++++++++------------- js2/src/interpreter.h | 36 +++++++-- js2/src/vmtypes.h | 4 +- js2/tests/cpp/js2_shell.cpp | 56 ++++++++++--- 16 files changed, 684 insertions(+), 150 deletions(-) create mode 100644 js/js2/debugger.cpp create mode 100644 js2/src/debugger.cpp diff --git a/js/js2/Makefile b/js/js2/Makefile index 1a977a77efb..75f7318649d 100644 --- a/js/js2/Makefile +++ b/js/js2/Makefile @@ -11,7 +11,8 @@ objs = hash.o \ parser.o \ utilities.o \ world.o \ - vmtypes.o + vmtypes.o \ + debugger.o gc_path = ../../gc/boehm/ diff --git a/js/js2/debugger.cpp b/js/js2/debugger.cpp new file mode 100644 index 00000000000..14fc3f95dec --- /dev/null +++ b/js/js2/debugger.cpp @@ -0,0 +1,151 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the JavaScript 2 Prototype. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU Public License (the "GPL"), in which case the + * provisions of the GPL are applicable instead of those above. + * If you wish to allow use of your version of this file only + * under the terms of the GPL and not to allow others to use your + * version of this file under the NPL, indicate your decision by + * deleting the provisions above and replace them with the notice + * and other provisions required by the GPL. If you do not delete + * the provisions above, a recipient may use your version of this + * file under either the NPL or the GPL. + */ + +#include "world.h" +#include "debugger.h" + +#include +#include +#include + +namespace JavaScript { +namespace Debugger { + + enum ShellCommand { + ASSEMBLE, + DISSASSEMBLE, + STEP, + PRINT, + PRINT2, + EXIT, + COMMAND_COUNT + }; + + static const char *shell_cmd_names[] = { + "assemble", + "dissassemble", + "step", + "print", + "print2", + "exit", + 0 + }; + + /* return true if str2 starts with/is str1 + * XXX ignore case */ + bool + startsWith (const String &str1, const String &str2) + { + uint n; + size_t m = str1.size(); + + if (m > str2.size()) + return false; + + for (n = 0; n < m; ++n) + if (str1[n] != str2[n]) + return false; + + return true; + } + + bool + Shell::doCommand (Interpreter::Context */*context*/, const String &source) + { + Lexer lex (mWorld, source, widenCString("debugger console"), 0); + const String *cmd; + ShellCommand match = COMMAND_COUNT; + int ambig_matches = 0; + + const Token &t = lex.get(true); + + if (t.hasKind(Token::identifier)) + cmd = &(t.getIdentifier()); + else + { + mErr << "you idiot.\n"; + return false; + } + + for (int i = ASSEMBLE; i < COMMAND_COUNT; ++i) + { + String possibleMatch (widenCString(shell_cmd_names[i])); + if (startsWith(*cmd, possibleMatch)) + { + if (cmd->size() == possibleMatch.size()) + { + /* exact match */ + ambig_matches = 0; + match = (ShellCommand)i; + break; + } + else if (match == COMMAND_COUNT) /* no match yet */ + match = (ShellCommand)i; + else + ++ambig_matches; /* something already matched, + * ambiguous command */ + } + + } + + if (ambig_matches == 0) + switch (match) + { + case COMMAND_COUNT: + mErr << "Unknown command '" << *cmd << "'.\n"; + break; + + case ASSEMBLE: + case DISSASSEMBLE: + case STEP: + case PRINT: + default: + mErr << "Input '" << *cmd << "' matched command '" << + shell_cmd_names[match] << "'.\n"; + break; + + } + else + mErr << "Ambiguous command '" << *cmd << "', " << + (uint)(ambig_matches + 1) << " similar commands.\n"; + + return (ambig_matches == 0); + + } + +} /* namespace Debugger */ +} /* namespace JavaScript */ + + + + diff --git a/js/js2/debugger.h b/js/js2/debugger.h index bc8215446dc..a327ba76956 100644 --- a/js/js2/debugger.h +++ b/js/js2/debugger.h @@ -33,9 +33,29 @@ /* this is all vapor, don't take it to serious yet */ +#ifndef debugger_h +#define debugger_h + +#include "utilities.h" +#include "interpreter.h" + namespace JavaScript { namespace Debugger { + class Shell { + public: + Shell (World &aWorld, Formatter &aOut, Formatter &aErr) : + mWorld(aWorld), mOut(aOut), mErr(aErr) {} + bool doCommand (Interpreter::Context *context, + const String &aSource); + + private: + World &mWorld; + Formatter &mOut, &mErr; + + }; + +#if 0 typedef void (debuggerCallback) (Context *aContext, ICodeDebugger *aICD); class Breakpoint { @@ -78,6 +98,9 @@ namespace Debugger { KILL }; + /** + * tell the debugger what to do when the debuggerCallback returns + */ void setNextAction (DebuggerAction aAction); /** @@ -105,6 +128,9 @@ namespace Debugger { }; /* class ICodeDebugger */ +#endif /* 0 */ + } /* namespace Debugger */ } /* namespace JavaScript */ +#endif /* debugger_h */ diff --git a/js/js2/icodegenerator.cpp b/js/js2/icodegenerator.cpp index 8cd505be0b3..a9cdcba4efe 100644 --- a/js/js2/icodegenerator.cpp +++ b/js/js2/icodegenerator.cpp @@ -66,7 +66,7 @@ namespace ICG { iCode = new InstructionStream(); if (hasTryStatement) exceptionRegister = allocateVariable(world->identifiers[widenCString("__exceptionObject__")]); - for (int i = 0; i < switchStatementNesting; i++) { + for (uint i = 0; i < switchStatementNesting; i++) { String s = widenCString("__switchControlVariable__"); char num[8]; sprintf(num, "%.2d", i); diff --git a/js/js2/interpreter.cpp b/js/js2/interpreter.cpp index aaa88543700..26a7f097695 100644 --- a/js/js2/interpreter.cpp +++ b/js/js2/interpreter.cpp @@ -33,7 +33,6 @@ #include "interpreter.h" #include "world.h" -#include "vmtypes.h" namespace JavaScript { namespace JSTypes { @@ -126,9 +125,9 @@ struct Linkage : public Context::Frame, public gc_base { { } - Context::Frame* getNext() { return mNext; } +Context::Frame* getNext() { return mNext; } - void getState(InstructionIterator& pc, JSValues*& registers, ICodeModule*& iCode) +void getState(InstructionIterator& pc, JSValues*& registers, ICodeModule*& iCode) { pc = mReturnPC; registers = &mActivation->mRegisters; @@ -138,34 +137,38 @@ struct Linkage : public Context::Frame, public gc_base { JSValue Context::interpret(ICodeModule* iCode, const JSValues& args) { - // initial activation. - Activation* activation = new Activation(iCode, args); - JSValues* registers = &activation->mRegisters; + assert(mActivation == 0); /* recursion == bad */ + + JSValue rv; + mActivation = new Activation(iCode, args); + JSValues* registers = &mActivation->mRegisters; + mICode = iCode; InstructionIterator begin_pc = iCode->its_iCode->begin(); - InstructionIterator pc = begin_pc; + mPc = begin_pc; // stack of all catch/finally handlers available for the current activation - std::stack subroutineStack; // to implement jsr/rts for finally code + // to implement jsr/rts for finally code + std::stack subroutineStack; + while (true) { try { // tell any listeners about the current execution state. // XXX should only do this if we're single stepping/tracing. - if (mListeners.size()) { - broadcast(pc, registers, activation->mICode); - } - Instruction* instruction = *pc; + if (mListeners.size()) + broadcast(STEP); + + Instruction* instruction = *mPc; switch (instruction->op()) { case CALL: { Call* call = static_cast(instruction); - mLinkage = new Linkage(mLinkage, ++pc, begin_pc, activation, - op1(call)); - ICodeModule* target = - (*registers)[op2(call)].function->getICode(); - activation = new Activation(target, activation, op3(call)); - registers = &activation->mRegisters; - begin_pc = pc = target->its_iCode->begin(); + mLinkage = new Linkage(mLinkage, ++mPc, begin_pc, + mActivation, op1(call)); + mICode = (*registers)[op2(call)].function->getICode(); + mActivation = new Activation(mICode, mActivation, op3(call)); + registers = &mActivation->mRegisters; + begin_pc = mPc = mICode->its_iCode->begin(); } continue; @@ -174,13 +177,17 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args) JSValue result; Linkage *linkage = mLinkage; if (!linkage) - return result; + { + rv = result; + goto out; + } mLinkage = linkage->mNext; - activation = linkage->mActivation; - registers = &activation->mRegisters; + mActivation = linkage->mActivation; + registers = &mActivation->mRegisters; (*registers)[linkage->mResult] = result; - pc = linkage->mReturnPC; + mPc = linkage->mReturnPC; begin_pc = linkage->mBasePC; + mICode = mActivation->mICode; } continue; @@ -192,13 +199,17 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args) result = (*registers)[op1(ret)]; Linkage* linkage = mLinkage; if (!linkage) - return result; + { + rv = result; + goto out; + } mLinkage = linkage->mNext; - activation = linkage->mActivation; - registers = &activation->mRegisters; + mActivation = linkage->mActivation; + registers = &mActivation->mRegisters; (*registers)[linkage->mResult] = result; - pc = linkage->mReturnPC; + mPc = linkage->mReturnPC; begin_pc = linkage->mBasePC; + mICode = mActivation->mICode; } continue; case MOVE: @@ -281,7 +292,7 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args) { GenericBranch* bra = static_cast(instruction); - pc = begin_pc + ofs(bra); + mPc = begin_pc + ofs(bra); continue; } break; @@ -290,7 +301,7 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args) GenericBranch* bc = static_cast(instruction); if ((*registers)[src1(bc)].i32 < 0) { - pc = begin_pc + ofs(bc); + mPc = begin_pc + ofs(bc); continue; } } @@ -300,7 +311,7 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args) GenericBranch* bc = static_cast(instruction); if ((*registers)[src1(bc)].i32 <= 0) { - pc = begin_pc + ofs(bc); + mPc = begin_pc + ofs(bc); continue; } } @@ -310,7 +321,7 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args) GenericBranch* bc = static_cast(instruction); if ((*registers)[src1(bc)].i32 == 0) { - pc = begin_pc + ofs(bc); + mPc = begin_pc + ofs(bc); continue; } } @@ -320,7 +331,7 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args) GenericBranch* bc = static_cast(instruction); if ((*registers)[src1(bc)].i32 != 0) { - pc = begin_pc + ofs(bc); + mPc = begin_pc + ofs(bc); continue; } } @@ -330,7 +341,7 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args) GenericBranch* bc = static_cast(instruction); if ((*registers)[src1(bc)].i32 >= 0) { - pc = begin_pc + ofs(bc); + mPc = begin_pc + ofs(bc); continue; } } @@ -340,7 +351,7 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args) GenericBranch* bc = static_cast(instruction); if ((*registers)[src1(bc)].i32 > 0) { - pc = begin_pc + ofs(bc); + mPc = begin_pc + ofs(bc); continue; } } @@ -396,7 +407,8 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args) case NOT: { Not* nt = static_cast(instruction); - (*registers)[dst(nt)] = JSValue(int32(!(*registers)[src1(nt)].i32)); + (*registers)[dst(nt)] = + JSValue(int32(!(*registers)[src1(nt)].i32)); } break; @@ -409,28 +421,29 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args) case TRY: { // push the catch handler address onto the try stack Try* tri = static_cast(instruction); - activation->catchStack.push_back(new Handler(op1(tri), op2(tri))); + mActivation->catchStack.push_back(new Handler(op1(tri), + op2(tri))); } break; case ENDTRY : { - Handler *h = activation->catchStack.back(); - activation->catchStack.pop_back(); + Handler *h = mActivation->catchStack.back(); + mActivation->catchStack.pop_back(); delete h; } break; case JSR : { - subroutineStack.push(++pc); + subroutineStack.push(++mPc); Jsr* jsr = static_cast(instruction); uint32 offset = ofs(jsr); - pc = begin_pc + offset; + mPc = begin_pc + offset; continue; } case RTS : { ASSERT(!subroutineStack.empty()); - pc = subroutineStack.top(); + mPc = subroutineStack.top(); subroutineStack.pop(); continue; } @@ -441,24 +454,24 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args) } // increment the program counter. - ++pc; + ++mPc; } catch (JSException x) { if (mLinkage) { - if (activation->catchStack.empty()) { + if (mActivation->catchStack.empty()) { Linkage *pLinkage = mLinkage; for (; pLinkage != NULL; pLinkage = pLinkage->mNext) { if (!pLinkage->mActivation->catchStack.empty()) { - activation = pLinkage->mActivation; - Handler *h = activation->catchStack.back(); - registers = &activation->mRegisters; + mActivation = pLinkage->mActivation; + Handler *h = mActivation->catchStack.back(); + registers = &mActivation->mRegisters; begin_pc = pLinkage->mBasePC; if (h->catchTarget) { - pc = begin_pc + h->catchTarget->mOffset; + mPc = begin_pc + h->catchTarget->mOffset; } else { ASSERT(h->finallyTarget); - pc = begin_pc + h->finallyTarget->mOffset; + mPc = begin_pc + h->finallyTarget->mOffset; } mLinkage = pLinkage; break; @@ -468,22 +481,34 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args) continue; } else { - Handler *h = activation->catchStack.back(); + Handler *h = mActivation->catchStack.back(); if (h->catchTarget) { - pc = begin_pc + h->catchTarget->mOffset; + mPc = begin_pc + h->catchTarget->mOffset; } else { ASSERT(h->finallyTarget); - pc = begin_pc + h->finallyTarget->mOffset; + mPc = begin_pc + h->finallyTarget->mOffset; } continue; } } - return x.value; + rv = x.value; } } + + out: + delete mActivation; + mActivation = 0; + return rv; + } /* interpret */ + +JSValues &Context::getRegisters() +{ + return mActivation->mRegisters; +} + void Context::addListener(Listener* listener) { mListeners.push_back(listener); @@ -493,7 +518,8 @@ typedef std::vector::iterator ListenerIterator; void Context::removeListener(Listener* listener) { - for (ListenerIterator i = mListeners.begin(), e = mListeners.end(); i != e; ++i) { + for (ListenerIterator i = mListeners.begin(), e = mListeners.end(); + i != e; ++i) { if (*i == listener) { mListeners.erase(i); break; @@ -501,11 +527,12 @@ void Context::removeListener(Listener* listener) } } -void Context::broadcast(InstructionIterator pc, JSValues* registers, ICodeModule* iCode) +void Context::broadcast(InterpretStage Stage) { - for (ListenerIterator i = mListeners.begin(), e = mListeners.end(); i != e; ++i) { + for (ListenerIterator i = mListeners.begin(), e = mListeners.end(); + i != e; ++i) { Listener* listener = *i; - listener->listen(this, pc, registers, iCode); + listener->listen(this, Stage); } } diff --git a/js/js2/interpreter.h b/js/js2/interpreter.h index 43297fde002..0082fb77b7d 100644 --- a/js/js2/interpreter.h +++ b/js/js2/interpreter.h @@ -22,6 +22,7 @@ #include "utilities.h" #include "jstypes.h" +#include "vmtypes.h" #include "icodegenerator.h" #include "gc_allocator.h" @@ -31,14 +32,20 @@ namespace Interpreter { using namespace ICG; using namespace JSTypes; + struct Activation; + + enum InterpretStage { + STEP, + CATCH, + TRAP + }; + struct Linkage; class Context : public gc_base { public: explicit Context(World& world, JSNamespace* aGlobal) - : mWorld(world), mGlobal(aGlobal), mLinkage(0) - { - } + : mWorld(world), mGlobal(aGlobal), mLinkage(0), mActivation(0) {} JSNamespace* getGlobalObject() { return mGlobal; } @@ -49,9 +56,21 @@ namespace Interpreter { return t; } + InstructionIterator getPC() + { + return mPc; + } + + JSValues &getRegisters(); + + ICodeModule *getICode() + { + return mICode; + } + class Listener { public: - virtual void listen(Context* context, InstructionIterator pc, JSValues* registers, ICodeModule* iCode) = 0; + virtual void listen(Context *context, InterpretStage Stage) = 0; }; void addListener(Listener* listener); @@ -60,7 +79,8 @@ namespace Interpreter { class Frame { public: virtual Frame* getNext() = 0; - virtual void getState(InstructionIterator& pc, JSValues*& registers, ICodeModule*& iCode) = 0; + virtual void getState(InstructionIterator& pc, JSValues*& registers, + ICodeModule*& iCode) = 0; }; Frame* getFrames(); @@ -68,13 +88,17 @@ namespace Interpreter { JSValue interpret(ICodeModule* iCode, const JSValues& args); private: - void broadcast(InstructionIterator pc, JSValues* registers, ICodeModule* iCode); + void broadcast(InterpretStage Stage); private: World& mWorld; JSNamespace* mGlobal; Linkage* mLinkage; std::vector mListeners; + Activation* mActivation; + ICodeModule *mICode; + InstructionIterator mPc; + }; /* class Context */ } /* namespace Interpreter */ diff --git a/js/js2/js2.cpp b/js/js2/js2.cpp index 56c89dcbd12..227839974c4 100644 --- a/js/js2/js2.cpp +++ b/js/js2/js2.cpp @@ -28,6 +28,7 @@ #include "interpreter.h" #include "icodegenerator.h" +#include "debugger.h" #if defined(XP_MAC) && !defined(XP_MAC_MPW) #include @@ -79,6 +80,8 @@ static bool promptLine(LineReader &inReader, string &s, } +JavaScript::World world; +JavaScript::Debugger::Shell jsd(world, JavaScript::stdOut, JavaScript::stdOut); const bool showTokens = true; static void readEvalPrint(FILE *in, World &world) @@ -123,27 +126,60 @@ static void readEvalPrint(FILE *in, World &world) stdOut << '\n'; } + +#if 0 +class Tracer : public Context::Listener { + void listen(Context* context, InterpretStage stage) + { + static String lastLine (widenCString("\n")); + String line; + ICodeModule *iCode = context->getICode(); + InstructionIterator pc = context->getPC(); + + stdOut << "jsd [pc:"; + printFormat (stdOut, "%04X", (pc - iCode->its_iCode->begin())); + stdOut << ", reason:" << (uint)stage << "]> "; + + std::getline(cin, line); + if (line.size() == 0) + line = lastLine; + else + { + line.append(widenCString("\n")); + lastLine = line; + } + + jsd.doCommand(context, line); + } +}; + +#else /** * Poor man's instruction tracing facility. */ class Tracer : public Context::Listener { typedef InstructionStream::difference_type InstructionOffset; - void listen(Context* /*context*/, InstructionIterator pc, - JSValues* registers, ICodeModule* iCode) + void listen(Context* context, InterpretStage /*stage*/) { + ICodeModule *iCode = context->getICode(); + JSValues ®isters = context->getRegisters(); + InstructionIterator pc = context->getPC(); + + InstructionOffset offset = (pc - iCode->its_iCode->begin()); printFormat(stdOut, "%04X: ", offset); Instruction* i = *pc; stdOut << *i; if (i->op() != BRANCH && i->count() > 0) { stdOut << " ["; - i->printOperands(stdOut, *registers); + i->printOperands(stdOut, registers); stdOut << "]\n"; } else { stdOut << '\n'; } } }; +#endif static void testICG(World &world) { @@ -553,15 +589,17 @@ int main(int argc, char **argv) #if defined(XP_MAC) && !defined(XP_MAC_MPW) initConsole("\pJavaScript Shell", "Welcome to the js2 shell.\n", argc, argv); #endif - JavaScript::World world; + #if 1 - assert(JavaScript::Shell::testFactorial(world, 5) == 120); - assert(JavaScript::Shell::testObjects(world, 5) == 5); - assert(JavaScript::Shell::testProto(world, 5) == 5); + using namespace JavaScript::Shell; + + assert(testFactorial(world, 5) == 120); + assert(testObjects(world, 5) == 5); + assert(testProto(world, 5) == 5); // JavaScript::Shell::testICG(world); - assert(JavaScript::Shell::testFunctionCall(world, 5) == 5); + assert(testFunctionCall(world, 5) == 5); #endif - JavaScript::Shell::readEvalPrint(stdin, world); + readEvalPrint(stdin, world); return 0; // return ProcessArgs(argv + 1, argc - 1); } diff --git a/js/js2/vmtypes.h b/js/js2/vmtypes.h index 567bd06811c..fca0ccc13a2 100644 --- a/js/js2/vmtypes.h +++ b/js/js2/vmtypes.h @@ -202,9 +202,9 @@ namespace VM { typedef std::vector