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