diff --git a/mozilla/js/js2/debugger.cpp b/mozilla/js/js2/debugger.cpp index 1b5444e1682..c706ed390e3 100644 --- a/mozilla/js/js2/debugger.cpp +++ b/mozilla/js/js2/debugger.cpp @@ -40,27 +40,48 @@ namespace JavaScript { namespace Debugger { - + enum ShellCommand { ASSEMBLE, + AMBIGUOUS, + AMBIGUOUS2, + CONTINUE, DISSASSEMBLE, - STEP, - PRINT, - PRINT2, EXIT, + HELP, + LET, + PRINT, + REGISTER, + STEP, COMMAND_COUNT }; - static const char *shell_cmd_names[] = { - "assemble", - "dissassemble", - "step", - "print", - "print2", - "exit", - 0 - }; + static const char *shell_cmds[][3] = { + {"assemble", " ", "nyi"}, + {"ambiguous", " ", "Test command for ambiguous command detection"}, + {"ambiguous2", " ", "Test command for ambiguous command detection"}, + {"continue", " ", "Continue execution until complete."}, + {"dissassemble", "", "nyi"}, + {"exit", " ", "nyi"}, + {"help", " ", "Display this message."}, + {"let", " ", "Set a debugger environment variable."}, + {"print", " ", "nyi"}, + {"register", " ", "Show the value of a single register or all \ +registers, or set the value of a single register."}, + {"step", " ", "Execute the current opcode."}, + {0, 0} /* sentry */ + }; + enum ShellVariable { + TRACE, + VARIABLE_COUNT + }; + + static const char *shell_vars[][3] = { + {"trace", "", "(bool) always show opcode before execution."}, + {0, 0} /* sentry */ + }; + /* return true if str2 starts with/is str1 * XXX ignore case */ static bool @@ -79,38 +100,31 @@ namespace Debugger { 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; - uint32 ambig_matches = 0; - - const Token &t = lex.get(true); - if (t.hasKind(Token::identifier)) - cmd = &(t.getIdentifier()); - else - { - mErr << "you idiot.\n"; - return false; - } + /** + * locate the best match for |partial| in the command list |list|. + * if no matches are found, return |length|, if multiple matches are found, + * return |length| plus the number of ambiguous matches + */ + static uint32 + matchElement (const String &partial, const char *list[][3], size_t length) + { + uint32 ambig_matches = 0; + uint32 match = length; - for (int i = ASSEMBLE; i < COMMAND_COUNT; ++i) + for (uint32 i = 0; i < length ; ++i) { - String possibleMatch (widenCString(shell_cmd_names[i])); - if (startsWith(*cmd, possibleMatch)) + String possibleMatch (widenCString(list[i][0])); + if (startsWith(partial, possibleMatch)) { - if (cmd->size() == possibleMatch.size()) + if (partial.size() == possibleMatch.size()) { /* exact match */ ambig_matches = 0; - match = (ShellCommand)i; - break; + return i; } else if (match == COMMAND_COUNT) /* no match yet */ - match = (ShellCommand)i; + match = i; else ++ambig_matches; /* something already matched, * ambiguous command */ @@ -119,30 +133,235 @@ namespace Debugger { } if (ambig_matches == 0) - switch (match) + return match; + else + return length + ambig_matches; + + } + + + + static void + showHelp(Formatter &out) + { + int i; + + for (i = 0; shell_cmds[i][0] != 0; i++) + out << shell_cmds[i][0] << shell_cmds[i][1] << "\t" << + shell_cmds[i][2] << "\n"; + } + + static void + showCurrentOp(Context* cx, Formatter &aOut) + { + ICodeModule *iCode = cx->getICode(); + JSValues ®isters = cx->getRegisters(); + InstructionIterator pc = cx->getPC(); + + printFormat(aOut, "trace [%02u:%04u]: ", + iCode->mID, (pc - iCode->its_iCode->begin())); + Instruction* i = *pc; + stdOut << *i; + if (i->op() != BRANCH && i->count() > 0) { + aOut << " ["; + i->printOperands(stdOut, registers); + aOut << "]\n"; + } else { + aOut << '\n'; + } + } + + void + Shell::listen(Context* cx, InterpretStage stage) + { + + if (mTraceFlag) + showCurrentOp (cx, mOut); + + if (!(mStopMask & stage)) + return; + + static String lastLine (widenCString("help\n")); + String line; + + do { + ICodeModule *iCode = cx->getICode(); + InstructionIterator pc = cx->getPC(); + + stdOut << "jsd [pc:"; + printFormat (stdOut, "%04X", (pc - iCode->its_iCode->begin())); + stdOut << "]> "; + + getline(cin, line); + if (line.size() == 0) + line = lastLine; + else + { + line.append(widenCString("\n")); + lastLine = line; + } + + } while (doCommand(cx, line)); + } + + + /** + * lex and execute the debugger command in |source|, return true if the + * command does not require the script being debugged to continue (eg, ask + * for more debugger input.) + */ + bool + Shell::doCommand (Interpreter::Context *cx, const String &source) + { + Lexer lex (mWorld, source, widenCString("debugger console"), 0); + const String *cmd; + uint32 match; + bool rv = true; + + const Token &t = lex.get(true); + + if (t.hasKind(Token::identifier)) + cmd = &(t.getIdentifier()); + else + { + mErr << "you idiot.\n"; + return true; + } + + match = matchElement (*cmd, shell_cmds, (size_t)COMMAND_COUNT); + + if (match <= (uint32)COMMAND_COUNT) + switch ((ShellCommand)match) { case COMMAND_COUNT: mErr << "Unknown command '" << *cmd << "'.\n"; break; - - case ASSEMBLE: + + case AMBIGUOUS: + case AMBIGUOUS2: + mErr << "I pity the foo.\n"; + break; + + case CONTINUE: + mStopMask &= (IS_ALL ^ IS_STEP); + rv = false; + break; + case DISSASSEMBLE: - case STEP: + mOut << *cx->getICode(); + break; + + case HELP: + showHelp (mOut); + break; + case PRINT: + doPrint (cx, lex); + break; + + case STEP: + mStopMask |= IS_STEP; + rv = false; + break; + + case LET: + doSetVariable (lex); + break; + default: - mErr << "Input '" << *cmd << "' matched command '" << - shell_cmd_names[match] << "'.\n"; + mErr << "Input '" << *cmd << "' matched unimplemented " << + "command '" << shell_cmds[match][0] << "'.\n"; break; } else mErr << "Ambiguous command '" << *cmd << "', " << - (ambig_matches + 1) << " similar commands.\n"; + (match - (uint32)COMMAND_COUNT + 1) << " similar commands.\n"; - return (ambig_matches == 0); - + return rv; } + void + Shell::doSetVariable (Lexer &lex) + { + uint32 match; + const String *varname; + const Token *t = &(lex.get(true)); + + if (t->hasKind(Token::identifier)) + varname = &(t->getIdentifier()); + else + { + mErr << "invalid variable name.\n"; + return; + } + + match = matchElement (*varname, shell_vars, (size_t)VARIABLE_COUNT); + + if (match <= (uint32)VARIABLE_COUNT) + switch ((ShellVariable)match) + { + case VARIABLE_COUNT: + mErr << "Unknown variable '" << *varname << "'.\n"; + break; + + case TRACE: + t = &(lex.get(true)); + if (t->hasKind(Token::assignment)) + t = &(lex.get(true)); /* optional = */ + + if (t->hasKind(Token::True)) + mTraceFlag = true; + else if (t->hasKind(Token::False)) + mTraceFlag = false; + else + goto badval; + break; + + default: + mErr << "Variable '" << *varname << + "' matched unimplemented variable '" << + shell_vars[match][0] << "'.\n"; + } + else + mErr << "Ambiguous variable '" << *varname << "', " << + (match - (uint32)COMMAND_COUNT + 1) << " similar variables.\n"; + + return; + + badval: + mErr << "Invalid value for variable '" << + shell_vars[(ShellVariable)match][0] << "'\n"; + + + } + + void + Shell::doPrint (Context *cx, Lexer &lex) + { + const Token *t = &(lex.get(true)); + + if (!(t->hasKind(Token::identifier))) + { + mErr << "Invalid register name.\n"; + return; + } + +/* + const StringAtom *name = &(t->getIdentifier()); + + VariableMap::iterator i = ((cx->getICode())->itsVariables)->find(*name); +// if (i) + mOut << (*i).first << " = " << (*i).second << "\n"; +// else +// mOut << "No " << *name << " defined.\n"; + +*/ + } + + + + } /* namespace Debugger */ } /* namespace JavaScript */ diff --git a/mozilla/js/js2/debugger.h b/mozilla/js/js2/debugger.h index a327ba76956..00d3f3bec92 100644 --- a/mozilla/js/js2/debugger.h +++ b/mozilla/js/js2/debugger.h @@ -41,38 +41,20 @@ 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); + using namespace Interpreter; class Breakpoint { + public: /* representation of a breakpoint */ void set(); void clear(); bool getState(); InstructionIterator getPC(); - }; - + }; + class ICodeDebugger { public: - /** - * set a debugger callback, returning the old one - */ - static debuggerCallback * setCallback (debuggerCallback *aCallback); - /** * install an icdebugger on a context */ @@ -128,7 +110,52 @@ namespace Debugger { }; /* class ICodeDebugger */ -#endif /* 0 */ + class Shell : public Context::Listener { + public: + Shell (World &aWorld, istream &aIn, Formatter &aOut, Formatter &aErr) : + mWorld(aWorld), mIn(aIn), mOut(aOut), mErr(aErr), + mStopMask(IS_ALL), mTraceFlag(true) + { + mDebugger = new ICodeDebugger(); + } + + ~Shell () + { + delete mDebugger; + } + + void listen(Context *context, InterpretStage stage); + + /** + * install on a context + */ + bool attachToContext (Context *aContext) + { + aContext->addListener (this); + return true; + } + + /** + * detach an icdebugger from a context + */ + bool detachFromContext (Context *aContext) + { + aContext->removeListener (this); + return true; + } + + private: + bool doCommand (Context *context, const String &aSource); + void doSetVariable (Lexer &lex); + void doPrint (Context *cx, Lexer &lex); + + World &mWorld; + istream &mIn; + Formatter &mOut, &mErr; + ICodeDebugger *mDebugger; + uint32 mStopMask; + bool mTraceFlag; + }; } /* namespace Debugger */ } /* namespace JavaScript */ diff --git a/mozilla/js/js2/icodegenerator.cpp b/mozilla/js/js2/icodegenerator.cpp index abff0823610..6fb12efcd41 100644 --- a/mozilla/js/js2/icodegenerator.cpp +++ b/mozilla/js/js2/icodegenerator.cpp @@ -46,12 +46,18 @@ namespace ICG { using namespace VM; + uint32 ICodeModule::sMaxID = 0; Formatter& operator<<(Formatter &f, ICodeGenerator &i) { return i.print(f); } - + + Formatter& operator<<(Formatter &f, ICodeModule &i) + { + return i.print(f); + } + // // ICodeGenerator // @@ -85,7 +91,7 @@ namespace ICG { ICodeModule *ICodeGenerator::complete() { ASSERT(stitcher.empty()); - ASSERT(labelSet == NULL); + //ASSERT(labelSet == NULL); #ifdef DEBUG for (LabelList::iterator i = labels.begin(); i != labels.end(); i++) { @@ -785,27 +791,17 @@ namespace ICG { Formatter& ICodeGenerator::print(Formatter& f) { - f << "ICG! " << (uint32)iCode->size() << "\n"; - for (InstructionIterator i = iCode->begin(); - i != iCode->end(); i++) { - bool isLabel = false; - - for (LabelList::iterator k = labels.begin(); - k != labels.end(); k++) - if ((ptrdiff_t)(*k)->mOffset == (i - iCode->begin())) { - f << "#" << (uint32)(i - iCode->begin()) << "\t"; - isLabel = true; - break; - } - - if (!isLabel) - f << "\t"; - - f << **i << "\n"; - } - + f << "ICG! " << (uint32)iCode->size() << "\n" << *iCode; return f; } + + Formatter& ICodeModule::print(Formatter& f) + { + f << "ICM! " << (uint32)its_iCode->size() << "\n" << *its_iCode; + return f; + } + + } // namespace ICG diff --git a/mozilla/js/js2/icodegenerator.h b/mozilla/js/js2/icodegenerator.h index 4134eb09c87..e73df07bca2 100644 --- a/mozilla/js/js2/icodegenerator.h +++ b/mozilla/js/js2/icodegenerator.h @@ -85,21 +85,25 @@ namespace ICG { class ICodeModule { public: - ICodeModule(InstructionStream *iCode, - VariableList *variables, - uint32 maxRegister, - uint32 maxParameter) : - its_iCode(iCode), - itsVariables(variables), - itsParameterCount(maxParameter), - itsMaxRegister(maxRegister) { } + ICodeModule(InstructionStream *iCode, VariableList *variables, + uint32 maxRegister, uint32 maxParameter) : + its_iCode(iCode), itsVariables(variables), + itsParameterCount(maxParameter), itsMaxRegister(maxRegister), + mID(++sMaxID) { } + + Formatter& print(Formatter& f); InstructionStream *its_iCode; VariableList *itsVariables; - uint32 itsMaxRegister; - uint32 itsParameterCount; + uint32 itsParameterCount; + uint32 itsMaxRegister; + uint32 mID; + static uint32 sMaxID; + }; - + + Formatter& operator<<(Formatter &f, ICodeModule &i); + /****************************************************************/ // An ICodeGenerator provides the interface between the parser and the diff --git a/mozilla/js/js2/interpreter.cpp b/mozilla/js/js2/interpreter.cpp index d3165e7e54e..eea00701b83 100644 --- a/mozilla/js/js2/interpreter.cpp +++ b/mozilla/js/js2/interpreter.cpp @@ -151,7 +151,7 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args) // tell any listeners about the current execution state. // XXX should only do this if we're single stepping/tracing. if (mListeners.size()) - broadcast(STEP); + broadcast(IS_STEP); Instruction* instruction = *mPC; switch (instruction->op()) { diff --git a/mozilla/js/js2/interpreter.h b/mozilla/js/js2/interpreter.h index 92567f5dd26..5eed215a7be 100644 --- a/mozilla/js/js2/interpreter.h +++ b/mozilla/js/js2/interpreter.h @@ -35,9 +35,11 @@ namespace Interpreter { struct Activation; enum InterpretStage { - STEP, - CATCH, - TRAP + IS_NONE = 0, + IS_STEP = 1, + IS_THROW = 2, + IS_TRAP = 4, + IS_ALL = 0xffff }; struct Linkage; diff --git a/mozilla/js/js2/js2.cpp b/mozilla/js/js2/js2.cpp index afa424e2fbb..6b9e6fb6e71 100644 --- a/mozilla/js/js2/js2.cpp +++ b/mozilla/js/js2/js2.cpp @@ -81,7 +81,8 @@ static bool promptLine(LineReader &inReader, string &s, JavaScript::World world; -JavaScript::Debugger::Shell jsd(world, JavaScript::stdOut, JavaScript::stdOut); +JavaScript::Debugger::Shell jsd(world, cin, JavaScript::stdOut, + JavaScript::stdOut); const bool showTokens = true; static void readEvalPrint(FILE *in, World &world) @@ -127,33 +128,6 @@ static void readEvalPrint(FILE *in, World &world) } -#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. */ @@ -167,7 +141,9 @@ class Tracer : public Context::Listener { InstructionOffset offset = (pc - iCode->its_iCode->begin()); - printFormat(stdOut, "%04X: ", offset); + printFormat(stdOut, "trace [%02u:%04u]: ", + iCode->mID, offset); + Instruction* i = *pc; stdOut << *i; if (i->op() != BRANCH && i->count() > 0) { @@ -179,7 +155,6 @@ class Tracer : public Context::Listener { } } }; -#endif static void testICG(World &world) { @@ -310,8 +285,11 @@ static float64 testFunctionCall(World &world, float64 n) { JSScope glob; Context cx(world, &glob); + /* Tracer t; cx.addListener(&t); + */ + jsd.attachToContext(&cx); uint32 position = 0; //StringAtom& global = world.identifiers[widenCString("global")]; diff --git a/mozilla/js/js2/tools/gencode.pl b/mozilla/js/js2/tools/gencode.pl index 0407fc16c3a..3623ff3dc30 100644 --- a/mozilla/js/js2/tools/gencode.pl +++ b/mozilla/js/js2/tools/gencode.pl @@ -82,13 +82,13 @@ $ops{"LOAD_NAME"} = { super => "Instruction_2", rem => "dest, name", - params => [ ("Register", "StringAtom*" ) ] + params => [ ("Register", "const StringAtom*" ) ] }; $ops{"SAVE_NAME"} = { super => "Instruction_2", rem => "name, source", - params => [ ("StringAtom*", "Register") ] + params => [ ("const StringAtom*", "Register") ] }; $ops{"NEW_OBJECT"} = { @@ -106,13 +106,13 @@ $ops{"GET_PROP"} = { super => "Instruction_3", rem => "dest, object, prop name", - params => [ ("Register", "Register", "StringAtom*") ] + params => [ ("Register", "Register", "const StringAtom*") ] }; $ops{"SET_PROP"} = { super => "Instruction_3", rem => "object, name, source", - params => [ ("Register", "StringAtom*", "Register") ] + params => [ ("Register", "const StringAtom*", "Register") ] }; $ops{"GET_ELEMENT"} = { @@ -392,7 +392,7 @@ sub get_print_body { push (@oplist, "\"R\" << mOp$op"); } elsif ($type eq "Label*") { push (@oplist, "\"Offset \" << mOp$op->mOffset"); - } elsif ($type eq "StringAtom*") { + } elsif ($type =~ /StringAtom/) { push (@oplist, "\"'\" << *mOp$op << \"'\""); } else { push (@oplist, "mOp$op"); diff --git a/mozilla/js/js2/vmtypes.cpp b/mozilla/js/js2/vmtypes.cpp index bf888a17452..6d49b5d3a37 100644 --- a/mozilla/js/js2/vmtypes.cpp +++ b/mozilla/js/js2/vmtypes.cpp @@ -76,6 +76,37 @@ namespace VM { return f; } + Formatter& operator<< (Formatter &f, InstructionStream &is) + { + + for (InstructionIterator i = is.begin(); + i != is.end(); i++) { + /* + bool isLabel = false; + + for (LabelList::iterator k = labels.begin(); + k != labels.end(); k++) + if ((ptrdiff_t)(*k)->mOffset == (i - is.begin())) { + f << "#" << (uint32)(i - is.begin()) << "\t"; + isLabel = true; + break; + } + + if (!isLabel) + f << "\t"; + + f << **i << "\n"; + */ + + printFormat(stdOut, "%04u", (uint32)(i - is.begin())); + f << ": " << **i << "\n"; + + } + + + return f; + } + } /* namespace VM */ } /* namespace JavaScript */ diff --git a/mozilla/js/js2/vmtypes.h b/mozilla/js/js2/vmtypes.h index 97cc9bc6d04..b98ffe9a0cc 100644 --- a/mozilla/js/js2/vmtypes.h +++ b/mozilla/js/js2/vmtypes.h @@ -176,6 +176,7 @@ namespace VM { typedef std::vector RegisterList; typedef std::vector InstructionStream; typedef InstructionStream::iterator InstructionIterator; + typedef std::map > VariableMap; /** * Helper to print Call operands. @@ -192,6 +193,7 @@ namespace VM { Formatter& operator<< (Formatter& f, Instruction& i); Formatter& operator<< (Formatter& f, RegisterList& rl); Formatter& operator<< (Formatter& f, const ArgList& al); + Formatter& operator<< (Formatter& f, InstructionStream& is); /********************************************************************/ @@ -325,7 +327,6 @@ namespace VM { /********************************************************************/ /* Specific opcodes */ - class Add : public Arithmetic { public: /* dest, source1, source2 */ @@ -514,11 +515,11 @@ namespace VM { } }; - class GetProp : public Instruction_3 { + class GetProp : public Instruction_3 { public: /* dest, object, prop name */ - GetProp (Register aOp1, Register aOp2, StringAtom* aOp3) : - Instruction_3 + GetProp (Register aOp1, Register aOp2, const StringAtom* aOp3) : + Instruction_3 (GET_PROP, aOp1, aOp2, aOp3) {}; virtual Formatter& print(Formatter& f) { f << opcodeNames[GET_PROP] << "\t" << "R" << mOp1 << ", " << "R" << mOp2 << ", " << "'" << *mOp3 << "'"; @@ -561,11 +562,11 @@ namespace VM { } }; - class LoadName : public Instruction_2 { + class LoadName : public Instruction_2 { public: /* dest, name */ - LoadName (Register aOp1, StringAtom* aOp2) : - Instruction_2 + LoadName (Register aOp1, const StringAtom* aOp2) : + Instruction_2 (LOAD_NAME, aOp1, aOp2) {}; virtual Formatter& print(Formatter& f) { f << opcodeNames[LOAD_NAME] << "\t" << "R" << mOp1 << ", " << "'" << *mOp2 << "'"; @@ -711,11 +712,11 @@ namespace VM { } }; - class SaveName : public Instruction_2 { + class SaveName : public Instruction_2 { public: /* name, source */ - SaveName (StringAtom* aOp1, Register aOp2) : - Instruction_2 + SaveName (const StringAtom* aOp1, Register aOp2) : + Instruction_2 (SAVE_NAME, aOp1, aOp2) {}; virtual Formatter& print(Formatter& f) { f << opcodeNames[SAVE_NAME] << "\t" << "'" << *mOp1 << "'" << ", " << "R" << mOp2; @@ -743,11 +744,11 @@ namespace VM { } }; - class SetProp : public Instruction_3 { + class SetProp : public Instruction_3 { public: /* object, name, source */ - SetProp (Register aOp1, StringAtom* aOp2, Register aOp3) : - Instruction_3 + SetProp (Register aOp1, const StringAtom* aOp2, Register aOp3) : + Instruction_3 (SET_PROP, aOp1, aOp2, aOp3) {}; virtual Formatter& print(Formatter& f) { f << opcodeNames[SET_PROP] << "\t" << "R" << mOp1 << ", " << "'" << *mOp2 << "'" << ", " << "R" << mOp3; diff --git a/mozilla/js2/src/debugger.cpp b/mozilla/js2/src/debugger.cpp index 1b5444e1682..c706ed390e3 100644 --- a/mozilla/js2/src/debugger.cpp +++ b/mozilla/js2/src/debugger.cpp @@ -40,27 +40,48 @@ namespace JavaScript { namespace Debugger { - + enum ShellCommand { ASSEMBLE, + AMBIGUOUS, + AMBIGUOUS2, + CONTINUE, DISSASSEMBLE, - STEP, - PRINT, - PRINT2, EXIT, + HELP, + LET, + PRINT, + REGISTER, + STEP, COMMAND_COUNT }; - static const char *shell_cmd_names[] = { - "assemble", - "dissassemble", - "step", - "print", - "print2", - "exit", - 0 - }; + static const char *shell_cmds[][3] = { + {"assemble", " ", "nyi"}, + {"ambiguous", " ", "Test command for ambiguous command detection"}, + {"ambiguous2", " ", "Test command for ambiguous command detection"}, + {"continue", " ", "Continue execution until complete."}, + {"dissassemble", "", "nyi"}, + {"exit", " ", "nyi"}, + {"help", " ", "Display this message."}, + {"let", " ", "Set a debugger environment variable."}, + {"print", " ", "nyi"}, + {"register", " ", "Show the value of a single register or all \ +registers, or set the value of a single register."}, + {"step", " ", "Execute the current opcode."}, + {0, 0} /* sentry */ + }; + enum ShellVariable { + TRACE, + VARIABLE_COUNT + }; + + static const char *shell_vars[][3] = { + {"trace", "", "(bool) always show opcode before execution."}, + {0, 0} /* sentry */ + }; + /* return true if str2 starts with/is str1 * XXX ignore case */ static bool @@ -79,38 +100,31 @@ namespace Debugger { 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; - uint32 ambig_matches = 0; - - const Token &t = lex.get(true); - if (t.hasKind(Token::identifier)) - cmd = &(t.getIdentifier()); - else - { - mErr << "you idiot.\n"; - return false; - } + /** + * locate the best match for |partial| in the command list |list|. + * if no matches are found, return |length|, if multiple matches are found, + * return |length| plus the number of ambiguous matches + */ + static uint32 + matchElement (const String &partial, const char *list[][3], size_t length) + { + uint32 ambig_matches = 0; + uint32 match = length; - for (int i = ASSEMBLE; i < COMMAND_COUNT; ++i) + for (uint32 i = 0; i < length ; ++i) { - String possibleMatch (widenCString(shell_cmd_names[i])); - if (startsWith(*cmd, possibleMatch)) + String possibleMatch (widenCString(list[i][0])); + if (startsWith(partial, possibleMatch)) { - if (cmd->size() == possibleMatch.size()) + if (partial.size() == possibleMatch.size()) { /* exact match */ ambig_matches = 0; - match = (ShellCommand)i; - break; + return i; } else if (match == COMMAND_COUNT) /* no match yet */ - match = (ShellCommand)i; + match = i; else ++ambig_matches; /* something already matched, * ambiguous command */ @@ -119,30 +133,235 @@ namespace Debugger { } if (ambig_matches == 0) - switch (match) + return match; + else + return length + ambig_matches; + + } + + + + static void + showHelp(Formatter &out) + { + int i; + + for (i = 0; shell_cmds[i][0] != 0; i++) + out << shell_cmds[i][0] << shell_cmds[i][1] << "\t" << + shell_cmds[i][2] << "\n"; + } + + static void + showCurrentOp(Context* cx, Formatter &aOut) + { + ICodeModule *iCode = cx->getICode(); + JSValues ®isters = cx->getRegisters(); + InstructionIterator pc = cx->getPC(); + + printFormat(aOut, "trace [%02u:%04u]: ", + iCode->mID, (pc - iCode->its_iCode->begin())); + Instruction* i = *pc; + stdOut << *i; + if (i->op() != BRANCH && i->count() > 0) { + aOut << " ["; + i->printOperands(stdOut, registers); + aOut << "]\n"; + } else { + aOut << '\n'; + } + } + + void + Shell::listen(Context* cx, InterpretStage stage) + { + + if (mTraceFlag) + showCurrentOp (cx, mOut); + + if (!(mStopMask & stage)) + return; + + static String lastLine (widenCString("help\n")); + String line; + + do { + ICodeModule *iCode = cx->getICode(); + InstructionIterator pc = cx->getPC(); + + stdOut << "jsd [pc:"; + printFormat (stdOut, "%04X", (pc - iCode->its_iCode->begin())); + stdOut << "]> "; + + getline(cin, line); + if (line.size() == 0) + line = lastLine; + else + { + line.append(widenCString("\n")); + lastLine = line; + } + + } while (doCommand(cx, line)); + } + + + /** + * lex and execute the debugger command in |source|, return true if the + * command does not require the script being debugged to continue (eg, ask + * for more debugger input.) + */ + bool + Shell::doCommand (Interpreter::Context *cx, const String &source) + { + Lexer lex (mWorld, source, widenCString("debugger console"), 0); + const String *cmd; + uint32 match; + bool rv = true; + + const Token &t = lex.get(true); + + if (t.hasKind(Token::identifier)) + cmd = &(t.getIdentifier()); + else + { + mErr << "you idiot.\n"; + return true; + } + + match = matchElement (*cmd, shell_cmds, (size_t)COMMAND_COUNT); + + if (match <= (uint32)COMMAND_COUNT) + switch ((ShellCommand)match) { case COMMAND_COUNT: mErr << "Unknown command '" << *cmd << "'.\n"; break; - - case ASSEMBLE: + + case AMBIGUOUS: + case AMBIGUOUS2: + mErr << "I pity the foo.\n"; + break; + + case CONTINUE: + mStopMask &= (IS_ALL ^ IS_STEP); + rv = false; + break; + case DISSASSEMBLE: - case STEP: + mOut << *cx->getICode(); + break; + + case HELP: + showHelp (mOut); + break; + case PRINT: + doPrint (cx, lex); + break; + + case STEP: + mStopMask |= IS_STEP; + rv = false; + break; + + case LET: + doSetVariable (lex); + break; + default: - mErr << "Input '" << *cmd << "' matched command '" << - shell_cmd_names[match] << "'.\n"; + mErr << "Input '" << *cmd << "' matched unimplemented " << + "command '" << shell_cmds[match][0] << "'.\n"; break; } else mErr << "Ambiguous command '" << *cmd << "', " << - (ambig_matches + 1) << " similar commands.\n"; + (match - (uint32)COMMAND_COUNT + 1) << " similar commands.\n"; - return (ambig_matches == 0); - + return rv; } + void + Shell::doSetVariable (Lexer &lex) + { + uint32 match; + const String *varname; + const Token *t = &(lex.get(true)); + + if (t->hasKind(Token::identifier)) + varname = &(t->getIdentifier()); + else + { + mErr << "invalid variable name.\n"; + return; + } + + match = matchElement (*varname, shell_vars, (size_t)VARIABLE_COUNT); + + if (match <= (uint32)VARIABLE_COUNT) + switch ((ShellVariable)match) + { + case VARIABLE_COUNT: + mErr << "Unknown variable '" << *varname << "'.\n"; + break; + + case TRACE: + t = &(lex.get(true)); + if (t->hasKind(Token::assignment)) + t = &(lex.get(true)); /* optional = */ + + if (t->hasKind(Token::True)) + mTraceFlag = true; + else if (t->hasKind(Token::False)) + mTraceFlag = false; + else + goto badval; + break; + + default: + mErr << "Variable '" << *varname << + "' matched unimplemented variable '" << + shell_vars[match][0] << "'.\n"; + } + else + mErr << "Ambiguous variable '" << *varname << "', " << + (match - (uint32)COMMAND_COUNT + 1) << " similar variables.\n"; + + return; + + badval: + mErr << "Invalid value for variable '" << + shell_vars[(ShellVariable)match][0] << "'\n"; + + + } + + void + Shell::doPrint (Context *cx, Lexer &lex) + { + const Token *t = &(lex.get(true)); + + if (!(t->hasKind(Token::identifier))) + { + mErr << "Invalid register name.\n"; + return; + } + +/* + const StringAtom *name = &(t->getIdentifier()); + + VariableMap::iterator i = ((cx->getICode())->itsVariables)->find(*name); +// if (i) + mOut << (*i).first << " = " << (*i).second << "\n"; +// else +// mOut << "No " << *name << " defined.\n"; + +*/ + } + + + + } /* namespace Debugger */ } /* namespace JavaScript */ diff --git a/mozilla/js2/src/debugger.h b/mozilla/js2/src/debugger.h index a327ba76956..00d3f3bec92 100644 --- a/mozilla/js2/src/debugger.h +++ b/mozilla/js2/src/debugger.h @@ -41,38 +41,20 @@ 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); + using namespace Interpreter; class Breakpoint { + public: /* representation of a breakpoint */ void set(); void clear(); bool getState(); InstructionIterator getPC(); - }; - + }; + class ICodeDebugger { public: - /** - * set a debugger callback, returning the old one - */ - static debuggerCallback * setCallback (debuggerCallback *aCallback); - /** * install an icdebugger on a context */ @@ -128,7 +110,52 @@ namespace Debugger { }; /* class ICodeDebugger */ -#endif /* 0 */ + class Shell : public Context::Listener { + public: + Shell (World &aWorld, istream &aIn, Formatter &aOut, Formatter &aErr) : + mWorld(aWorld), mIn(aIn), mOut(aOut), mErr(aErr), + mStopMask(IS_ALL), mTraceFlag(true) + { + mDebugger = new ICodeDebugger(); + } + + ~Shell () + { + delete mDebugger; + } + + void listen(Context *context, InterpretStage stage); + + /** + * install on a context + */ + bool attachToContext (Context *aContext) + { + aContext->addListener (this); + return true; + } + + /** + * detach an icdebugger from a context + */ + bool detachFromContext (Context *aContext) + { + aContext->removeListener (this); + return true; + } + + private: + bool doCommand (Context *context, const String &aSource); + void doSetVariable (Lexer &lex); + void doPrint (Context *cx, Lexer &lex); + + World &mWorld; + istream &mIn; + Formatter &mOut, &mErr; + ICodeDebugger *mDebugger; + uint32 mStopMask; + bool mTraceFlag; + }; } /* namespace Debugger */ } /* namespace JavaScript */ diff --git a/mozilla/js2/src/icodegenerator.cpp b/mozilla/js2/src/icodegenerator.cpp index abff0823610..6fb12efcd41 100644 --- a/mozilla/js2/src/icodegenerator.cpp +++ b/mozilla/js2/src/icodegenerator.cpp @@ -46,12 +46,18 @@ namespace ICG { using namespace VM; + uint32 ICodeModule::sMaxID = 0; Formatter& operator<<(Formatter &f, ICodeGenerator &i) { return i.print(f); } - + + Formatter& operator<<(Formatter &f, ICodeModule &i) + { + return i.print(f); + } + // // ICodeGenerator // @@ -85,7 +91,7 @@ namespace ICG { ICodeModule *ICodeGenerator::complete() { ASSERT(stitcher.empty()); - ASSERT(labelSet == NULL); + //ASSERT(labelSet == NULL); #ifdef DEBUG for (LabelList::iterator i = labels.begin(); i != labels.end(); i++) { @@ -785,27 +791,17 @@ namespace ICG { Formatter& ICodeGenerator::print(Formatter& f) { - f << "ICG! " << (uint32)iCode->size() << "\n"; - for (InstructionIterator i = iCode->begin(); - i != iCode->end(); i++) { - bool isLabel = false; - - for (LabelList::iterator k = labels.begin(); - k != labels.end(); k++) - if ((ptrdiff_t)(*k)->mOffset == (i - iCode->begin())) { - f << "#" << (uint32)(i - iCode->begin()) << "\t"; - isLabel = true; - break; - } - - if (!isLabel) - f << "\t"; - - f << **i << "\n"; - } - + f << "ICG! " << (uint32)iCode->size() << "\n" << *iCode; return f; } + + Formatter& ICodeModule::print(Formatter& f) + { + f << "ICM! " << (uint32)its_iCode->size() << "\n" << *its_iCode; + return f; + } + + } // namespace ICG diff --git a/mozilla/js2/src/icodegenerator.h b/mozilla/js2/src/icodegenerator.h index 4134eb09c87..e73df07bca2 100644 --- a/mozilla/js2/src/icodegenerator.h +++ b/mozilla/js2/src/icodegenerator.h @@ -85,21 +85,25 @@ namespace ICG { class ICodeModule { public: - ICodeModule(InstructionStream *iCode, - VariableList *variables, - uint32 maxRegister, - uint32 maxParameter) : - its_iCode(iCode), - itsVariables(variables), - itsParameterCount(maxParameter), - itsMaxRegister(maxRegister) { } + ICodeModule(InstructionStream *iCode, VariableList *variables, + uint32 maxRegister, uint32 maxParameter) : + its_iCode(iCode), itsVariables(variables), + itsParameterCount(maxParameter), itsMaxRegister(maxRegister), + mID(++sMaxID) { } + + Formatter& print(Formatter& f); InstructionStream *its_iCode; VariableList *itsVariables; - uint32 itsMaxRegister; - uint32 itsParameterCount; + uint32 itsParameterCount; + uint32 itsMaxRegister; + uint32 mID; + static uint32 sMaxID; + }; - + + Formatter& operator<<(Formatter &f, ICodeModule &i); + /****************************************************************/ // An ICodeGenerator provides the interface between the parser and the diff --git a/mozilla/js2/src/interpreter.cpp b/mozilla/js2/src/interpreter.cpp index d3165e7e54e..eea00701b83 100644 --- a/mozilla/js2/src/interpreter.cpp +++ b/mozilla/js2/src/interpreter.cpp @@ -151,7 +151,7 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args) // tell any listeners about the current execution state. // XXX should only do this if we're single stepping/tracing. if (mListeners.size()) - broadcast(STEP); + broadcast(IS_STEP); Instruction* instruction = *mPC; switch (instruction->op()) { diff --git a/mozilla/js2/src/interpreter.h b/mozilla/js2/src/interpreter.h index 92567f5dd26..5eed215a7be 100644 --- a/mozilla/js2/src/interpreter.h +++ b/mozilla/js2/src/interpreter.h @@ -35,9 +35,11 @@ namespace Interpreter { struct Activation; enum InterpretStage { - STEP, - CATCH, - TRAP + IS_NONE = 0, + IS_STEP = 1, + IS_THROW = 2, + IS_TRAP = 4, + IS_ALL = 0xffff }; struct Linkage; diff --git a/mozilla/js2/src/vmtypes.cpp b/mozilla/js2/src/vmtypes.cpp index bf888a17452..6d49b5d3a37 100644 --- a/mozilla/js2/src/vmtypes.cpp +++ b/mozilla/js2/src/vmtypes.cpp @@ -76,6 +76,37 @@ namespace VM { return f; } + Formatter& operator<< (Formatter &f, InstructionStream &is) + { + + for (InstructionIterator i = is.begin(); + i != is.end(); i++) { + /* + bool isLabel = false; + + for (LabelList::iterator k = labels.begin(); + k != labels.end(); k++) + if ((ptrdiff_t)(*k)->mOffset == (i - is.begin())) { + f << "#" << (uint32)(i - is.begin()) << "\t"; + isLabel = true; + break; + } + + if (!isLabel) + f << "\t"; + + f << **i << "\n"; + */ + + printFormat(stdOut, "%04u", (uint32)(i - is.begin())); + f << ": " << **i << "\n"; + + } + + + return f; + } + } /* namespace VM */ } /* namespace JavaScript */ diff --git a/mozilla/js2/src/vmtypes.h b/mozilla/js2/src/vmtypes.h index 97cc9bc6d04..b98ffe9a0cc 100644 --- a/mozilla/js2/src/vmtypes.h +++ b/mozilla/js2/src/vmtypes.h @@ -176,6 +176,7 @@ namespace VM { typedef std::vector RegisterList; typedef std::vector InstructionStream; typedef InstructionStream::iterator InstructionIterator; + typedef std::map > VariableMap; /** * Helper to print Call operands. @@ -192,6 +193,7 @@ namespace VM { Formatter& operator<< (Formatter& f, Instruction& i); Formatter& operator<< (Formatter& f, RegisterList& rl); Formatter& operator<< (Formatter& f, const ArgList& al); + Formatter& operator<< (Formatter& f, InstructionStream& is); /********************************************************************/ @@ -325,7 +327,6 @@ namespace VM { /********************************************************************/ /* Specific opcodes */ - class Add : public Arithmetic { public: /* dest, source1, source2 */ @@ -514,11 +515,11 @@ namespace VM { } }; - class GetProp : public Instruction_3 { + class GetProp : public Instruction_3 { public: /* dest, object, prop name */ - GetProp (Register aOp1, Register aOp2, StringAtom* aOp3) : - Instruction_3 + GetProp (Register aOp1, Register aOp2, const StringAtom* aOp3) : + Instruction_3 (GET_PROP, aOp1, aOp2, aOp3) {}; virtual Formatter& print(Formatter& f) { f << opcodeNames[GET_PROP] << "\t" << "R" << mOp1 << ", " << "R" << mOp2 << ", " << "'" << *mOp3 << "'"; @@ -561,11 +562,11 @@ namespace VM { } }; - class LoadName : public Instruction_2 { + class LoadName : public Instruction_2 { public: /* dest, name */ - LoadName (Register aOp1, StringAtom* aOp2) : - Instruction_2 + LoadName (Register aOp1, const StringAtom* aOp2) : + Instruction_2 (LOAD_NAME, aOp1, aOp2) {}; virtual Formatter& print(Formatter& f) { f << opcodeNames[LOAD_NAME] << "\t" << "R" << mOp1 << ", " << "'" << *mOp2 << "'"; @@ -711,11 +712,11 @@ namespace VM { } }; - class SaveName : public Instruction_2 { + class SaveName : public Instruction_2 { public: /* name, source */ - SaveName (StringAtom* aOp1, Register aOp2) : - Instruction_2 + SaveName (const StringAtom* aOp1, Register aOp2) : + Instruction_2 (SAVE_NAME, aOp1, aOp2) {}; virtual Formatter& print(Formatter& f) { f << opcodeNames[SAVE_NAME] << "\t" << "'" << *mOp1 << "'" << ", " << "R" << mOp2; @@ -743,11 +744,11 @@ namespace VM { } }; - class SetProp : public Instruction_3 { + class SetProp : public Instruction_3 { public: /* object, name, source */ - SetProp (Register aOp1, StringAtom* aOp2, Register aOp3) : - Instruction_3 + SetProp (Register aOp1, const StringAtom* aOp2, Register aOp3) : + Instruction_3 (SET_PROP, aOp1, aOp2, aOp3) {}; virtual Formatter& print(Formatter& f) { f << opcodeNames[SET_PROP] << "\t" << "R" << mOp1 << ", " << "'" << *mOp2 << "'" << ", " << "R" << mOp3; diff --git a/mozilla/js2/tests/cpp/js2_shell.cpp b/mozilla/js2/tests/cpp/js2_shell.cpp index afa424e2fbb..6b9e6fb6e71 100644 --- a/mozilla/js2/tests/cpp/js2_shell.cpp +++ b/mozilla/js2/tests/cpp/js2_shell.cpp @@ -81,7 +81,8 @@ static bool promptLine(LineReader &inReader, string &s, JavaScript::World world; -JavaScript::Debugger::Shell jsd(world, JavaScript::stdOut, JavaScript::stdOut); +JavaScript::Debugger::Shell jsd(world, cin, JavaScript::stdOut, + JavaScript::stdOut); const bool showTokens = true; static void readEvalPrint(FILE *in, World &world) @@ -127,33 +128,6 @@ static void readEvalPrint(FILE *in, World &world) } -#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. */ @@ -167,7 +141,9 @@ class Tracer : public Context::Listener { InstructionOffset offset = (pc - iCode->its_iCode->begin()); - printFormat(stdOut, "%04X: ", offset); + printFormat(stdOut, "trace [%02u:%04u]: ", + iCode->mID, offset); + Instruction* i = *pc; stdOut << *i; if (i->op() != BRANCH && i->count() > 0) { @@ -179,7 +155,6 @@ class Tracer : public Context::Listener { } } }; -#endif static void testICG(World &world) { @@ -310,8 +285,11 @@ static float64 testFunctionCall(World &world, float64 n) { JSScope glob; Context cx(world, &glob); + /* Tracer t; cx.addListener(&t); + */ + jsd.attachToContext(&cx); uint32 position = 0; //StringAtom& global = world.identifiers[widenCString("global")]; diff --git a/mozilla/js2/tools/gencode.pl b/mozilla/js2/tools/gencode.pl index 0407fc16c3a..3623ff3dc30 100644 --- a/mozilla/js2/tools/gencode.pl +++ b/mozilla/js2/tools/gencode.pl @@ -82,13 +82,13 @@ $ops{"LOAD_NAME"} = { super => "Instruction_2", rem => "dest, name", - params => [ ("Register", "StringAtom*" ) ] + params => [ ("Register", "const StringAtom*" ) ] }; $ops{"SAVE_NAME"} = { super => "Instruction_2", rem => "name, source", - params => [ ("StringAtom*", "Register") ] + params => [ ("const StringAtom*", "Register") ] }; $ops{"NEW_OBJECT"} = { @@ -106,13 +106,13 @@ $ops{"GET_PROP"} = { super => "Instruction_3", rem => "dest, object, prop name", - params => [ ("Register", "Register", "StringAtom*") ] + params => [ ("Register", "Register", "const StringAtom*") ] }; $ops{"SET_PROP"} = { super => "Instruction_3", rem => "object, name, source", - params => [ ("Register", "StringAtom*", "Register") ] + params => [ ("Register", "const StringAtom*", "Register") ] }; $ops{"GET_ELEMENT"} = { @@ -392,7 +392,7 @@ sub get_print_body { push (@oplist, "\"R\" << mOp$op"); } elsif ($type eq "Label*") { push (@oplist, "\"Offset \" << mOp$op->mOffset"); - } elsif ($type eq "StringAtom*") { + } elsif ($type =~ /StringAtom/) { push (@oplist, "\"'\" << *mOp$op << \"'\""); } else { push (@oplist, "mOp$op");