much debugger spankage; parse and execute step, continue, set, and print (kind-of) commands.

push implementation details of ICodeGenerator::print() into InstructionStream so they can be shared with ICodeModule.

copy variableList from ICodeGenerator to ICodeModule.

s/ScringAtom/const StringAtom/ in gencode.pl, regenerate vmtypes.h


git-svn-id: svn://10.0.0.236/trunk@68276 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
rginda%netscape.com
2000-05-04 22:42:49 +00:00
parent 8f0f85459b
commit 5f3e8f7af0
20 changed files with 820 additions and 304 deletions

View File

@@ -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 &registers = 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 */

View File

@@ -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 */

View File

@@ -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

View File

@@ -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

View File

@@ -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()) {

View File

@@ -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;

View File

@@ -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")];

View File

@@ -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");

View File

@@ -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 */

View File

@@ -176,6 +176,7 @@ namespace VM {
typedef std::vector<Register> RegisterList;
typedef std::vector<Instruction *> InstructionStream;
typedef InstructionStream::iterator InstructionIterator;
typedef std::map<String, Register, std::less<String> > 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<Register, Register, StringAtom*> {
class GetProp : public Instruction_3<Register, Register, const StringAtom*> {
public:
/* dest, object, prop name */
GetProp (Register aOp1, Register aOp2, StringAtom* aOp3) :
Instruction_3<Register, Register, StringAtom*>
GetProp (Register aOp1, Register aOp2, const StringAtom* aOp3) :
Instruction_3<Register, Register, const StringAtom*>
(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<Register, StringAtom*> {
class LoadName : public Instruction_2<Register, const StringAtom*> {
public:
/* dest, name */
LoadName (Register aOp1, StringAtom* aOp2) :
Instruction_2<Register, StringAtom*>
LoadName (Register aOp1, const StringAtom* aOp2) :
Instruction_2<Register, const StringAtom*>
(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<StringAtom*, Register> {
class SaveName : public Instruction_2<const StringAtom*, Register> {
public:
/* name, source */
SaveName (StringAtom* aOp1, Register aOp2) :
Instruction_2<StringAtom*, Register>
SaveName (const StringAtom* aOp1, Register aOp2) :
Instruction_2<const StringAtom*, Register>
(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<Register, StringAtom*, Register> {
class SetProp : public Instruction_3<Register, const StringAtom*, Register> {
public:
/* object, name, source */
SetProp (Register aOp1, StringAtom* aOp2, Register aOp3) :
Instruction_3<Register, StringAtom*, Register>
SetProp (Register aOp1, const StringAtom* aOp2, Register aOp3) :
Instruction_3<Register, const StringAtom*, Register>
(SET_PROP, aOp1, aOp2, aOp3) {};
virtual Formatter& print(Formatter& f) {
f << opcodeNames[SET_PROP] << "\t" << "R" << mOp1 << ", " << "'" << *mOp2 << "'" << ", " << "R" << mOp3;

View File

@@ -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 &registers = 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 */

View File

@@ -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 */

View File

@@ -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

View File

@@ -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

View File

@@ -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()) {

View File

@@ -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;

View File

@@ -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 */

View File

@@ -176,6 +176,7 @@ namespace VM {
typedef std::vector<Register> RegisterList;
typedef std::vector<Instruction *> InstructionStream;
typedef InstructionStream::iterator InstructionIterator;
typedef std::map<String, Register, std::less<String> > 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<Register, Register, StringAtom*> {
class GetProp : public Instruction_3<Register, Register, const StringAtom*> {
public:
/* dest, object, prop name */
GetProp (Register aOp1, Register aOp2, StringAtom* aOp3) :
Instruction_3<Register, Register, StringAtom*>
GetProp (Register aOp1, Register aOp2, const StringAtom* aOp3) :
Instruction_3<Register, Register, const StringAtom*>
(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<Register, StringAtom*> {
class LoadName : public Instruction_2<Register, const StringAtom*> {
public:
/* dest, name */
LoadName (Register aOp1, StringAtom* aOp2) :
Instruction_2<Register, StringAtom*>
LoadName (Register aOp1, const StringAtom* aOp2) :
Instruction_2<Register, const StringAtom*>
(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<StringAtom*, Register> {
class SaveName : public Instruction_2<const StringAtom*, Register> {
public:
/* name, source */
SaveName (StringAtom* aOp1, Register aOp2) :
Instruction_2<StringAtom*, Register>
SaveName (const StringAtom* aOp1, Register aOp2) :
Instruction_2<const StringAtom*, Register>
(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<Register, StringAtom*, Register> {
class SetProp : public Instruction_3<Register, const StringAtom*, Register> {
public:
/* object, name, source */
SetProp (Register aOp1, StringAtom* aOp2, Register aOp3) :
Instruction_3<Register, StringAtom*, Register>
SetProp (Register aOp1, const StringAtom* aOp2, Register aOp3) :
Instruction_3<Register, const StringAtom*, Register>
(SET_PROP, aOp1, aOp2, aOp3) {};
virtual Formatter& print(Formatter& f) {
f << opcodeNames[SET_PROP] << "\t" << "R" << mOp1 << ", " << "'" << *mOp2 << "'" << ", " << "R" << mOp3;

View File

@@ -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")];

View File

@@ -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");