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:
@@ -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 */
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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")];
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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")];
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user