зеркало из https://github.com/mozilla/pjs.git
Moved genExpr to ICodeGenerator - added most expression ops.
This commit is contained in:
Родитель
ad5c33b8e4
Коммит
4ec5cba5d9
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -131,6 +131,8 @@ namespace ICG {
|
|||
Register switchRegister; // register containing switch control value for most
|
||||
// recently in progress switch statement.
|
||||
VariableList *variableList; // name|register pair for each variable
|
||||
|
||||
World *mWorld; // used to register strings
|
||||
|
||||
|
||||
|
||||
|
@ -158,6 +160,8 @@ namespace ICG {
|
|||
void branch(Label *label);
|
||||
void branchConditional(Label *label, Register condition);
|
||||
void branchNotConditional(Label *label, Register condition);
|
||||
void branchTrue(Label *label, Register condition);
|
||||
void branchFalse(Label *label, Register condition);
|
||||
|
||||
void beginTry(Label *catchLabel, Label *finallyLabel)
|
||||
{ iCode->push_back(new Tryin(catchLabel, finallyLabel)); }
|
||||
|
@ -167,6 +171,8 @@ namespace ICG {
|
|||
void resetStatement()
|
||||
{ if (labelSet) { delete labelSet; labelSet = NULL; } resetTopRegister(); }
|
||||
|
||||
ICodeOp mapExprNodeToICodeOp(ExprNode::Kind kind);
|
||||
|
||||
public:
|
||||
ICodeGenerator(World *world = NULL,
|
||||
bool hasTryStatement = false,
|
||||
|
@ -182,14 +188,18 @@ namespace ICG {
|
|||
|
||||
ICodeModule *complete();
|
||||
|
||||
Register ICodeGenerator::genExpr(ExprNode *p, bool needBoolValueInBranch = false,
|
||||
Label *trueBranch = NULL,
|
||||
Label *falseBranch = NULL);
|
||||
|
||||
|
||||
Register allocateVariable(const StringAtom& name)
|
||||
{ Register result = getRegister(); (*variableList)[name] = result;
|
||||
registerBase = topRegister; return result; }
|
||||
|
||||
Register findVariable(const StringAtom& name)
|
||||
{ VariableList::iterator i = variableList->find(name);
|
||||
// What's map? // ASSERT(i != map.end());
|
||||
return (*i).second; }
|
||||
ASSERT(i != variableList->end()); return (*i).second; }
|
||||
|
||||
Register allocateParameter(const StringAtom& name)
|
||||
{ parameterCount++; return allocateVariable(name); }
|
||||
|
@ -203,9 +213,11 @@ namespace ICG {
|
|||
|
||||
void move(Register destination, Register source);
|
||||
void complement(Register destination, Register source);
|
||||
Register test(Register source);
|
||||
|
||||
Register compare(ICodeOp op, Register source1, Register source2);
|
||||
|
||||
Register loadValue(JSValue value);
|
||||
Register loadImmediate(double value);
|
||||
Register loadString(String &value);
|
||||
|
||||
|
@ -224,7 +236,9 @@ namespace ICG {
|
|||
|
||||
Register getElement(Register base, Register index);
|
||||
void setElement(Register base, Register index, Register value);
|
||||
|
||||
Register elementInc(Register base, Register index);
|
||||
Register elementDec(Register base, Register index);
|
||||
|
||||
Register getRegisterBase() { return topRegister; }
|
||||
InstructionStream *get_iCode() { return iCode; }
|
||||
StatementLabels *getStatementLabels() { return labelSet; labelSet = NULL; }
|
||||
|
@ -285,7 +299,7 @@ namespace ICG {
|
|||
|
||||
void beginLabelStatement(uint32 /* pos */, const StringAtom &label)
|
||||
{ labelSet->push_back(&label); }
|
||||
void endLabelStatement() { labelSet->pop_back(); }
|
||||
void endLabelStatement() { if (labelSet) labelSet->pop_back(); }
|
||||
|
||||
void continueStatement(uint32 pos);
|
||||
void breakStatement(uint32 pos);
|
||||
|
|
|
@ -277,6 +277,21 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args)
|
|||
}
|
||||
}
|
||||
break;
|
||||
/*
|
||||
versions below are not right, there is no 'array' type really, the index operation
|
||||
turns into a get_property call like this, only we need to be using JSString throughout.
|
||||
case GET_ELEMENT:
|
||||
{
|
||||
GetElement* ge = static_cast<GetElement*>(instruction);
|
||||
JSValue& base = (*registers)[src1(ge)];
|
||||
JSValue index = (*registers)[src2(ge)].toString();
|
||||
if (base.tag == JSValue::object_tag) {
|
||||
JSObject* object = base.object;
|
||||
(*registers)[dst(ge)] = object->getProperty(*index.string);
|
||||
}
|
||||
}
|
||||
break;
|
||||
*/
|
||||
case GET_ELEMENT:
|
||||
{
|
||||
GetElement* ge = static_cast<GetElement*>(instruction);
|
||||
|
@ -297,6 +312,7 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args)
|
|||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case LOAD_IMMEDIATE:
|
||||
{
|
||||
LoadImmediate* li = static_cast<LoadImmediate*>(instruction);
|
||||
|
@ -309,6 +325,12 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args)
|
|||
(*registers)[dst(ls)] = JSValue(src1(ls));
|
||||
}
|
||||
break;
|
||||
case LOAD_VALUE:
|
||||
{
|
||||
LoadValue* lv = static_cast<LoadValue*>(instruction);
|
||||
(*registers)[dst(lv)] = src1(lv);
|
||||
}
|
||||
break;
|
||||
case BRANCH:
|
||||
{
|
||||
GenericBranch* bra =
|
||||
|
@ -357,6 +379,28 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args)
|
|||
}
|
||||
}
|
||||
break;
|
||||
case BRANCH_TRUE:
|
||||
{
|
||||
GenericBranch* bc =
|
||||
static_cast<GenericBranch*>(instruction);
|
||||
ASSERT((*registers)[src1(bc)].isBoolean());
|
||||
if ((*registers)[src1(bc)].boolean) {
|
||||
mPC = begin_pc + ofs(bc);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BRANCH_FALSE:
|
||||
{
|
||||
GenericBranch* bc =
|
||||
static_cast<GenericBranch*>(instruction);
|
||||
ASSERT((*registers)[src1(bc)].isBoolean());
|
||||
if (!(*registers)[src1(bc)].boolean) {
|
||||
mPC = begin_pc + ofs(bc);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BRANCH_GE:
|
||||
{
|
||||
GenericBranch* bc =
|
||||
|
@ -377,6 +421,74 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args)
|
|||
}
|
||||
}
|
||||
break;
|
||||
case SHIFTLEFT:
|
||||
{
|
||||
Arithmetic* shl = static_cast<Arithmetic*>(instruction);
|
||||
JSValue& dest = (*registers)[dst(shl)];
|
||||
JSValue& r1 = (*registers)[src1(shl)];
|
||||
JSValue& r2 = (*registers)[src2(shl)];
|
||||
JSValue num1(r1.toInt32());
|
||||
JSValue num2(r2.toUInt32());
|
||||
dest = num1.i32 << (num2.u32 & 0x1F);
|
||||
}
|
||||
break;
|
||||
case SHIFTRIGHT:
|
||||
{
|
||||
Arithmetic* shr = static_cast<Arithmetic*>(instruction);
|
||||
JSValue& dest = (*registers)[dst(shr)];
|
||||
JSValue& r1 = (*registers)[src1(shr)];
|
||||
JSValue& r2 = (*registers)[src2(shr)];
|
||||
JSValue num1(r1.toInt32());
|
||||
JSValue num2(r2.toUInt32());
|
||||
dest = num1.i32 >> (num2.u32 & 0x1F);
|
||||
}
|
||||
break;
|
||||
case USHIFTRIGHT:
|
||||
{
|
||||
Arithmetic* ushr = static_cast<Arithmetic*>(instruction);
|
||||
JSValue& dest = (*registers)[dst(ushr)];
|
||||
JSValue& r1 = (*registers)[src1(ushr)];
|
||||
JSValue& r2 = (*registers)[src2(ushr)];
|
||||
JSValue num1(r1.toUInt32());
|
||||
JSValue num2(r2.toUInt32());
|
||||
dest = num1.u32 >> (num2.u32 & 0x1F);
|
||||
}
|
||||
break;
|
||||
|
||||
case AND:
|
||||
{
|
||||
Arithmetic* shr = static_cast<Arithmetic*>(instruction);
|
||||
JSValue& dest = (*registers)[dst(shr)];
|
||||
JSValue& r1 = (*registers)[src1(shr)];
|
||||
JSValue& r2 = (*registers)[src2(shr)];
|
||||
JSValue num1(r1.toInt32());
|
||||
JSValue num2(r2.toInt32());
|
||||
dest = num1.i32 & num2.i32;
|
||||
}
|
||||
break;
|
||||
case OR:
|
||||
{
|
||||
Arithmetic* shr = static_cast<Arithmetic*>(instruction);
|
||||
JSValue& dest = (*registers)[dst(shr)];
|
||||
JSValue& r1 = (*registers)[src1(shr)];
|
||||
JSValue& r2 = (*registers)[src2(shr)];
|
||||
JSValue num1(r1.toInt32());
|
||||
JSValue num2(r2.toInt32());
|
||||
dest = num1.i32 | num2.i32;
|
||||
}
|
||||
break;
|
||||
case XOR:
|
||||
{
|
||||
Arithmetic* shr = static_cast<Arithmetic*>(instruction);
|
||||
JSValue& dest = (*registers)[dst(shr)];
|
||||
JSValue& r1 = (*registers)[src1(shr)];
|
||||
JSValue& r2 = (*registers)[src2(shr)];
|
||||
JSValue num1(r1.toInt32());
|
||||
JSValue num2(r2.toInt32());
|
||||
dest = num1.i32 ^ num2.i32;
|
||||
}
|
||||
break;
|
||||
|
||||
case ADD:
|
||||
{
|
||||
// could get clever here with Functional forms.
|
||||
|
@ -462,11 +574,10 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args)
|
|||
JSValue r = mGlobal->getVariable(*src1(nx)).toNumber();
|
||||
dest = r;
|
||||
r.f64 += val3(nx);
|
||||
mGlobal->setVariable(*src1(nx), dest);
|
||||
mGlobal->setVariable(*src1(nx), r);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case COMPARE_LT:
|
||||
case COMPARE_LE:
|
||||
case COMPARE_EQ:
|
||||
|
@ -475,11 +586,44 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args)
|
|||
case COMPARE_GE:
|
||||
{
|
||||
Arithmetic* cmp = static_cast<Arithmetic*>(instruction);
|
||||
float64 diff =
|
||||
((*registers)[src1(cmp)].f64 -
|
||||
(*registers)[src2(cmp)].f64);
|
||||
(*registers)[dst(cmp)] =
|
||||
JSValue(int32(diff == 0.0 ? 0 : (diff > 0.0 ? 1 : -1)));
|
||||
JSValue& dest = (*registers)[dst(cmp)];
|
||||
JSValue lv = (*registers)[src1(cmp)].toPrimitive(JSValue::Number);
|
||||
JSValue rv = (*registers)[src2(cmp)].toPrimitive(JSValue::Number);
|
||||
if (lv.isString() && rv.isString()) {
|
||||
// XXX FIXME urgh, call w_strcmp ??? on a JSString ???
|
||||
}
|
||||
else {
|
||||
lv = lv.toNumber();
|
||||
rv = rv.toNumber();
|
||||
if (lv.isNaN() || rv.isNaN())
|
||||
dest = JSValue();
|
||||
else {
|
||||
// FIXME, does this do the right thing for +/- infinity?
|
||||
switch (instruction->op()) {
|
||||
case COMPARE_LT:
|
||||
dest = JSValue(lv.f64 < rv.f64); break;
|
||||
case COMPARE_LE:
|
||||
dest = JSValue(lv.f64 <= rv.f64); break;
|
||||
case COMPARE_EQ:
|
||||
dest = JSValue(lv.f64 == rv.f64); break;
|
||||
case COMPARE_NE:
|
||||
dest = JSValue(lv.f64 != rv.f64); break;
|
||||
case COMPARE_GT:
|
||||
dest = JSValue(lv.f64 > rv.f64); break;
|
||||
case COMPARE_GE:
|
||||
dest = JSValue(lv.f64 >= rv.f64); break;
|
||||
}
|
||||
// float64 diff = lv.f64 - rv.f64;
|
||||
// dest = JSValue( (int32) (diff == 0.0 ? 0 : (diff > 0.0 ? 1 : -1)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
case TEST:
|
||||
{
|
||||
Test* tst = static_cast<Test*>(instruction);
|
||||
(*registers)[dst(tst)] = (*registers)[src1(tst)].toBoolean();
|
||||
}
|
||||
break;
|
||||
case NEGATE:
|
||||
|
@ -494,6 +638,13 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args)
|
|||
(*registers)[dst(pos)] = (*registers)[src1(pos)].toNumber();
|
||||
}
|
||||
break;
|
||||
case BITNOT:
|
||||
{
|
||||
Bitnot* bn = static_cast<Bitnot*>(instruction);
|
||||
(*registers)[dst(bn)] = JSValue(~(*registers)[src1(bn)].toInt32().i32);
|
||||
}
|
||||
break;
|
||||
/*
|
||||
case NOT:
|
||||
{
|
||||
Not* nt = static_cast<Not*>(instruction);
|
||||
|
@ -501,7 +652,7 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args)
|
|||
JSValue(int32(!(*registers)[src1(nt)].i32));
|
||||
}
|
||||
break;
|
||||
|
||||
*/
|
||||
case THROW:
|
||||
{
|
||||
Throw* thrw = static_cast<Throw*>(instruction);
|
||||
|
@ -610,7 +761,7 @@ void Context::addListener(Listener* listener)
|
|||
void Context::removeListener(Listener* listener)
|
||||
{
|
||||
ListenerIterator e = mListeners.end();
|
||||
ListenerIterator l = find(mListeners.begin(), e, listener);
|
||||
ListenerIterator l = std::find(mListeners.begin(), e, listener);
|
||||
if (l != e) mListeners.erase(l);
|
||||
}
|
||||
|
||||
|
|
|
@ -40,18 +40,54 @@ namespace JSTypes {
|
|||
|
||||
// using JavaScript::StringAtom;
|
||||
|
||||
// the canonical undefined value.
|
||||
// the canonical undefined value, etc.
|
||||
const JSValue kUndefinedValue;
|
||||
const JSValue kNaN(0.0, 0.0);
|
||||
const JSValue kNaN = JSValue(nan);
|
||||
const JSValue kTrue = JSValue(true);
|
||||
const JSValue kFalse = JSValue(false);
|
||||
|
||||
#ifdef IS_LITTLE_ENDIAN
|
||||
#define JSDOUBLE_HI32(x) (((uint32 *)&(x))[1])
|
||||
#define JSDOUBLE_LO32(x) (((uint32 *)&(x))[0])
|
||||
#else
|
||||
#define JSDOUBLE_HI32(x) (((uint32 *)&(x))[0])
|
||||
#define JSDOUBLE_LO32(x) (((uint32 *)&(x))[1])
|
||||
#endif
|
||||
|
||||
#define JSDOUBLE_HI32_SIGNBIT 0x80000000
|
||||
#define JSDOUBLE_HI32_EXPMASK 0x7ff00000
|
||||
#define JSDOUBLE_HI32_MANTMASK 0x000fffff
|
||||
|
||||
#define JSDOUBLE_IS_NaN(x) \
|
||||
((JSDOUBLE_HI32(x) & JSDOUBLE_HI32_EXPMASK) == JSDOUBLE_HI32_EXPMASK && \
|
||||
(JSDOUBLE_LO32(x) || (JSDOUBLE_HI32(x) & JSDOUBLE_HI32_MANTMASK)))
|
||||
|
||||
#define JSDOUBLE_IS_INFINITE(x) \
|
||||
((JSDOUBLE_HI32(x) & ~JSDOUBLE_HI32_SIGNBIT) == JSDOUBLE_HI32_EXPMASK && \
|
||||
!JSDOUBLE_LO32(x))
|
||||
|
||||
#define JSDOUBLE_IS_FINITE(x) \
|
||||
((JSDOUBLE_HI32(x) & JSDOUBLE_HI32_EXPMASK) != JSDOUBLE_HI32_EXPMASK)
|
||||
|
||||
#define JSDOUBLE_IS_NEGZERO(d) (JSDOUBLE_HI32(d) == JSDOUBLE_HI32_SIGNBIT && \
|
||||
JSDOUBLE_LO32(d) == 0)
|
||||
|
||||
|
||||
|
||||
JSValue::JSValue(float64 a, float64 b)
|
||||
bool JSValue::isNaN() const
|
||||
{
|
||||
f64 = a/b;
|
||||
tag = f64_tag;
|
||||
ASSERT(isNumber());
|
||||
switch (tag) {
|
||||
case i32_tag:
|
||||
case u32_tag:
|
||||
return false;
|
||||
case f64_tag:
|
||||
return JSDOUBLE_IS_NaN(f64);
|
||||
default:
|
||||
NOT_REACHED("Broken compiler?");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int JSValue::operator==(const JSValue& value) const
|
||||
{
|
||||
if (this->tag == value.tag) {
|
||||
|
@ -62,6 +98,7 @@ int JSValue::operator==(const JSValue& value) const
|
|||
CASE(i32); CASE(u32); CASE(f32);
|
||||
CASE(i64); CASE(u64); CASE(f64);
|
||||
CASE(object); CASE(array); CASE(function); CASE(string);
|
||||
CASE(boolean);
|
||||
#undef CASE
|
||||
// question: are all undefined values equal to one another?
|
||||
case undefined_tag: return 1;
|
||||
|
@ -76,6 +113,9 @@ Formatter& operator<<(Formatter& f, const JSValue& value)
|
|||
case JSValue::i32_tag:
|
||||
f << float64(value.i32);
|
||||
break;
|
||||
case JSValue::u32_tag:
|
||||
f << float64(value.u32);
|
||||
break;
|
||||
case JSValue::f64_tag:
|
||||
f << value.f64;
|
||||
break;
|
||||
|
@ -87,39 +127,80 @@ Formatter& operator<<(Formatter& f, const JSValue& value)
|
|||
case JSValue::string_tag:
|
||||
f << *value.string;
|
||||
break;
|
||||
default:
|
||||
case JSValue::boolean_tag:
|
||||
f << ((value.boolean) ? "true" : "false");
|
||||
break;
|
||||
case JSValue::undefined_tag:
|
||||
f << "undefined";
|
||||
break;
|
||||
default:
|
||||
NOT_REACHED("Bad tag");
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
JSValue JSValue::toPrimitive(ECMA_type hint) const
|
||||
{
|
||||
switch (tag) {
|
||||
case i32_tag:
|
||||
case u32_tag:
|
||||
case f64_tag:
|
||||
case string_tag:
|
||||
case boolean_tag:
|
||||
case undefined_tag:
|
||||
return *this;
|
||||
|
||||
case object_tag:
|
||||
case array_tag:
|
||||
case function_tag:
|
||||
if (hint == String) {
|
||||
// FIXME
|
||||
}
|
||||
else {
|
||||
// FIXME
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
NOT_REACHED("Bad tag");
|
||||
}
|
||||
return kUndefinedValue;
|
||||
}
|
||||
|
||||
|
||||
JSValue JSValue::valueToString(const JSValue& value) // can assume value is not a string
|
||||
{
|
||||
char *chrp;
|
||||
char buf[dtosStandardBufferSize];
|
||||
switch (value.tag) {
|
||||
case JSValue::i32_tag:
|
||||
case i32_tag:
|
||||
chrp = doubleToStr(buf, dtosStandardBufferSize, value.i32, dtosStandard, 0);
|
||||
break;
|
||||
case JSValue::f64_tag:
|
||||
case u32_tag:
|
||||
chrp = doubleToStr(buf, dtosStandardBufferSize, value.u32, dtosStandard, 0);
|
||||
break;
|
||||
case f64_tag:
|
||||
chrp = doubleToStr(buf, dtosStandardBufferSize, value.f64, dtosStandard, 0);
|
||||
break;
|
||||
case JSValue::object_tag:
|
||||
case object_tag:
|
||||
chrp = "object";
|
||||
break;
|
||||
case JSValue::array_tag:
|
||||
case array_tag:
|
||||
chrp = "array";
|
||||
break;
|
||||
case JSValue::function_tag:
|
||||
case function_tag:
|
||||
chrp = "function";
|
||||
break;
|
||||
case JSValue::string_tag:
|
||||
case string_tag:
|
||||
return value;
|
||||
default:
|
||||
case boolean_tag:
|
||||
chrp = (value.boolean) ? "true" : "false";
|
||||
break;
|
||||
case undefined_tag:
|
||||
chrp = "undefined";
|
||||
break;
|
||||
default:
|
||||
NOT_REACHED("Bad tag");
|
||||
}
|
||||
return JSValue(new JSString(chrp));
|
||||
}
|
||||
|
@ -127,25 +208,142 @@ JSValue JSValue::valueToString(const JSValue& value) // can assume value is not
|
|||
JSValue JSValue::valueToNumber(const JSValue& value) // can assume value is not a number
|
||||
{
|
||||
switch (value.tag) {
|
||||
case JSValue::i32_tag:
|
||||
case JSValue::f64_tag:
|
||||
case i32_tag:
|
||||
return JSValue((float64)value.i32);
|
||||
case u32_tag:
|
||||
return JSValue((float64)value.u32);
|
||||
case f64_tag:
|
||||
return value;
|
||||
case JSValue::string_tag:
|
||||
case string_tag:
|
||||
{
|
||||
JSString* str = value.string;
|
||||
const char16 *numEnd;
|
||||
double d = stringToDouble(str->begin(), str->end(), numEnd);
|
||||
return JSValue(d);
|
||||
}
|
||||
case JSValue::object_tag:
|
||||
case JSValue::array_tag:
|
||||
case JSValue::function_tag:
|
||||
break;
|
||||
default:
|
||||
case object_tag:
|
||||
case array_tag:
|
||||
case function_tag:
|
||||
// XXX more needed :
|
||||
// toNumber(toPrimitive(hint Number))
|
||||
return kUndefinedValue;
|
||||
case boolean_tag:
|
||||
return JSValue((value.boolean) ? 1.0 : 0.0);
|
||||
case undefined_tag:
|
||||
return kNaN;
|
||||
break;
|
||||
default:
|
||||
NOT_REACHED("Bad tag");
|
||||
return kUndefinedValue;
|
||||
}
|
||||
return kUndefinedValue;
|
||||
}
|
||||
|
||||
JSValue JSValue::valueToBoolean(const JSValue& value)
|
||||
{
|
||||
switch (value.tag) {
|
||||
case i32_tag:
|
||||
return JSValue(value.i32 != 0);
|
||||
case u32_tag:
|
||||
return JSValue(value.u32 != 0);
|
||||
case f64_tag:
|
||||
return JSValue(!(value.f64 == 0.0) || JSDOUBLE_IS_NaN(value.f64));
|
||||
case string_tag:
|
||||
return JSValue(value.string->length() != 0);
|
||||
case boolean_tag:
|
||||
return value;
|
||||
case object_tag:
|
||||
case array_tag:
|
||||
case function_tag:
|
||||
return kTrue;
|
||||
case undefined_tag:
|
||||
return kFalse;
|
||||
default:
|
||||
NOT_REACHED("Bad tag");
|
||||
return kUndefinedValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const double two32 = 4294967296.0;
|
||||
static const double two31 = 2147483648.0;
|
||||
|
||||
JSValue JSValue::valueToInt32(const JSValue& value)
|
||||
{
|
||||
double d;
|
||||
switch (value.tag) {
|
||||
case i32_tag:
|
||||
return value;
|
||||
case u32_tag:
|
||||
d = value.u32;
|
||||
break;
|
||||
case f64_tag:
|
||||
d = value.f64;
|
||||
break;
|
||||
case string_tag:
|
||||
{
|
||||
JSString* str = value.string;
|
||||
const char16 *numEnd;
|
||||
d = stringToDouble(str->begin(), str->end(), numEnd);
|
||||
}
|
||||
break;
|
||||
case boolean_tag:
|
||||
return JSValue((int32)((value.boolean) ? 1 : 0));
|
||||
case object_tag:
|
||||
case array_tag:
|
||||
case undefined_tag:
|
||||
// toNumber(toPrimitive(hint Number))
|
||||
return kUndefinedValue;
|
||||
default:
|
||||
NOT_REACHED("Bad tag");
|
||||
return kUndefinedValue;
|
||||
}
|
||||
if ((d == 0.0) || !JSDOUBLE_IS_FINITE(d) )
|
||||
return JSValue((int32)0);
|
||||
d = fmod(d, two32);
|
||||
d = (d >= 0) ? d : d + two32;
|
||||
if (d >= two31)
|
||||
return JSValue((int32)(d - two32));
|
||||
else
|
||||
return JSValue((int32)d);
|
||||
|
||||
}
|
||||
|
||||
JSValue JSValue::valueToUInt32(const JSValue& value)
|
||||
{
|
||||
double d;
|
||||
switch (value.tag) {
|
||||
case i32_tag:
|
||||
return JSValue((uint32)value.i32);
|
||||
case u32_tag:
|
||||
return value;
|
||||
case f64_tag:
|
||||
d = value.f64;
|
||||
break;
|
||||
case string_tag:
|
||||
{
|
||||
JSString* str = value.string;
|
||||
const char16 *numEnd;
|
||||
d = stringToDouble(str->begin(), str->end(), numEnd);
|
||||
}
|
||||
break;
|
||||
case boolean_tag:
|
||||
return JSValue((uint32)((value.boolean) ? 1 : 0));
|
||||
case object_tag:
|
||||
case array_tag:
|
||||
case undefined_tag:
|
||||
// toNumber(toPrimitive(hint Number))
|
||||
return kUndefinedValue;
|
||||
default:
|
||||
NOT_REACHED("Bad tag");
|
||||
return kUndefinedValue;
|
||||
}
|
||||
if ((d == 0.0) || !JSDOUBLE_IS_FINITE(d))
|
||||
return JSValue((uint32)0);
|
||||
bool neg = (d < 0);
|
||||
d = floor(neg ? -d : d);
|
||||
d = neg ? -d : d;
|
||||
d = fmod(d, two32);
|
||||
d = (d >= 0) ? d : d + two32;
|
||||
return JSValue((uint32)d);
|
||||
}
|
||||
|
||||
JSFunction::~JSFunction()
|
||||
|
|
|
@ -76,44 +76,66 @@ namespace JSTypes {
|
|||
JSArray* array;
|
||||
JSFunction *function;
|
||||
JSString *string;
|
||||
bool boolean;
|
||||
};
|
||||
|
||||
/* These are the ECMA types, for use in 'toPrimitive' calls */
|
||||
enum ECMA_type {
|
||||
Undefined, Null, Boolean, Number, Object, String,
|
||||
NoHint
|
||||
};
|
||||
|
||||
enum {
|
||||
i8_tag, u8_tag,
|
||||
i16_tag, u16_tag,
|
||||
i32_tag, u32_tag,
|
||||
i64_tag, u64_tag,
|
||||
f32_tag, f64_tag,
|
||||
object_tag, array_tag, function_tag, string_tag,
|
||||
object_tag, array_tag, function_tag, string_tag, boolean_tag,
|
||||
undefined_tag
|
||||
} tag;
|
||||
|
||||
JSValue() : f64(0.0), tag(undefined_tag) {}
|
||||
explicit JSValue(int32 i32) : i32(i32), tag(i32_tag) {}
|
||||
explicit JSValue(uint32 u32) : u32(u32), tag(u32_tag) {}
|
||||
explicit JSValue(float64 f64) : f64(f64), tag(f64_tag) {}
|
||||
explicit JSValue(JSObject* object) : object(object), tag(object_tag) {}
|
||||
explicit JSValue(JSArray* array) : array(array), tag(array_tag) {}
|
||||
explicit JSValue(JSFunction* function) : function(function), tag(function_tag) {}
|
||||
explicit JSValue(JSString* string) : string(string), tag(string_tag) {}
|
||||
explicit JSValue(bool boolean) : boolean(boolean), tag(boolean_tag) {}
|
||||
|
||||
JSValue(float64 a, float64 b);
|
||||
|
||||
int32& operator=(int32 i32) { return (tag = i32_tag, this->i32 = i32); }
|
||||
uint32& operator=(uint32 u32) { return (tag = u32_tag, this->u32 = u32); }
|
||||
float64& operator=(float64 f64) { return (tag = f64_tag, this->f64 = f64); }
|
||||
JSObject*& operator=(JSObject* object) { return (tag = object_tag, this->object = object); }
|
||||
JSArray*& operator=(JSArray* array) { return (tag = array_tag, this->array = array); }
|
||||
JSFunction*& operator=(JSFunction* function) { return (tag = function_tag, this->function = function); }
|
||||
JSString*& operator=(JSString* string) { return (tag = string_tag, this->string = string); }
|
||||
bool& operator=(bool boolean) { return (tag = boolean_tag, this->boolean = boolean); }
|
||||
|
||||
bool isString() const { return (tag == string_tag); }
|
||||
bool isNumber() const { return ((tag == f64_tag) || (tag == i32_tag)); }
|
||||
bool isBoolean() const { return (tag == boolean_tag); }
|
||||
bool isNumber() const { return (tag == f64_tag); }
|
||||
/* this is correct wrt ECMA, The i32 & u32 kinds
|
||||
will have to be converted to doubles anyway because
|
||||
we can't have overflow happening in generic arithmetic */
|
||||
bool isNaN() const;
|
||||
|
||||
JSValue toString() const { return (isString() ? *this : valueToString(*this)); }
|
||||
JSValue toNumber() const { return (isNumber() ? *this : valueToNumber(*this)); }
|
||||
JSValue toInt32() const { return ((tag == i32_tag) ? *this : valueToInt32(*this)); }
|
||||
JSValue toUInt32() const { return ((tag == u32_tag) ? *this : valueToUInt32(*this)); }
|
||||
JSValue toBoolean() const { return ((tag == boolean_tag) ? *this : valueToBoolean(*this)); }
|
||||
|
||||
JSValue toPrimitive(ECMA_type hint = NoHint) const;
|
||||
|
||||
|
||||
static JSValue valueToString(const JSValue& value);
|
||||
static JSValue valueToNumber(const JSValue& value);
|
||||
static JSValue valueToInt32(const JSValue& value);
|
||||
static JSValue valueToUInt32(const JSValue& value);
|
||||
static JSValue valueToBoolean(const JSValue& value);
|
||||
|
||||
int operator==(const JSValue& value) const;
|
||||
};
|
||||
|
@ -140,6 +162,8 @@ namespace JSTypes {
|
|||
|
||||
extern const JSValue kUndefinedValue;
|
||||
extern const JSValue kNaN;
|
||||
extern const JSValue kTrue;
|
||||
extern const JSValue kFalse;
|
||||
|
||||
/**
|
||||
* Basic behavior of all JS objects, mapping a name to a value,
|
||||
|
|
|
@ -107,6 +107,30 @@ namespace VM {
|
|||
return f;
|
||||
}
|
||||
|
||||
ICodeOp Instruction::getBranchOp()
|
||||
{
|
||||
// XXX FIXME, convert to table lookup or arithmetic after the dust settles
|
||||
switch (mOpcode) {
|
||||
case COMPARE_EQ:
|
||||
// return BRANCH_EQ;
|
||||
case COMPARE_NE:
|
||||
// return BRANCH_NE;
|
||||
case COMPARE_GE:
|
||||
// return BRANCH_GE;
|
||||
case COMPARE_GT:
|
||||
// return BRANCH_GT;
|
||||
case COMPARE_LE:
|
||||
// return BRANCH_LE;
|
||||
case COMPARE_LT:
|
||||
// return BRANCH_LT;
|
||||
case TEST:
|
||||
return BRANCH_TRUE;
|
||||
default:
|
||||
NOT_REACHED("Unexpected branch code");
|
||||
return NOP;
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace VM */
|
||||
} /* namespace JavaScript */
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
namespace JavaScript {
|
||||
namespace VM {
|
||||
|
||||
using JSTypes::JSValue;
|
||||
using JSTypes::JSValues;
|
||||
using JSTypes::JSString;
|
||||
|
||||
|
@ -51,11 +52,13 @@ namespace VM {
|
|||
BITNOT, /* dest, source */
|
||||
BRANCH, /* target label */
|
||||
BRANCH_EQ, /* target label, condition */
|
||||
BRANCH_FALSE, /* target label, condition */
|
||||
BRANCH_GE, /* target label, condition */
|
||||
BRANCH_GT, /* target label, condition */
|
||||
BRANCH_LE, /* target label, condition */
|
||||
BRANCH_LT, /* target label, condition */
|
||||
BRANCH_NE, /* target label, condition */
|
||||
BRANCH_TRUE, /* target label, condition */
|
||||
CALL, /* result, target, args */
|
||||
COMPARE_EQ, /* dest, source */
|
||||
COMPARE_GE, /* dest, source */
|
||||
|
@ -65,13 +68,15 @@ namespace VM {
|
|||
COMPARE_LT, /* dest, source */
|
||||
COMPARE_NE, /* dest, source */
|
||||
DIVIDE, /* dest, source1, source2 */
|
||||
GET_ELEMENT, /* dest, array, index */
|
||||
ELEM_XCR, /* dest, base, index, value */
|
||||
GET_ELEMENT, /* dest, base, index */
|
||||
GET_PROP, /* dest, object, prop name */
|
||||
INSTANCEOF, /* dest, source */
|
||||
JSR, /* target */
|
||||
LOAD_IMMEDIATE, /* dest, immediate value (double) */
|
||||
LOAD_NAME, /* dest, name */
|
||||
LOAD_STRING, /* dest, immediate value (string) */
|
||||
LOAD_VALUE, /* dest, immediate value (JSValue) */
|
||||
MOVE, /* dest, source */
|
||||
MULTIPLY, /* dest, source1, source2 */
|
||||
NAME_XCR, /* dest, name, value */
|
||||
|
@ -88,13 +93,14 @@ namespace VM {
|
|||
RETURN_VOID, /* Return without a value */
|
||||
RTS, /* Return to sender */
|
||||
SAVE_NAME, /* name, source */
|
||||
SET_ELEMENT, /* base, source1, source2 */
|
||||
SET_ELEMENT, /* base, index, value */
|
||||
SET_PROP, /* object, name, source */
|
||||
SHIFTLEFT, /* dest, source1, source2 */
|
||||
SHIFTRIGHT, /* dest, source1, source2 */
|
||||
STRICT_EQ, /* dest, source */
|
||||
STRICT_NE, /* dest, source */
|
||||
SUBTRACT, /* dest, source1, source2 */
|
||||
TEST, /* dest, source */
|
||||
THROW, /* exception value */
|
||||
TRYIN, /* catch target, finally target */
|
||||
TRYOUT, /* mmm, there is no try, only do */
|
||||
|
@ -114,11 +120,13 @@ namespace VM {
|
|||
"BITNOT ",
|
||||
"BRANCH ",
|
||||
"BRANCH_EQ ",
|
||||
"BRANCH_FALSE ",
|
||||
"BRANCH_GE ",
|
||||
"BRANCH_GT ",
|
||||
"BRANCH_LE ",
|
||||
"BRANCH_LT ",
|
||||
"BRANCH_NE ",
|
||||
"BRANCH_TRUE ",
|
||||
"CALL ",
|
||||
"COMPARE_EQ ",
|
||||
"COMPARE_GE ",
|
||||
|
@ -128,6 +136,7 @@ namespace VM {
|
|||
"COMPARE_LT ",
|
||||
"COMPARE_NE ",
|
||||
"DIVIDE ",
|
||||
"ELEM_XCR ",
|
||||
"GET_ELEMENT ",
|
||||
"GET_PROP ",
|
||||
"INSTANCEOF ",
|
||||
|
@ -135,6 +144,7 @@ namespace VM {
|
|||
"LOAD_IMMEDIATE",
|
||||
"LOAD_NAME ",
|
||||
"LOAD_STRING ",
|
||||
"LOAD_VALUE ",
|
||||
"MOVE ",
|
||||
"MULTIPLY ",
|
||||
"NAME_XCR ",
|
||||
|
@ -158,6 +168,7 @@ namespace VM {
|
|||
"STRICT_EQ ",
|
||||
"STRICT_NE ",
|
||||
"SUBTRACT ",
|
||||
"TEST ",
|
||||
"THROW ",
|
||||
"TRYIN ",
|
||||
"TRYOUT ",
|
||||
|
@ -188,11 +199,7 @@ namespace VM {
|
|||
return f;
|
||||
}
|
||||
|
||||
ICodeOp getBranchOp()
|
||||
{
|
||||
return ((mOpcode >= COMPARE_EQ) && (mOpcode <= COMPARE_NE)) ?
|
||||
(ICodeOp)(BRANCH_EQ + (mOpcode - COMPARE_EQ)) : NOP;
|
||||
}
|
||||
ICodeOp getBranchOp();
|
||||
|
||||
ICodeOp op() { return mOpcode; }
|
||||
|
||||
|
@ -445,6 +452,15 @@ namespace VM {
|
|||
/* print() and printOperands() inherited from GenericBranch */
|
||||
};
|
||||
|
||||
class BranchFalse : public GenericBranch {
|
||||
public:
|
||||
/* target label, condition */
|
||||
BranchFalse (Label* aOp1, Register aOp2) :
|
||||
GenericBranch
|
||||
(BRANCH_FALSE, aOp1, aOp2) {};
|
||||
/* print() and printOperands() inherited from GenericBranch */
|
||||
};
|
||||
|
||||
class BranchGE : public GenericBranch {
|
||||
public:
|
||||
/* target label, condition */
|
||||
|
@ -490,6 +506,15 @@ namespace VM {
|
|||
/* print() and printOperands() inherited from GenericBranch */
|
||||
};
|
||||
|
||||
class BranchTrue : public GenericBranch {
|
||||
public:
|
||||
/* target label, condition */
|
||||
BranchTrue (Label* aOp1, Register aOp2) :
|
||||
GenericBranch
|
||||
(BRANCH_TRUE, aOp1, aOp2) {};
|
||||
/* print() and printOperands() inherited from GenericBranch */
|
||||
};
|
||||
|
||||
class Call : public Instruction_3<Register, Register, RegisterList> {
|
||||
public:
|
||||
/* result, target, args */
|
||||
|
@ -578,6 +603,22 @@ namespace VM {
|
|||
/* print() and printOperands() inherited from Arithmetic */
|
||||
};
|
||||
|
||||
class ElemXcr : public Instruction_4<Register, Register, Register, double> {
|
||||
public:
|
||||
/* dest, base, index, value */
|
||||
ElemXcr (Register aOp1, Register aOp2, Register aOp3, double aOp4) :
|
||||
Instruction_4<Register, Register, Register, double>
|
||||
(ELEM_XCR, aOp1, aOp2, aOp3, aOp4) {};
|
||||
virtual Formatter& print(Formatter& f) {
|
||||
f << opcodeNames[ELEM_XCR] << "\t" << "R" << mOp1 << ", " << "R" << mOp2 << ", " << "R" << mOp3 << ", " << mOp4;
|
||||
return f;
|
||||
}
|
||||
virtual Formatter& printOperands(Formatter& f, const JSValues& registers) {
|
||||
f << "R" << mOp1 << '=' << registers[mOp1] << ", " << "R" << mOp2 << '=' << registers[mOp2] << ", " << "R" << mOp3 << '=' << registers[mOp3];
|
||||
return f;
|
||||
}
|
||||
};
|
||||
|
||||
class GetElement : public Instruction_3<Register, Register, Register> {
|
||||
public:
|
||||
/* dest, array, index */
|
||||
|
@ -682,6 +723,22 @@ namespace VM {
|
|||
}
|
||||
};
|
||||
|
||||
class LoadValue : public Instruction_2<Register, JSValue> {
|
||||
public:
|
||||
/* dest, immediate value (JSValue) */
|
||||
LoadValue (Register aOp1, JSValue aOp2) :
|
||||
Instruction_2<Register, JSValue>
|
||||
(LOAD_VALUE, aOp1, aOp2) {};
|
||||
virtual Formatter& print(Formatter& f) {
|
||||
f << opcodeNames[LOAD_VALUE] << "\t" << "R" << mOp1 << ", " << mOp2;
|
||||
return f;
|
||||
}
|
||||
virtual Formatter& printOperands(Formatter& f, const JSValues& registers) {
|
||||
f << "R" << mOp1 << '=' << registers[mOp1];
|
||||
return f;
|
||||
}
|
||||
};
|
||||
|
||||
class Move : public Instruction_2<Register, Register> {
|
||||
public:
|
||||
/* dest, source */
|
||||
|
@ -991,6 +1048,22 @@ namespace VM {
|
|||
/* print() and printOperands() inherited from Arithmetic */
|
||||
};
|
||||
|
||||
class Test : public Instruction_2<Register, Register> {
|
||||
public:
|
||||
/* dest, source */
|
||||
Test (Register aOp1, Register aOp2) :
|
||||
Instruction_2<Register, Register>
|
||||
(TEST, aOp1, aOp2) {};
|
||||
virtual Formatter& print(Formatter& f) {
|
||||
f << opcodeNames[TEST] << "\t" << "R" << mOp1 << ", " << "R" << mOp2;
|
||||
return f;
|
||||
}
|
||||
virtual Formatter& printOperands(Formatter& f, const JSValues& registers) {
|
||||
f << "R" << mOp1 << '=' << registers[mOp1] << ", " << "R" << mOp2 << '=' << registers[mOp2];
|
||||
return f;
|
||||
}
|
||||
};
|
||||
|
||||
class Throw : public Instruction_1<Register> {
|
||||
public:
|
||||
/* exception value */
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -131,6 +131,8 @@ namespace ICG {
|
|||
Register switchRegister; // register containing switch control value for most
|
||||
// recently in progress switch statement.
|
||||
VariableList *variableList; // name|register pair for each variable
|
||||
|
||||
World *mWorld; // used to register strings
|
||||
|
||||
|
||||
|
||||
|
@ -158,6 +160,8 @@ namespace ICG {
|
|||
void branch(Label *label);
|
||||
void branchConditional(Label *label, Register condition);
|
||||
void branchNotConditional(Label *label, Register condition);
|
||||
void branchTrue(Label *label, Register condition);
|
||||
void branchFalse(Label *label, Register condition);
|
||||
|
||||
void beginTry(Label *catchLabel, Label *finallyLabel)
|
||||
{ iCode->push_back(new Tryin(catchLabel, finallyLabel)); }
|
||||
|
@ -167,6 +171,8 @@ namespace ICG {
|
|||
void resetStatement()
|
||||
{ if (labelSet) { delete labelSet; labelSet = NULL; } resetTopRegister(); }
|
||||
|
||||
ICodeOp mapExprNodeToICodeOp(ExprNode::Kind kind);
|
||||
|
||||
public:
|
||||
ICodeGenerator(World *world = NULL,
|
||||
bool hasTryStatement = false,
|
||||
|
@ -182,14 +188,18 @@ namespace ICG {
|
|||
|
||||
ICodeModule *complete();
|
||||
|
||||
Register ICodeGenerator::genExpr(ExprNode *p, bool needBoolValueInBranch = false,
|
||||
Label *trueBranch = NULL,
|
||||
Label *falseBranch = NULL);
|
||||
|
||||
|
||||
Register allocateVariable(const StringAtom& name)
|
||||
{ Register result = getRegister(); (*variableList)[name] = result;
|
||||
registerBase = topRegister; return result; }
|
||||
|
||||
Register findVariable(const StringAtom& name)
|
||||
{ VariableList::iterator i = variableList->find(name);
|
||||
// What's map? // ASSERT(i != map.end());
|
||||
return (*i).second; }
|
||||
ASSERT(i != variableList->end()); return (*i).second; }
|
||||
|
||||
Register allocateParameter(const StringAtom& name)
|
||||
{ parameterCount++; return allocateVariable(name); }
|
||||
|
@ -203,9 +213,11 @@ namespace ICG {
|
|||
|
||||
void move(Register destination, Register source);
|
||||
void complement(Register destination, Register source);
|
||||
Register test(Register source);
|
||||
|
||||
Register compare(ICodeOp op, Register source1, Register source2);
|
||||
|
||||
Register loadValue(JSValue value);
|
||||
Register loadImmediate(double value);
|
||||
Register loadString(String &value);
|
||||
|
||||
|
@ -224,7 +236,9 @@ namespace ICG {
|
|||
|
||||
Register getElement(Register base, Register index);
|
||||
void setElement(Register base, Register index, Register value);
|
||||
|
||||
Register elementInc(Register base, Register index);
|
||||
Register elementDec(Register base, Register index);
|
||||
|
||||
Register getRegisterBase() { return topRegister; }
|
||||
InstructionStream *get_iCode() { return iCode; }
|
||||
StatementLabels *getStatementLabels() { return labelSet; labelSet = NULL; }
|
||||
|
@ -285,7 +299,7 @@ namespace ICG {
|
|||
|
||||
void beginLabelStatement(uint32 /* pos */, const StringAtom &label)
|
||||
{ labelSet->push_back(&label); }
|
||||
void endLabelStatement() { labelSet->pop_back(); }
|
||||
void endLabelStatement() { if (labelSet) labelSet->pop_back(); }
|
||||
|
||||
void continueStatement(uint32 pos);
|
||||
void breakStatement(uint32 pos);
|
||||
|
|
|
@ -277,6 +277,21 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args)
|
|||
}
|
||||
}
|
||||
break;
|
||||
/*
|
||||
versions below are not right, there is no 'array' type really, the index operation
|
||||
turns into a get_property call like this, only we need to be using JSString throughout.
|
||||
case GET_ELEMENT:
|
||||
{
|
||||
GetElement* ge = static_cast<GetElement*>(instruction);
|
||||
JSValue& base = (*registers)[src1(ge)];
|
||||
JSValue index = (*registers)[src2(ge)].toString();
|
||||
if (base.tag == JSValue::object_tag) {
|
||||
JSObject* object = base.object;
|
||||
(*registers)[dst(ge)] = object->getProperty(*index.string);
|
||||
}
|
||||
}
|
||||
break;
|
||||
*/
|
||||
case GET_ELEMENT:
|
||||
{
|
||||
GetElement* ge = static_cast<GetElement*>(instruction);
|
||||
|
@ -297,6 +312,7 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args)
|
|||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case LOAD_IMMEDIATE:
|
||||
{
|
||||
LoadImmediate* li = static_cast<LoadImmediate*>(instruction);
|
||||
|
@ -309,6 +325,12 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args)
|
|||
(*registers)[dst(ls)] = JSValue(src1(ls));
|
||||
}
|
||||
break;
|
||||
case LOAD_VALUE:
|
||||
{
|
||||
LoadValue* lv = static_cast<LoadValue*>(instruction);
|
||||
(*registers)[dst(lv)] = src1(lv);
|
||||
}
|
||||
break;
|
||||
case BRANCH:
|
||||
{
|
||||
GenericBranch* bra =
|
||||
|
@ -357,6 +379,28 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args)
|
|||
}
|
||||
}
|
||||
break;
|
||||
case BRANCH_TRUE:
|
||||
{
|
||||
GenericBranch* bc =
|
||||
static_cast<GenericBranch*>(instruction);
|
||||
ASSERT((*registers)[src1(bc)].isBoolean());
|
||||
if ((*registers)[src1(bc)].boolean) {
|
||||
mPC = begin_pc + ofs(bc);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BRANCH_FALSE:
|
||||
{
|
||||
GenericBranch* bc =
|
||||
static_cast<GenericBranch*>(instruction);
|
||||
ASSERT((*registers)[src1(bc)].isBoolean());
|
||||
if (!(*registers)[src1(bc)].boolean) {
|
||||
mPC = begin_pc + ofs(bc);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BRANCH_GE:
|
||||
{
|
||||
GenericBranch* bc =
|
||||
|
@ -377,6 +421,74 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args)
|
|||
}
|
||||
}
|
||||
break;
|
||||
case SHIFTLEFT:
|
||||
{
|
||||
Arithmetic* shl = static_cast<Arithmetic*>(instruction);
|
||||
JSValue& dest = (*registers)[dst(shl)];
|
||||
JSValue& r1 = (*registers)[src1(shl)];
|
||||
JSValue& r2 = (*registers)[src2(shl)];
|
||||
JSValue num1(r1.toInt32());
|
||||
JSValue num2(r2.toUInt32());
|
||||
dest = num1.i32 << (num2.u32 & 0x1F);
|
||||
}
|
||||
break;
|
||||
case SHIFTRIGHT:
|
||||
{
|
||||
Arithmetic* shr = static_cast<Arithmetic*>(instruction);
|
||||
JSValue& dest = (*registers)[dst(shr)];
|
||||
JSValue& r1 = (*registers)[src1(shr)];
|
||||
JSValue& r2 = (*registers)[src2(shr)];
|
||||
JSValue num1(r1.toInt32());
|
||||
JSValue num2(r2.toUInt32());
|
||||
dest = num1.i32 >> (num2.u32 & 0x1F);
|
||||
}
|
||||
break;
|
||||
case USHIFTRIGHT:
|
||||
{
|
||||
Arithmetic* ushr = static_cast<Arithmetic*>(instruction);
|
||||
JSValue& dest = (*registers)[dst(ushr)];
|
||||
JSValue& r1 = (*registers)[src1(ushr)];
|
||||
JSValue& r2 = (*registers)[src2(ushr)];
|
||||
JSValue num1(r1.toUInt32());
|
||||
JSValue num2(r2.toUInt32());
|
||||
dest = num1.u32 >> (num2.u32 & 0x1F);
|
||||
}
|
||||
break;
|
||||
|
||||
case AND:
|
||||
{
|
||||
Arithmetic* shr = static_cast<Arithmetic*>(instruction);
|
||||
JSValue& dest = (*registers)[dst(shr)];
|
||||
JSValue& r1 = (*registers)[src1(shr)];
|
||||
JSValue& r2 = (*registers)[src2(shr)];
|
||||
JSValue num1(r1.toInt32());
|
||||
JSValue num2(r2.toInt32());
|
||||
dest = num1.i32 & num2.i32;
|
||||
}
|
||||
break;
|
||||
case OR:
|
||||
{
|
||||
Arithmetic* shr = static_cast<Arithmetic*>(instruction);
|
||||
JSValue& dest = (*registers)[dst(shr)];
|
||||
JSValue& r1 = (*registers)[src1(shr)];
|
||||
JSValue& r2 = (*registers)[src2(shr)];
|
||||
JSValue num1(r1.toInt32());
|
||||
JSValue num2(r2.toInt32());
|
||||
dest = num1.i32 | num2.i32;
|
||||
}
|
||||
break;
|
||||
case XOR:
|
||||
{
|
||||
Arithmetic* shr = static_cast<Arithmetic*>(instruction);
|
||||
JSValue& dest = (*registers)[dst(shr)];
|
||||
JSValue& r1 = (*registers)[src1(shr)];
|
||||
JSValue& r2 = (*registers)[src2(shr)];
|
||||
JSValue num1(r1.toInt32());
|
||||
JSValue num2(r2.toInt32());
|
||||
dest = num1.i32 ^ num2.i32;
|
||||
}
|
||||
break;
|
||||
|
||||
case ADD:
|
||||
{
|
||||
// could get clever here with Functional forms.
|
||||
|
@ -462,11 +574,10 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args)
|
|||
JSValue r = mGlobal->getVariable(*src1(nx)).toNumber();
|
||||
dest = r;
|
||||
r.f64 += val3(nx);
|
||||
mGlobal->setVariable(*src1(nx), dest);
|
||||
mGlobal->setVariable(*src1(nx), r);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case COMPARE_LT:
|
||||
case COMPARE_LE:
|
||||
case COMPARE_EQ:
|
||||
|
@ -475,11 +586,44 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args)
|
|||
case COMPARE_GE:
|
||||
{
|
||||
Arithmetic* cmp = static_cast<Arithmetic*>(instruction);
|
||||
float64 diff =
|
||||
((*registers)[src1(cmp)].f64 -
|
||||
(*registers)[src2(cmp)].f64);
|
||||
(*registers)[dst(cmp)] =
|
||||
JSValue(int32(diff == 0.0 ? 0 : (diff > 0.0 ? 1 : -1)));
|
||||
JSValue& dest = (*registers)[dst(cmp)];
|
||||
JSValue lv = (*registers)[src1(cmp)].toPrimitive(JSValue::Number);
|
||||
JSValue rv = (*registers)[src2(cmp)].toPrimitive(JSValue::Number);
|
||||
if (lv.isString() && rv.isString()) {
|
||||
// XXX FIXME urgh, call w_strcmp ??? on a JSString ???
|
||||
}
|
||||
else {
|
||||
lv = lv.toNumber();
|
||||
rv = rv.toNumber();
|
||||
if (lv.isNaN() || rv.isNaN())
|
||||
dest = JSValue();
|
||||
else {
|
||||
// FIXME, does this do the right thing for +/- infinity?
|
||||
switch (instruction->op()) {
|
||||
case COMPARE_LT:
|
||||
dest = JSValue(lv.f64 < rv.f64); break;
|
||||
case COMPARE_LE:
|
||||
dest = JSValue(lv.f64 <= rv.f64); break;
|
||||
case COMPARE_EQ:
|
||||
dest = JSValue(lv.f64 == rv.f64); break;
|
||||
case COMPARE_NE:
|
||||
dest = JSValue(lv.f64 != rv.f64); break;
|
||||
case COMPARE_GT:
|
||||
dest = JSValue(lv.f64 > rv.f64); break;
|
||||
case COMPARE_GE:
|
||||
dest = JSValue(lv.f64 >= rv.f64); break;
|
||||
}
|
||||
// float64 diff = lv.f64 - rv.f64;
|
||||
// dest = JSValue( (int32) (diff == 0.0 ? 0 : (diff > 0.0 ? 1 : -1)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
case TEST:
|
||||
{
|
||||
Test* tst = static_cast<Test*>(instruction);
|
||||
(*registers)[dst(tst)] = (*registers)[src1(tst)].toBoolean();
|
||||
}
|
||||
break;
|
||||
case NEGATE:
|
||||
|
@ -494,6 +638,13 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args)
|
|||
(*registers)[dst(pos)] = (*registers)[src1(pos)].toNumber();
|
||||
}
|
||||
break;
|
||||
case BITNOT:
|
||||
{
|
||||
Bitnot* bn = static_cast<Bitnot*>(instruction);
|
||||
(*registers)[dst(bn)] = JSValue(~(*registers)[src1(bn)].toInt32().i32);
|
||||
}
|
||||
break;
|
||||
/*
|
||||
case NOT:
|
||||
{
|
||||
Not* nt = static_cast<Not*>(instruction);
|
||||
|
@ -501,7 +652,7 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args)
|
|||
JSValue(int32(!(*registers)[src1(nt)].i32));
|
||||
}
|
||||
break;
|
||||
|
||||
*/
|
||||
case THROW:
|
||||
{
|
||||
Throw* thrw = static_cast<Throw*>(instruction);
|
||||
|
@ -610,7 +761,7 @@ void Context::addListener(Listener* listener)
|
|||
void Context::removeListener(Listener* listener)
|
||||
{
|
||||
ListenerIterator e = mListeners.end();
|
||||
ListenerIterator l = find(mListeners.begin(), e, listener);
|
||||
ListenerIterator l = std::find(mListeners.begin(), e, listener);
|
||||
if (l != e) mListeners.erase(l);
|
||||
}
|
||||
|
||||
|
|
|
@ -40,18 +40,54 @@ namespace JSTypes {
|
|||
|
||||
// using JavaScript::StringAtom;
|
||||
|
||||
// the canonical undefined value.
|
||||
// the canonical undefined value, etc.
|
||||
const JSValue kUndefinedValue;
|
||||
const JSValue kNaN(0.0, 0.0);
|
||||
const JSValue kNaN = JSValue(nan);
|
||||
const JSValue kTrue = JSValue(true);
|
||||
const JSValue kFalse = JSValue(false);
|
||||
|
||||
#ifdef IS_LITTLE_ENDIAN
|
||||
#define JSDOUBLE_HI32(x) (((uint32 *)&(x))[1])
|
||||
#define JSDOUBLE_LO32(x) (((uint32 *)&(x))[0])
|
||||
#else
|
||||
#define JSDOUBLE_HI32(x) (((uint32 *)&(x))[0])
|
||||
#define JSDOUBLE_LO32(x) (((uint32 *)&(x))[1])
|
||||
#endif
|
||||
|
||||
#define JSDOUBLE_HI32_SIGNBIT 0x80000000
|
||||
#define JSDOUBLE_HI32_EXPMASK 0x7ff00000
|
||||
#define JSDOUBLE_HI32_MANTMASK 0x000fffff
|
||||
|
||||
#define JSDOUBLE_IS_NaN(x) \
|
||||
((JSDOUBLE_HI32(x) & JSDOUBLE_HI32_EXPMASK) == JSDOUBLE_HI32_EXPMASK && \
|
||||
(JSDOUBLE_LO32(x) || (JSDOUBLE_HI32(x) & JSDOUBLE_HI32_MANTMASK)))
|
||||
|
||||
#define JSDOUBLE_IS_INFINITE(x) \
|
||||
((JSDOUBLE_HI32(x) & ~JSDOUBLE_HI32_SIGNBIT) == JSDOUBLE_HI32_EXPMASK && \
|
||||
!JSDOUBLE_LO32(x))
|
||||
|
||||
#define JSDOUBLE_IS_FINITE(x) \
|
||||
((JSDOUBLE_HI32(x) & JSDOUBLE_HI32_EXPMASK) != JSDOUBLE_HI32_EXPMASK)
|
||||
|
||||
#define JSDOUBLE_IS_NEGZERO(d) (JSDOUBLE_HI32(d) == JSDOUBLE_HI32_SIGNBIT && \
|
||||
JSDOUBLE_LO32(d) == 0)
|
||||
|
||||
|
||||
|
||||
JSValue::JSValue(float64 a, float64 b)
|
||||
bool JSValue::isNaN() const
|
||||
{
|
||||
f64 = a/b;
|
||||
tag = f64_tag;
|
||||
ASSERT(isNumber());
|
||||
switch (tag) {
|
||||
case i32_tag:
|
||||
case u32_tag:
|
||||
return false;
|
||||
case f64_tag:
|
||||
return JSDOUBLE_IS_NaN(f64);
|
||||
default:
|
||||
NOT_REACHED("Broken compiler?");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int JSValue::operator==(const JSValue& value) const
|
||||
{
|
||||
if (this->tag == value.tag) {
|
||||
|
@ -62,6 +98,7 @@ int JSValue::operator==(const JSValue& value) const
|
|||
CASE(i32); CASE(u32); CASE(f32);
|
||||
CASE(i64); CASE(u64); CASE(f64);
|
||||
CASE(object); CASE(array); CASE(function); CASE(string);
|
||||
CASE(boolean);
|
||||
#undef CASE
|
||||
// question: are all undefined values equal to one another?
|
||||
case undefined_tag: return 1;
|
||||
|
@ -76,6 +113,9 @@ Formatter& operator<<(Formatter& f, const JSValue& value)
|
|||
case JSValue::i32_tag:
|
||||
f << float64(value.i32);
|
||||
break;
|
||||
case JSValue::u32_tag:
|
||||
f << float64(value.u32);
|
||||
break;
|
||||
case JSValue::f64_tag:
|
||||
f << value.f64;
|
||||
break;
|
||||
|
@ -87,39 +127,80 @@ Formatter& operator<<(Formatter& f, const JSValue& value)
|
|||
case JSValue::string_tag:
|
||||
f << *value.string;
|
||||
break;
|
||||
default:
|
||||
case JSValue::boolean_tag:
|
||||
f << ((value.boolean) ? "true" : "false");
|
||||
break;
|
||||
case JSValue::undefined_tag:
|
||||
f << "undefined";
|
||||
break;
|
||||
default:
|
||||
NOT_REACHED("Bad tag");
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
JSValue JSValue::toPrimitive(ECMA_type hint) const
|
||||
{
|
||||
switch (tag) {
|
||||
case i32_tag:
|
||||
case u32_tag:
|
||||
case f64_tag:
|
||||
case string_tag:
|
||||
case boolean_tag:
|
||||
case undefined_tag:
|
||||
return *this;
|
||||
|
||||
case object_tag:
|
||||
case array_tag:
|
||||
case function_tag:
|
||||
if (hint == String) {
|
||||
// FIXME
|
||||
}
|
||||
else {
|
||||
// FIXME
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
NOT_REACHED("Bad tag");
|
||||
}
|
||||
return kUndefinedValue;
|
||||
}
|
||||
|
||||
|
||||
JSValue JSValue::valueToString(const JSValue& value) // can assume value is not a string
|
||||
{
|
||||
char *chrp;
|
||||
char buf[dtosStandardBufferSize];
|
||||
switch (value.tag) {
|
||||
case JSValue::i32_tag:
|
||||
case i32_tag:
|
||||
chrp = doubleToStr(buf, dtosStandardBufferSize, value.i32, dtosStandard, 0);
|
||||
break;
|
||||
case JSValue::f64_tag:
|
||||
case u32_tag:
|
||||
chrp = doubleToStr(buf, dtosStandardBufferSize, value.u32, dtosStandard, 0);
|
||||
break;
|
||||
case f64_tag:
|
||||
chrp = doubleToStr(buf, dtosStandardBufferSize, value.f64, dtosStandard, 0);
|
||||
break;
|
||||
case JSValue::object_tag:
|
||||
case object_tag:
|
||||
chrp = "object";
|
||||
break;
|
||||
case JSValue::array_tag:
|
||||
case array_tag:
|
||||
chrp = "array";
|
||||
break;
|
||||
case JSValue::function_tag:
|
||||
case function_tag:
|
||||
chrp = "function";
|
||||
break;
|
||||
case JSValue::string_tag:
|
||||
case string_tag:
|
||||
return value;
|
||||
default:
|
||||
case boolean_tag:
|
||||
chrp = (value.boolean) ? "true" : "false";
|
||||
break;
|
||||
case undefined_tag:
|
||||
chrp = "undefined";
|
||||
break;
|
||||
default:
|
||||
NOT_REACHED("Bad tag");
|
||||
}
|
||||
return JSValue(new JSString(chrp));
|
||||
}
|
||||
|
@ -127,25 +208,142 @@ JSValue JSValue::valueToString(const JSValue& value) // can assume value is not
|
|||
JSValue JSValue::valueToNumber(const JSValue& value) // can assume value is not a number
|
||||
{
|
||||
switch (value.tag) {
|
||||
case JSValue::i32_tag:
|
||||
case JSValue::f64_tag:
|
||||
case i32_tag:
|
||||
return JSValue((float64)value.i32);
|
||||
case u32_tag:
|
||||
return JSValue((float64)value.u32);
|
||||
case f64_tag:
|
||||
return value;
|
||||
case JSValue::string_tag:
|
||||
case string_tag:
|
||||
{
|
||||
JSString* str = value.string;
|
||||
const char16 *numEnd;
|
||||
double d = stringToDouble(str->begin(), str->end(), numEnd);
|
||||
return JSValue(d);
|
||||
}
|
||||
case JSValue::object_tag:
|
||||
case JSValue::array_tag:
|
||||
case JSValue::function_tag:
|
||||
break;
|
||||
default:
|
||||
case object_tag:
|
||||
case array_tag:
|
||||
case function_tag:
|
||||
// XXX more needed :
|
||||
// toNumber(toPrimitive(hint Number))
|
||||
return kUndefinedValue;
|
||||
case boolean_tag:
|
||||
return JSValue((value.boolean) ? 1.0 : 0.0);
|
||||
case undefined_tag:
|
||||
return kNaN;
|
||||
break;
|
||||
default:
|
||||
NOT_REACHED("Bad tag");
|
||||
return kUndefinedValue;
|
||||
}
|
||||
return kUndefinedValue;
|
||||
}
|
||||
|
||||
JSValue JSValue::valueToBoolean(const JSValue& value)
|
||||
{
|
||||
switch (value.tag) {
|
||||
case i32_tag:
|
||||
return JSValue(value.i32 != 0);
|
||||
case u32_tag:
|
||||
return JSValue(value.u32 != 0);
|
||||
case f64_tag:
|
||||
return JSValue(!(value.f64 == 0.0) || JSDOUBLE_IS_NaN(value.f64));
|
||||
case string_tag:
|
||||
return JSValue(value.string->length() != 0);
|
||||
case boolean_tag:
|
||||
return value;
|
||||
case object_tag:
|
||||
case array_tag:
|
||||
case function_tag:
|
||||
return kTrue;
|
||||
case undefined_tag:
|
||||
return kFalse;
|
||||
default:
|
||||
NOT_REACHED("Bad tag");
|
||||
return kUndefinedValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const double two32 = 4294967296.0;
|
||||
static const double two31 = 2147483648.0;
|
||||
|
||||
JSValue JSValue::valueToInt32(const JSValue& value)
|
||||
{
|
||||
double d;
|
||||
switch (value.tag) {
|
||||
case i32_tag:
|
||||
return value;
|
||||
case u32_tag:
|
||||
d = value.u32;
|
||||
break;
|
||||
case f64_tag:
|
||||
d = value.f64;
|
||||
break;
|
||||
case string_tag:
|
||||
{
|
||||
JSString* str = value.string;
|
||||
const char16 *numEnd;
|
||||
d = stringToDouble(str->begin(), str->end(), numEnd);
|
||||
}
|
||||
break;
|
||||
case boolean_tag:
|
||||
return JSValue((int32)((value.boolean) ? 1 : 0));
|
||||
case object_tag:
|
||||
case array_tag:
|
||||
case undefined_tag:
|
||||
// toNumber(toPrimitive(hint Number))
|
||||
return kUndefinedValue;
|
||||
default:
|
||||
NOT_REACHED("Bad tag");
|
||||
return kUndefinedValue;
|
||||
}
|
||||
if ((d == 0.0) || !JSDOUBLE_IS_FINITE(d) )
|
||||
return JSValue((int32)0);
|
||||
d = fmod(d, two32);
|
||||
d = (d >= 0) ? d : d + two32;
|
||||
if (d >= two31)
|
||||
return JSValue((int32)(d - two32));
|
||||
else
|
||||
return JSValue((int32)d);
|
||||
|
||||
}
|
||||
|
||||
JSValue JSValue::valueToUInt32(const JSValue& value)
|
||||
{
|
||||
double d;
|
||||
switch (value.tag) {
|
||||
case i32_tag:
|
||||
return JSValue((uint32)value.i32);
|
||||
case u32_tag:
|
||||
return value;
|
||||
case f64_tag:
|
||||
d = value.f64;
|
||||
break;
|
||||
case string_tag:
|
||||
{
|
||||
JSString* str = value.string;
|
||||
const char16 *numEnd;
|
||||
d = stringToDouble(str->begin(), str->end(), numEnd);
|
||||
}
|
||||
break;
|
||||
case boolean_tag:
|
||||
return JSValue((uint32)((value.boolean) ? 1 : 0));
|
||||
case object_tag:
|
||||
case array_tag:
|
||||
case undefined_tag:
|
||||
// toNumber(toPrimitive(hint Number))
|
||||
return kUndefinedValue;
|
||||
default:
|
||||
NOT_REACHED("Bad tag");
|
||||
return kUndefinedValue;
|
||||
}
|
||||
if ((d == 0.0) || !JSDOUBLE_IS_FINITE(d))
|
||||
return JSValue((uint32)0);
|
||||
bool neg = (d < 0);
|
||||
d = floor(neg ? -d : d);
|
||||
d = neg ? -d : d;
|
||||
d = fmod(d, two32);
|
||||
d = (d >= 0) ? d : d + two32;
|
||||
return JSValue((uint32)d);
|
||||
}
|
||||
|
||||
JSFunction::~JSFunction()
|
||||
|
|
|
@ -76,44 +76,66 @@ namespace JSTypes {
|
|||
JSArray* array;
|
||||
JSFunction *function;
|
||||
JSString *string;
|
||||
bool boolean;
|
||||
};
|
||||
|
||||
/* These are the ECMA types, for use in 'toPrimitive' calls */
|
||||
enum ECMA_type {
|
||||
Undefined, Null, Boolean, Number, Object, String,
|
||||
NoHint
|
||||
};
|
||||
|
||||
enum {
|
||||
i8_tag, u8_tag,
|
||||
i16_tag, u16_tag,
|
||||
i32_tag, u32_tag,
|
||||
i64_tag, u64_tag,
|
||||
f32_tag, f64_tag,
|
||||
object_tag, array_tag, function_tag, string_tag,
|
||||
object_tag, array_tag, function_tag, string_tag, boolean_tag,
|
||||
undefined_tag
|
||||
} tag;
|
||||
|
||||
JSValue() : f64(0.0), tag(undefined_tag) {}
|
||||
explicit JSValue(int32 i32) : i32(i32), tag(i32_tag) {}
|
||||
explicit JSValue(uint32 u32) : u32(u32), tag(u32_tag) {}
|
||||
explicit JSValue(float64 f64) : f64(f64), tag(f64_tag) {}
|
||||
explicit JSValue(JSObject* object) : object(object), tag(object_tag) {}
|
||||
explicit JSValue(JSArray* array) : array(array), tag(array_tag) {}
|
||||
explicit JSValue(JSFunction* function) : function(function), tag(function_tag) {}
|
||||
explicit JSValue(JSString* string) : string(string), tag(string_tag) {}
|
||||
explicit JSValue(bool boolean) : boolean(boolean), tag(boolean_tag) {}
|
||||
|
||||
JSValue(float64 a, float64 b);
|
||||
|
||||
int32& operator=(int32 i32) { return (tag = i32_tag, this->i32 = i32); }
|
||||
uint32& operator=(uint32 u32) { return (tag = u32_tag, this->u32 = u32); }
|
||||
float64& operator=(float64 f64) { return (tag = f64_tag, this->f64 = f64); }
|
||||
JSObject*& operator=(JSObject* object) { return (tag = object_tag, this->object = object); }
|
||||
JSArray*& operator=(JSArray* array) { return (tag = array_tag, this->array = array); }
|
||||
JSFunction*& operator=(JSFunction* function) { return (tag = function_tag, this->function = function); }
|
||||
JSString*& operator=(JSString* string) { return (tag = string_tag, this->string = string); }
|
||||
bool& operator=(bool boolean) { return (tag = boolean_tag, this->boolean = boolean); }
|
||||
|
||||
bool isString() const { return (tag == string_tag); }
|
||||
bool isNumber() const { return ((tag == f64_tag) || (tag == i32_tag)); }
|
||||
bool isBoolean() const { return (tag == boolean_tag); }
|
||||
bool isNumber() const { return (tag == f64_tag); }
|
||||
/* this is correct wrt ECMA, The i32 & u32 kinds
|
||||
will have to be converted to doubles anyway because
|
||||
we can't have overflow happening in generic arithmetic */
|
||||
bool isNaN() const;
|
||||
|
||||
JSValue toString() const { return (isString() ? *this : valueToString(*this)); }
|
||||
JSValue toNumber() const { return (isNumber() ? *this : valueToNumber(*this)); }
|
||||
JSValue toInt32() const { return ((tag == i32_tag) ? *this : valueToInt32(*this)); }
|
||||
JSValue toUInt32() const { return ((tag == u32_tag) ? *this : valueToUInt32(*this)); }
|
||||
JSValue toBoolean() const { return ((tag == boolean_tag) ? *this : valueToBoolean(*this)); }
|
||||
|
||||
JSValue toPrimitive(ECMA_type hint = NoHint) const;
|
||||
|
||||
|
||||
static JSValue valueToString(const JSValue& value);
|
||||
static JSValue valueToNumber(const JSValue& value);
|
||||
static JSValue valueToInt32(const JSValue& value);
|
||||
static JSValue valueToUInt32(const JSValue& value);
|
||||
static JSValue valueToBoolean(const JSValue& value);
|
||||
|
||||
int operator==(const JSValue& value) const;
|
||||
};
|
||||
|
@ -140,6 +162,8 @@ namespace JSTypes {
|
|||
|
||||
extern const JSValue kUndefinedValue;
|
||||
extern const JSValue kNaN;
|
||||
extern const JSValue kTrue;
|
||||
extern const JSValue kFalse;
|
||||
|
||||
/**
|
||||
* Basic behavior of all JS objects, mapping a name to a value,
|
||||
|
|
|
@ -107,6 +107,30 @@ namespace VM {
|
|||
return f;
|
||||
}
|
||||
|
||||
ICodeOp Instruction::getBranchOp()
|
||||
{
|
||||
// XXX FIXME, convert to table lookup or arithmetic after the dust settles
|
||||
switch (mOpcode) {
|
||||
case COMPARE_EQ:
|
||||
// return BRANCH_EQ;
|
||||
case COMPARE_NE:
|
||||
// return BRANCH_NE;
|
||||
case COMPARE_GE:
|
||||
// return BRANCH_GE;
|
||||
case COMPARE_GT:
|
||||
// return BRANCH_GT;
|
||||
case COMPARE_LE:
|
||||
// return BRANCH_LE;
|
||||
case COMPARE_LT:
|
||||
// return BRANCH_LT;
|
||||
case TEST:
|
||||
return BRANCH_TRUE;
|
||||
default:
|
||||
NOT_REACHED("Unexpected branch code");
|
||||
return NOP;
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace VM */
|
||||
} /* namespace JavaScript */
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
namespace JavaScript {
|
||||
namespace VM {
|
||||
|
||||
using JSTypes::JSValue;
|
||||
using JSTypes::JSValues;
|
||||
using JSTypes::JSString;
|
||||
|
||||
|
@ -51,11 +52,13 @@ namespace VM {
|
|||
BITNOT, /* dest, source */
|
||||
BRANCH, /* target label */
|
||||
BRANCH_EQ, /* target label, condition */
|
||||
BRANCH_FALSE, /* target label, condition */
|
||||
BRANCH_GE, /* target label, condition */
|
||||
BRANCH_GT, /* target label, condition */
|
||||
BRANCH_LE, /* target label, condition */
|
||||
BRANCH_LT, /* target label, condition */
|
||||
BRANCH_NE, /* target label, condition */
|
||||
BRANCH_TRUE, /* target label, condition */
|
||||
CALL, /* result, target, args */
|
||||
COMPARE_EQ, /* dest, source */
|
||||
COMPARE_GE, /* dest, source */
|
||||
|
@ -65,13 +68,15 @@ namespace VM {
|
|||
COMPARE_LT, /* dest, source */
|
||||
COMPARE_NE, /* dest, source */
|
||||
DIVIDE, /* dest, source1, source2 */
|
||||
GET_ELEMENT, /* dest, array, index */
|
||||
ELEM_XCR, /* dest, base, index, value */
|
||||
GET_ELEMENT, /* dest, base, index */
|
||||
GET_PROP, /* dest, object, prop name */
|
||||
INSTANCEOF, /* dest, source */
|
||||
JSR, /* target */
|
||||
LOAD_IMMEDIATE, /* dest, immediate value (double) */
|
||||
LOAD_NAME, /* dest, name */
|
||||
LOAD_STRING, /* dest, immediate value (string) */
|
||||
LOAD_VALUE, /* dest, immediate value (JSValue) */
|
||||
MOVE, /* dest, source */
|
||||
MULTIPLY, /* dest, source1, source2 */
|
||||
NAME_XCR, /* dest, name, value */
|
||||
|
@ -88,13 +93,14 @@ namespace VM {
|
|||
RETURN_VOID, /* Return without a value */
|
||||
RTS, /* Return to sender */
|
||||
SAVE_NAME, /* name, source */
|
||||
SET_ELEMENT, /* base, source1, source2 */
|
||||
SET_ELEMENT, /* base, index, value */
|
||||
SET_PROP, /* object, name, source */
|
||||
SHIFTLEFT, /* dest, source1, source2 */
|
||||
SHIFTRIGHT, /* dest, source1, source2 */
|
||||
STRICT_EQ, /* dest, source */
|
||||
STRICT_NE, /* dest, source */
|
||||
SUBTRACT, /* dest, source1, source2 */
|
||||
TEST, /* dest, source */
|
||||
THROW, /* exception value */
|
||||
TRYIN, /* catch target, finally target */
|
||||
TRYOUT, /* mmm, there is no try, only do */
|
||||
|
@ -114,11 +120,13 @@ namespace VM {
|
|||
"BITNOT ",
|
||||
"BRANCH ",
|
||||
"BRANCH_EQ ",
|
||||
"BRANCH_FALSE ",
|
||||
"BRANCH_GE ",
|
||||
"BRANCH_GT ",
|
||||
"BRANCH_LE ",
|
||||
"BRANCH_LT ",
|
||||
"BRANCH_NE ",
|
||||
"BRANCH_TRUE ",
|
||||
"CALL ",
|
||||
"COMPARE_EQ ",
|
||||
"COMPARE_GE ",
|
||||
|
@ -128,6 +136,7 @@ namespace VM {
|
|||
"COMPARE_LT ",
|
||||
"COMPARE_NE ",
|
||||
"DIVIDE ",
|
||||
"ELEM_XCR ",
|
||||
"GET_ELEMENT ",
|
||||
"GET_PROP ",
|
||||
"INSTANCEOF ",
|
||||
|
@ -135,6 +144,7 @@ namespace VM {
|
|||
"LOAD_IMMEDIATE",
|
||||
"LOAD_NAME ",
|
||||
"LOAD_STRING ",
|
||||
"LOAD_VALUE ",
|
||||
"MOVE ",
|
||||
"MULTIPLY ",
|
||||
"NAME_XCR ",
|
||||
|
@ -158,6 +168,7 @@ namespace VM {
|
|||
"STRICT_EQ ",
|
||||
"STRICT_NE ",
|
||||
"SUBTRACT ",
|
||||
"TEST ",
|
||||
"THROW ",
|
||||
"TRYIN ",
|
||||
"TRYOUT ",
|
||||
|
@ -188,11 +199,7 @@ namespace VM {
|
|||
return f;
|
||||
}
|
||||
|
||||
ICodeOp getBranchOp()
|
||||
{
|
||||
return ((mOpcode >= COMPARE_EQ) && (mOpcode <= COMPARE_NE)) ?
|
||||
(ICodeOp)(BRANCH_EQ + (mOpcode - COMPARE_EQ)) : NOP;
|
||||
}
|
||||
ICodeOp getBranchOp();
|
||||
|
||||
ICodeOp op() { return mOpcode; }
|
||||
|
||||
|
@ -445,6 +452,15 @@ namespace VM {
|
|||
/* print() and printOperands() inherited from GenericBranch */
|
||||
};
|
||||
|
||||
class BranchFalse : public GenericBranch {
|
||||
public:
|
||||
/* target label, condition */
|
||||
BranchFalse (Label* aOp1, Register aOp2) :
|
||||
GenericBranch
|
||||
(BRANCH_FALSE, aOp1, aOp2) {};
|
||||
/* print() and printOperands() inherited from GenericBranch */
|
||||
};
|
||||
|
||||
class BranchGE : public GenericBranch {
|
||||
public:
|
||||
/* target label, condition */
|
||||
|
@ -490,6 +506,15 @@ namespace VM {
|
|||
/* print() and printOperands() inherited from GenericBranch */
|
||||
};
|
||||
|
||||
class BranchTrue : public GenericBranch {
|
||||
public:
|
||||
/* target label, condition */
|
||||
BranchTrue (Label* aOp1, Register aOp2) :
|
||||
GenericBranch
|
||||
(BRANCH_TRUE, aOp1, aOp2) {};
|
||||
/* print() and printOperands() inherited from GenericBranch */
|
||||
};
|
||||
|
||||
class Call : public Instruction_3<Register, Register, RegisterList> {
|
||||
public:
|
||||
/* result, target, args */
|
||||
|
@ -578,6 +603,22 @@ namespace VM {
|
|||
/* print() and printOperands() inherited from Arithmetic */
|
||||
};
|
||||
|
||||
class ElemXcr : public Instruction_4<Register, Register, Register, double> {
|
||||
public:
|
||||
/* dest, base, index, value */
|
||||
ElemXcr (Register aOp1, Register aOp2, Register aOp3, double aOp4) :
|
||||
Instruction_4<Register, Register, Register, double>
|
||||
(ELEM_XCR, aOp1, aOp2, aOp3, aOp4) {};
|
||||
virtual Formatter& print(Formatter& f) {
|
||||
f << opcodeNames[ELEM_XCR] << "\t" << "R" << mOp1 << ", " << "R" << mOp2 << ", " << "R" << mOp3 << ", " << mOp4;
|
||||
return f;
|
||||
}
|
||||
virtual Formatter& printOperands(Formatter& f, const JSValues& registers) {
|
||||
f << "R" << mOp1 << '=' << registers[mOp1] << ", " << "R" << mOp2 << '=' << registers[mOp2] << ", " << "R" << mOp3 << '=' << registers[mOp3];
|
||||
return f;
|
||||
}
|
||||
};
|
||||
|
||||
class GetElement : public Instruction_3<Register, Register, Register> {
|
||||
public:
|
||||
/* dest, array, index */
|
||||
|
@ -682,6 +723,22 @@ namespace VM {
|
|||
}
|
||||
};
|
||||
|
||||
class LoadValue : public Instruction_2<Register, JSValue> {
|
||||
public:
|
||||
/* dest, immediate value (JSValue) */
|
||||
LoadValue (Register aOp1, JSValue aOp2) :
|
||||
Instruction_2<Register, JSValue>
|
||||
(LOAD_VALUE, aOp1, aOp2) {};
|
||||
virtual Formatter& print(Formatter& f) {
|
||||
f << opcodeNames[LOAD_VALUE] << "\t" << "R" << mOp1 << ", " << mOp2;
|
||||
return f;
|
||||
}
|
||||
virtual Formatter& printOperands(Formatter& f, const JSValues& registers) {
|
||||
f << "R" << mOp1 << '=' << registers[mOp1];
|
||||
return f;
|
||||
}
|
||||
};
|
||||
|
||||
class Move : public Instruction_2<Register, Register> {
|
||||
public:
|
||||
/* dest, source */
|
||||
|
@ -991,6 +1048,22 @@ namespace VM {
|
|||
/* print() and printOperands() inherited from Arithmetic */
|
||||
};
|
||||
|
||||
class Test : public Instruction_2<Register, Register> {
|
||||
public:
|
||||
/* dest, source */
|
||||
Test (Register aOp1, Register aOp2) :
|
||||
Instruction_2<Register, Register>
|
||||
(TEST, aOp1, aOp2) {};
|
||||
virtual Formatter& print(Formatter& f) {
|
||||
f << opcodeNames[TEST] << "\t" << "R" << mOp1 << ", " << "R" << mOp2;
|
||||
return f;
|
||||
}
|
||||
virtual Formatter& printOperands(Formatter& f, const JSValues& registers) {
|
||||
f << "R" << mOp1 << '=' << registers[mOp1] << ", " << "R" << mOp2 << '=' << registers[mOp2];
|
||||
return f;
|
||||
}
|
||||
};
|
||||
|
||||
class Throw : public Instruction_1<Register> {
|
||||
public:
|
||||
/* exception value */
|
||||
|
|
Загрузка…
Ссылка в новой задаче