зеркало из https://github.com/mozilla/pjs.git
Work to support 'this'.
This commit is contained in:
Родитель
6beafca960
Коммит
3783f2c99e
|
@ -159,6 +159,14 @@ TypedRegister ICodeGenerator::loadString(String &value)
|
|||
return dest;
|
||||
}
|
||||
|
||||
TypedRegister ICodeGenerator::loadString(const StringAtom &value)
|
||||
{
|
||||
TypedRegister dest(getRegister(), &String_Type);
|
||||
LoadString *instr = new LoadString(dest, new JSString(value));
|
||||
iCode->push_back(instr);
|
||||
return dest;
|
||||
}
|
||||
|
||||
TypedRegister ICodeGenerator::loadBoolean(bool value)
|
||||
{
|
||||
TypedRegister dest(getRegister(), &Boolean_Type);
|
||||
|
@ -175,6 +183,14 @@ TypedRegister ICodeGenerator::newObject()
|
|||
return dest;
|
||||
}
|
||||
|
||||
TypedRegister ICodeGenerator::newFunction(ICodeModule *icm)
|
||||
{
|
||||
TypedRegister dest(getRegister(), &Function_Type);
|
||||
NewFunction *instr = new NewFunction(dest, icm);
|
||||
iCode->push_back(instr);
|
||||
return dest;
|
||||
}
|
||||
|
||||
TypedRegister ICodeGenerator::newArray()
|
||||
{
|
||||
TypedRegister dest(getRegister(), &Array_Type);
|
||||
|
@ -183,8 +199,6 @@ TypedRegister ICodeGenerator::newArray()
|
|||
return dest;
|
||||
}
|
||||
|
||||
|
||||
|
||||
TypedRegister ICodeGenerator::loadName(const StringAtom &name)
|
||||
{
|
||||
TypedRegister dest(getRegister(), &Any_Type);
|
||||
|
@ -343,18 +357,20 @@ TypedRegister ICodeGenerator::op(ICodeOp op, TypedRegister source1,
|
|||
return dest;
|
||||
}
|
||||
|
||||
TypedRegister ICodeGenerator::call(TypedRegister target, RegisterList args)
|
||||
TypedRegister ICodeGenerator::call(TypedRegister target, const StringAtom &name, RegisterList args)
|
||||
{
|
||||
TypedRegister dest(getRegister(), &Any_Type);
|
||||
Call *instr = new Call(dest, target, args);
|
||||
Call *instr = new Call(dest, target, &name, args);
|
||||
iCode->push_back(instr);
|
||||
return dest;
|
||||
}
|
||||
|
||||
void ICodeGenerator::callVoid(TypedRegister target, RegisterList args)
|
||||
TypedRegister ICodeGenerator::methodCall(TypedRegister targetBase, TypedRegister targetValue, RegisterList args)
|
||||
{
|
||||
Call *instr = new Call(TypedRegister(NotARegister, &Void_Type), target, args);
|
||||
TypedRegister dest(getRegister(), &Any_Type);
|
||||
MethodCall *instr = new MethodCall(dest, targetBase, targetValue, args);
|
||||
iCode->push_back(instr);
|
||||
return dest;
|
||||
}
|
||||
|
||||
void ICodeGenerator::branch(Label *label)
|
||||
|
@ -514,8 +530,6 @@ static bool generatedBoolean(ExprNode *p)
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
if trueBranch OR falseBranch are not null, the sub-expression should generate
|
||||
a conditional branch to the appropriate target. If either branch is NULL, it
|
||||
|
@ -563,14 +577,36 @@ TypedRegister ICodeGenerator::genExpr(ExprNode *p,
|
|||
case ExprNode::call :
|
||||
{
|
||||
InvokeExprNode *i = static_cast<InvokeExprNode *>(p);
|
||||
TypedRegister fn = genExpr(i->op);
|
||||
RegisterList args;
|
||||
ExprPairList *p = i->pairs;
|
||||
while (p) {
|
||||
args.push_back(genExpr(p->value));
|
||||
p = p->next;
|
||||
}
|
||||
ret = call(fn, args);
|
||||
|
||||
if (i->op->getKind() == ExprNode::dot) {
|
||||
BinaryExprNode *b = static_cast<BinaryExprNode *>(i->op);
|
||||
ret = methodCall(genExpr(b->op1), loadString(static_cast<IdentifierExprNode *>(b->op2)->name), args);
|
||||
}
|
||||
else
|
||||
if (i->op->getKind() == ExprNode::identifier) {
|
||||
if (!mWithinWith) {
|
||||
TypedRegister v = findVariable((static_cast<IdentifierExprNode *>(i->op))->name);
|
||||
if (v.first != NotARegister)
|
||||
ret = call(v, static_cast<IdentifierExprNode *>(i->op)->name, args);
|
||||
else
|
||||
ret = call(TypedRegister(NotARegister, &Null_Type), static_cast<IdentifierExprNode *>(i->op)->name, args);
|
||||
}
|
||||
else
|
||||
ret = methodCall(TypedRegister(NotARegister, &Null_Type), loadString(static_cast<IdentifierExprNode *>(i->op)->name), args);
|
||||
}
|
||||
else
|
||||
if (i->op->getKind() == ExprNode::index) {
|
||||
BinaryExprNode *b = static_cast<BinaryExprNode *>(i->op);
|
||||
ret = methodCall(genExpr(b->op1), genExpr(b->op2), args);
|
||||
}
|
||||
else
|
||||
ASSERT("WAH!");
|
||||
}
|
||||
break;
|
||||
case ExprNode::index :
|
||||
|
@ -588,6 +624,11 @@ TypedRegister ICodeGenerator::genExpr(ExprNode *p,
|
|||
ret = getProperty(base, static_cast<IdentifierExprNode *>(b->op2)->name);
|
||||
}
|
||||
break;
|
||||
case ExprNode::This :
|
||||
{
|
||||
ret = TypedRegister(0, &Any_Type);
|
||||
}
|
||||
break;
|
||||
case ExprNode::identifier :
|
||||
{
|
||||
if (!mWithinWith) {
|
||||
|
@ -644,6 +685,8 @@ TypedRegister ICodeGenerator::genExpr(ExprNode *p,
|
|||
ret = op(ADD, ret, loadImmediate(1.0));
|
||||
setElement(base, index, ret);
|
||||
}
|
||||
else
|
||||
ASSERT("WAH!");
|
||||
}
|
||||
break;
|
||||
case ExprNode::postIncrement:
|
||||
|
@ -673,6 +716,8 @@ TypedRegister ICodeGenerator::genExpr(ExprNode *p,
|
|||
TypedRegister index = genExpr(b->op2);
|
||||
ret = elementInc(base, index);
|
||||
}
|
||||
else
|
||||
ASSERT("WAH!");
|
||||
}
|
||||
break;
|
||||
case ExprNode::preDecrement:
|
||||
|
@ -712,6 +757,8 @@ TypedRegister ICodeGenerator::genExpr(ExprNode *p,
|
|||
ret = op(SUBTRACT, ret, loadImmediate(1.0));
|
||||
setElement(base, index, ret);
|
||||
}
|
||||
else
|
||||
ASSERT("WAH!");
|
||||
}
|
||||
break;
|
||||
case ExprNode::postDecrement:
|
||||
|
@ -741,6 +788,8 @@ TypedRegister ICodeGenerator::genExpr(ExprNode *p,
|
|||
TypedRegister index = genExpr(b->op2);
|
||||
ret = elementInc(base, index);
|
||||
}
|
||||
else
|
||||
ASSERT("WAH!");
|
||||
}
|
||||
break;
|
||||
case ExprNode::plus:
|
||||
|
@ -798,6 +847,8 @@ TypedRegister ICodeGenerator::genExpr(ExprNode *p,
|
|||
TypedRegister index = genExpr(lb->op2);
|
||||
setElement(base, index, ret);
|
||||
}
|
||||
else
|
||||
ASSERT("WAH!");
|
||||
}
|
||||
break;
|
||||
case ExprNode::addEquals:
|
||||
|
@ -850,6 +901,8 @@ TypedRegister ICodeGenerator::genExpr(ExprNode *p,
|
|||
ret = op(mapExprNodeToICodeOp(p->getKind()), v, ret);
|
||||
setElement(base, index, ret);
|
||||
}
|
||||
else
|
||||
ASSERT("WAH!");
|
||||
}
|
||||
break;
|
||||
case ExprNode::equal:
|
||||
|
@ -988,7 +1041,6 @@ TypedRegister ICodeGenerator::genExpr(ExprNode *p,
|
|||
case ExprNode::objectLiteral:
|
||||
{
|
||||
ret = newObject();
|
||||
|
||||
PairListExprNode *plen = static_cast<PairListExprNode *>(p);
|
||||
ExprPairList *e = plen->pairs;
|
||||
while (e) {
|
||||
|
@ -999,6 +1051,25 @@ TypedRegister ICodeGenerator::genExpr(ExprNode *p,
|
|||
}
|
||||
break;
|
||||
|
||||
case ExprNode::functionLiteral:
|
||||
{
|
||||
FunctionExprNode *f = static_cast<FunctionExprNode *>(p);
|
||||
ICodeGenerator icg(mWorld, mGlobal);
|
||||
icg.allocateParameter(mWorld->identifiers[widenCString("this")]); // always parameter #0
|
||||
VariableBinding *v = f->function.parameters;
|
||||
while (v) {
|
||||
if (v->name && (v->name->getKind() == ExprNode::identifier))
|
||||
icg.allocateParameter((static_cast<IdentifierExprNode *>(v->name))->name);
|
||||
v = v->next;
|
||||
}
|
||||
icg.preprocess(f->function.body);
|
||||
icg.genStmt(f->function.body);
|
||||
//stdOut << icg;
|
||||
ICodeModule *icm = icg.complete();
|
||||
ret = newFunction(icm);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
NOT_REACHED("Unsupported ExprNode kind");
|
||||
|
@ -1044,7 +1115,7 @@ void ICodeGenerator::preprocess(StmtNode *p)
|
|||
VariableBinding *v = vs->bindings;
|
||||
while (v) {
|
||||
if (v->name && (v->name->getKind() == ExprNode::identifier))
|
||||
if (v->type->getKind() == ExprNode::identifier)
|
||||
if (v->type && (v->type->getKind() == ExprNode::identifier))
|
||||
allocateVariable((static_cast<IdentifierExprNode *>(v->name))->name,
|
||||
(static_cast<IdentifierExprNode *>(v->type))->name);
|
||||
else
|
||||
|
@ -1141,6 +1212,7 @@ TypedRegister ICodeGenerator::genStmt(StmtNode *p, LabelSet *currentLabelSet)
|
|||
{
|
||||
FunctionStmtNode *f = static_cast<FunctionStmtNode *>(p);
|
||||
ICodeGenerator icg(mWorld, mGlobal);
|
||||
icg.allocateParameter(mWorld->identifiers[widenCString("this")]); // always parameter #0
|
||||
VariableBinding *v = f->function.parameters;
|
||||
while (v) {
|
||||
if (v->name && (v->name->getKind() == ExprNode::identifier))
|
||||
|
@ -1472,6 +1544,11 @@ TypedRegister ICodeGenerator::genStmt(StmtNode *p, LabelSet *currentLabelSet)
|
|||
}
|
||||
break;
|
||||
|
||||
case StmtNode::Class:
|
||||
{
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
NOT_REACHED("unimplemented statement kind");
|
||||
}
|
||||
|
|
|
@ -219,8 +219,8 @@ namespace ICG {
|
|||
|
||||
TypedRegister op(ICodeOp op, TypedRegister source);
|
||||
TypedRegister op(ICodeOp op, TypedRegister source1, TypedRegister source2);
|
||||
TypedRegister call(TypedRegister target, RegisterList args);
|
||||
void callVoid(TypedRegister target, RegisterList args);
|
||||
TypedRegister call(TypedRegister target, const StringAtom &name, RegisterList args);
|
||||
TypedRegister methodCall(TypedRegister targetBase, TypedRegister targetValue, RegisterList args);
|
||||
|
||||
void move(TypedRegister destination, TypedRegister source);
|
||||
TypedRegister logicalNot(TypedRegister source);
|
||||
|
@ -229,9 +229,11 @@ namespace ICG {
|
|||
TypedRegister loadBoolean(bool value);
|
||||
TypedRegister loadImmediate(double value);
|
||||
TypedRegister loadString(String &value);
|
||||
TypedRegister loadString(const StringAtom &name);
|
||||
|
||||
TypedRegister newObject();
|
||||
TypedRegister newArray();
|
||||
TypedRegister newFunction(ICodeModule *icm);
|
||||
|
||||
TypedRegister loadName(const StringAtom &name);
|
||||
void saveName(const StringAtom &name, TypedRegister value);
|
||||
|
|
|
@ -89,12 +89,13 @@ struct Activation : public gc_base {
|
|||
}
|
||||
}
|
||||
|
||||
Activation(ICodeModule* iCode, Activation* caller,
|
||||
Activation(ICodeModule* iCode, Activation* caller, const JSValue thisArg,
|
||||
const RegisterList& list)
|
||||
: mRegisters(iCode->itsMaxRegister + 1), mICode(iCode)
|
||||
{
|
||||
// copy caller's parameter list to initial registers.
|
||||
JSValues::iterator dest = mRegisters.begin();
|
||||
*dest++ = thisArg;
|
||||
const JSValues& params = caller->mRegisters;
|
||||
for (RegisterList::const_iterator src = list.begin(),
|
||||
end = list.end(); src != end; ++src, ++dest) {
|
||||
|
@ -102,12 +103,6 @@ struct Activation : public gc_base {
|
|||
}
|
||||
}
|
||||
|
||||
Activation(ICodeModule* iCode, const JSValue arg)
|
||||
: mRegisters(iCode->itsMaxRegister + 1), mICode(iCode)
|
||||
{
|
||||
mRegisters[0] = arg;
|
||||
}
|
||||
|
||||
Activation(ICodeModule* iCode, const JSValue arg1, const JSValue arg2)
|
||||
: mRegisters(iCode->itsMaxRegister + 1), mICode(iCode)
|
||||
{
|
||||
|
@ -489,26 +484,78 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args)
|
|||
}
|
||||
break;
|
||||
|
||||
case CALL:
|
||||
case METHOD_CALL:
|
||||
{
|
||||
Call* call = static_cast<Call*>(instruction);
|
||||
JSFunction *target = (*registers)[op2(call).first].function;
|
||||
MethodCall* call = static_cast<MethodCall*>(instruction);
|
||||
ASSERT((*registers)[op3(call).first].isString());
|
||||
|
||||
JSValue base;
|
||||
JSValue prop;
|
||||
if (op2(call).first == NotARegister) {
|
||||
base = mGlobal->getReference(prop, *((*registers)[op3(call).first].string));
|
||||
|
||||
}
|
||||
else {
|
||||
base = (*registers)[op2(call).first];
|
||||
ASSERT(base.tag == JSValue::object_tag); // XXX runtime error
|
||||
base = base.object->getReference(prop, *((*registers)[op3(call).first].string));
|
||||
}
|
||||
ASSERT(prop.isFunction()); // XXX runtime error
|
||||
JSFunction *target = prop.function;
|
||||
if (target->isNative()) {
|
||||
RegisterList ¶ms = op3(call);
|
||||
JSValues argv(params.size());
|
||||
JSValues::size_type i = 0;
|
||||
RegisterList ¶ms = op4(call);
|
||||
JSValues argv(params.size() + 1);
|
||||
argv[0] = base;
|
||||
JSValues::size_type i = 1;
|
||||
for (RegisterList::const_iterator src = params.begin(), end = params.end();
|
||||
src != end; ++src, ++i) {
|
||||
argv[i] = (*registers)[src->first];
|
||||
}
|
||||
if (op2(call).first != NotARegister)
|
||||
(*registers)[op2(call).first] = static_cast<JSNativeFunction*>(target)->mCode(argv);
|
||||
JSValue result = static_cast<JSNativeFunction*>(target)->mCode(argv);
|
||||
if (op1(call).first != NotARegister)
|
||||
(*registers)[op1(call).first] = result;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
mLinkage = new Linkage(mLinkage, ++mPC,
|
||||
mActivation, op1(call));
|
||||
mActivation = new Activation(target->getICode(), mActivation, op3(call));
|
||||
mActivation = new Activation(target->getICode(), mActivation, base, op4(call));
|
||||
registers = &mActivation->mRegisters;
|
||||
mPC = mActivation->mICode->its_iCode->begin();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
case CALL:
|
||||
{
|
||||
Call* call = static_cast<Call*>(instruction);
|
||||
JSFunction *target;
|
||||
if (op2(call).first == NotARegister) {
|
||||
ASSERT(mGlobal->getVariable(*op3(call)).isFunction());
|
||||
target = mGlobal->getVariable(*op3(call)).function;
|
||||
}
|
||||
else {
|
||||
ASSERT((*registers)[op2(call).first].isFunction()); // XXX runtime error
|
||||
target = (*registers)[op2(call).first].function;
|
||||
}
|
||||
if (target->isNative()) {
|
||||
RegisterList ¶ms = op4(call);
|
||||
JSValues argv(params.size() + 1);
|
||||
argv[0] = kNull;
|
||||
JSValues::size_type i = 1;
|
||||
for (RegisterList::const_iterator src = params.begin(), end = params.end();
|
||||
src != end; ++src, ++i) {
|
||||
argv[i] = (*registers)[src->first];
|
||||
}
|
||||
JSValue result = static_cast<JSNativeFunction*>(target)->mCode(argv);
|
||||
if (op1(call).first != NotARegister)
|
||||
(*registers)[op1(call).first] = result;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
mLinkage = new Linkage(mLinkage, ++mPC,
|
||||
mActivation, op1(call));
|
||||
mActivation = new Activation(target->getICode(), mActivation, kNull, op4(call));
|
||||
registers = &mActivation->mRegisters;
|
||||
mPC = mActivation->mICode->its_iCode->begin();
|
||||
continue;
|
||||
|
@ -577,6 +624,12 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args)
|
|||
(*registers)[dst(no).first] = JSValue(new JSObject());
|
||||
}
|
||||
break;
|
||||
case NEW_FUNCTION:
|
||||
{
|
||||
NewFunction* nf = static_cast<NewFunction*>(instruction);
|
||||
(*registers)[dst(nf).first] = JSValue(new JSFunction(src1(nf)));
|
||||
}
|
||||
break;
|
||||
case NEW_ARRAY:
|
||||
{
|
||||
NewArray* na = static_cast<NewArray*>(instruction);
|
||||
|
@ -591,6 +644,7 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args)
|
|||
JSObject* object = value.object;
|
||||
(*registers)[dst(gp).first] = object->getProperty(*src2(gp));
|
||||
}
|
||||
// XXX runtime error
|
||||
}
|
||||
break;
|
||||
case SET_PROP:
|
||||
|
@ -787,6 +841,7 @@ using JSString throughout.
|
|||
(*registers)[dst(bn).first] = JSValue(~(*registers)[src1(bn).first].toInt32().i32);
|
||||
}
|
||||
break;
|
||||
|
||||
case NOT:
|
||||
{
|
||||
Not* nt = static_cast<Not*>(instruction);
|
||||
|
@ -794,6 +849,7 @@ using JSString throughout.
|
|||
(*registers)[dst(nt).first] = JSValue(!(*registers)[src1(nt).first].boolean);
|
||||
}
|
||||
break;
|
||||
|
||||
case THROW:
|
||||
{
|
||||
Throw* thrw = static_cast<Throw*>(instruction);
|
||||
|
|
|
@ -89,9 +89,9 @@ const bool showTokens = false;
|
|||
static JSValue print(const JSValues &argv)
|
||||
{
|
||||
size_t n = argv.size();
|
||||
if (n > 0) {
|
||||
stdOut << argv[0];
|
||||
for (size_t i = 1; i < n; ++i)
|
||||
if (n > 1) { // the 'this' parameter in un-interesting
|
||||
stdOut << argv[1];
|
||||
for (size_t i = 2; i < n; ++i)
|
||||
stdOut << ' ' << argv[i];
|
||||
}
|
||||
stdOut << "\n";
|
||||
|
@ -99,13 +99,13 @@ static JSValue print(const JSValues &argv)
|
|||
}
|
||||
|
||||
|
||||
static void genCode(World &world, Context &cx, StmtNode *p)
|
||||
static void genCode(Context &cx, StmtNode *p)
|
||||
{
|
||||
JSScope glob;
|
||||
ICodeGenerator icg(&world, &glob);
|
||||
ICodeGenerator icg(&cx.getWorld(), cx.getGlobalObject());
|
||||
icg.isScript();
|
||||
TypedRegister ret(NotARegister, &None_Type);
|
||||
while (p) {
|
||||
icg.preprocess(p);
|
||||
ret = icg.genStmt(p);
|
||||
p = p->next;
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ static void readEvalPrint(FILE *in, World &world)
|
|||
stdOut << '\n';
|
||||
#if 0
|
||||
// Generate code for parsedStatements, which is a linked list of zero or more statements
|
||||
genCode(world, cx, parsedStatements);
|
||||
genCode(cx, parsedStatements);
|
||||
#endif
|
||||
}
|
||||
clear(buffer);
|
||||
|
@ -232,9 +232,15 @@ void testCompile()
|
|||
JSScope glob;
|
||||
ICodeGenerator icg(&world, &glob);
|
||||
icg.isScript();
|
||||
while (parsedStatements) {
|
||||
icg.genStmt(parsedStatements);
|
||||
parsedStatements = parsedStatements->next;
|
||||
StmtNode *s = parsedStatements;
|
||||
while (s) {
|
||||
icg.preprocess(s);
|
||||
s = s->next;
|
||||
}
|
||||
s = parsedStatements;
|
||||
while (s) {
|
||||
icg.genStmt(s);
|
||||
s = s->next;
|
||||
}
|
||||
cx.interpret(icg.complete(), JSValues());
|
||||
}
|
||||
|
@ -253,7 +259,7 @@ int main(int argc, char **argv)
|
|||
#endif
|
||||
using namespace JavaScript;
|
||||
using namespace Shell;
|
||||
#if 0
|
||||
#if 1
|
||||
testCompile();
|
||||
#endif
|
||||
readEvalPrint(stdin, world);
|
||||
|
|
|
@ -45,6 +45,7 @@ const JSValue kUndefinedValue;
|
|||
const JSValue kNaN = JSValue(nan);
|
||||
const JSValue kTrue = JSValue(true);
|
||||
const JSValue kFalse = JSValue(false);
|
||||
const JSValue kNull = JSValue((JSObject*)NULL);
|
||||
|
||||
const JSType Any_Type = JSType(NULL);
|
||||
const JSType Integer_Type = JSType(&Any_Type);
|
||||
|
@ -94,6 +95,8 @@ const JSType *JSValue::getType() const
|
|||
return &Integer_Type;
|
||||
case JSValue::u32_tag:
|
||||
return &Integer_Type;
|
||||
case JSValue::integer_tag:
|
||||
return &Integer_Type;
|
||||
case JSValue::f64_tag:
|
||||
return &Number_Type;
|
||||
case JSValue::object_tag:
|
||||
|
@ -123,6 +126,7 @@ bool JSValue::isNaN() const
|
|||
case i32_tag:
|
||||
case u32_tag:
|
||||
return false;
|
||||
case integer_tag:
|
||||
case f64_tag:
|
||||
return JSDOUBLE_IS_NaN(f64);
|
||||
default:
|
||||
|
@ -143,6 +147,7 @@ int JSValue::operator==(const JSValue& value) const
|
|||
CASE(object); CASE(array); CASE(function); CASE(string);
|
||||
CASE(boolean);
|
||||
#undef CASE
|
||||
case integer_tag : return (this->f64 == value.f64);
|
||||
// question: are all undefined values equal to one another?
|
||||
case undefined_tag: return 1;
|
||||
}
|
||||
|
@ -173,6 +178,7 @@ Formatter& operator<<(Formatter& f, const JSValue& value)
|
|||
case JSValue::u32_tag:
|
||||
f << float64(value.u32);
|
||||
break;
|
||||
case JSValue::integer_tag:
|
||||
case JSValue::f64_tag:
|
||||
f << value.f64;
|
||||
break;
|
||||
|
@ -197,6 +203,34 @@ Formatter& operator<<(Formatter& f, const JSValue& value)
|
|||
case JSValue::undefined_tag:
|
||||
f << "undefined";
|
||||
break;
|
||||
case JSValue::type_tag:
|
||||
if (value.type == &Any_Type) // yuck (see yuck comment below)
|
||||
f << "Any_Type";
|
||||
else if (value.type == &Integer_Type)
|
||||
f << "Integer_Type";
|
||||
else if (value.type == &Number_Type)
|
||||
f << "Number_Type";
|
||||
else if (value.type == &Character_Type)
|
||||
f << "Character_Type";
|
||||
else if (value.type == &String_Type)
|
||||
f << "String_Type";
|
||||
else if (value.type == &Function_Type)
|
||||
f << "Function_Type";
|
||||
else if (value.type == &Array_Type)
|
||||
f << "Array_Type";
|
||||
else if (value.type == &Type_Type)
|
||||
f << "Type_Type";
|
||||
else if (value.type == &Boolean_Type)
|
||||
f << "Boolean_Type";
|
||||
else if (value.type == &Null_Type)
|
||||
f << "Null_Type";
|
||||
else if (value.type == &Void_Type)
|
||||
f << "Void_Type";
|
||||
else if (value.type == &None_Type)
|
||||
f << "None_Type";
|
||||
else
|
||||
f << "Unknown_Type";
|
||||
break;
|
||||
default:
|
||||
NOT_REACHED("Bad tag");
|
||||
}
|
||||
|
@ -209,6 +243,7 @@ JSValue JSValue::toPrimitive(ECMA_type /*hint*/) const
|
|||
switch (tag) {
|
||||
case i32_tag:
|
||||
case u32_tag:
|
||||
case integer_tag:
|
||||
case f64_tag:
|
||||
case string_tag:
|
||||
case boolean_tag:
|
||||
|
@ -258,6 +293,7 @@ JSValue JSValue::valueToString(const JSValue& value) // can assume value is not
|
|||
case u32_tag:
|
||||
chrp = doubleToStr(buf, dtosStandardBufferSize, value.u32, dtosStandard, 0);
|
||||
break;
|
||||
case integer_tag:
|
||||
case f64_tag:
|
||||
chrp = doubleToStr(buf, dtosStandardBufferSize, value.f64, dtosStandard, 0);
|
||||
break;
|
||||
|
@ -291,6 +327,7 @@ JSValue JSValue::valueToNumber(const JSValue& value) // can assume value is not
|
|||
return JSValue((float64)value.i32);
|
||||
case u32_tag:
|
||||
return JSValue((float64)value.u32);
|
||||
case integer_tag:
|
||||
case f64_tag:
|
||||
return value;
|
||||
case string_tag:
|
||||
|
@ -316,6 +353,18 @@ JSValue JSValue::valueToNumber(const JSValue& value) // can assume value is not
|
|||
}
|
||||
}
|
||||
|
||||
JSValue JSValue::valueToInteger(const JSValue& value)
|
||||
{
|
||||
JSValue result = valueToNumber(value);
|
||||
ASSERT(result.tag == f64_tag);
|
||||
result.tag = i64_tag;
|
||||
bool neg = (result.f64 < 0);
|
||||
result.f64 = floor((neg) ? -result.f64 : result.f64);
|
||||
result.f64 = (neg) ? -result.f64 : result.f64;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
JSValue JSValue::valueToBoolean(const JSValue& value)
|
||||
{
|
||||
switch (value.tag) {
|
||||
|
@ -323,6 +372,7 @@ JSValue JSValue::valueToBoolean(const JSValue& value)
|
|||
return JSValue(value.i32 != 0);
|
||||
case u32_tag:
|
||||
return JSValue(value.u32 != 0);
|
||||
case integer_tag:
|
||||
case f64_tag:
|
||||
return JSValue(!(value.f64 == 0.0) || JSDOUBLE_IS_NaN(value.f64));
|
||||
case string_tag:
|
||||
|
@ -354,6 +404,7 @@ JSValue JSValue::valueToInt32(const JSValue& value)
|
|||
case u32_tag:
|
||||
d = value.u32;
|
||||
break;
|
||||
case integer_tag:
|
||||
case f64_tag:
|
||||
d = value.f64;
|
||||
break;
|
||||
|
@ -425,6 +476,22 @@ JSValue JSValue::valueToUInt32(const JSValue& value)
|
|||
return JSValue((uint32)d);
|
||||
}
|
||||
|
||||
|
||||
JSValue JSValue::convert(const JSType *toType)
|
||||
{
|
||||
if (toType == &Any_Type) // yuck, something wrong with this
|
||||
// maybe the base types should be
|
||||
// a family of classes, not just instances
|
||||
// of JSType ???
|
||||
return *this;
|
||||
else if (toType == &Integer_Type)
|
||||
return valueToInteger(*this);
|
||||
else
|
||||
// etc...
|
||||
return kUndefinedValue;
|
||||
}
|
||||
|
||||
|
||||
JSFunction::~JSFunction()
|
||||
{
|
||||
delete mICode;
|
||||
|
@ -451,6 +518,13 @@ JSString::JSString(const char* str)
|
|||
std::transform(str, str + n, begin(), JavaScript::widen);
|
||||
}
|
||||
|
||||
|
||||
JSString::operator String()
|
||||
{
|
||||
return String(begin(), size());
|
||||
}
|
||||
|
||||
|
||||
// # of sub-type relationship between this type and the other type
|
||||
// (== MAX_INT if other is not a base type)
|
||||
|
||||
|
|
|
@ -93,6 +93,7 @@ namespace JSTypes {
|
|||
i32_tag, u32_tag,
|
||||
i64_tag, u64_tag,
|
||||
f32_tag, f64_tag,
|
||||
integer_tag,
|
||||
object_tag, array_tag, function_tag, string_tag, boolean_tag, type_tag,
|
||||
undefined_tag
|
||||
} tag;
|
||||
|
@ -122,10 +123,12 @@ namespace JSTypes {
|
|||
bool isObject() const { return ((tag == object_tag) || (tag == function_tag) || (tag == array_tag)); }
|
||||
bool isString() const { return (tag == string_tag); }
|
||||
bool isBoolean() const { return (tag == boolean_tag); }
|
||||
bool isNumber() const { return (tag == f64_tag); }
|
||||
bool isNumber() const { return (tag == f64_tag) || (tag == integer_tag); }
|
||||
|
||||
/* this is correct wrt ECMA, The i32 & u32 kinds
|
||||
will have to be converted to doubles anyway because
|
||||
will have to be converted (to doubles?) anyway because
|
||||
we can't have overflow happening in generic arithmetic */
|
||||
|
||||
bool isUndefined() const { return (tag == undefined_tag); }
|
||||
bool isNull() const { return ((tag == object_tag) && (this->object == NULL)); }
|
||||
bool isNaN() const;
|
||||
|
@ -139,9 +142,12 @@ namespace JSTypes {
|
|||
|
||||
JSValue toPrimitive(ECMA_type hint = NoHint) const;
|
||||
|
||||
JSValue convert(const JSType *toType);
|
||||
|
||||
|
||||
static JSValue valueToString(const JSValue& value);
|
||||
static JSValue valueToNumber(const JSValue& value);
|
||||
static JSValue valueToInteger(const JSValue& value);
|
||||
static JSValue valueToInt32(const JSValue& value);
|
||||
static JSValue valueToUInt32(const JSValue& value);
|
||||
static JSValue valueToBoolean(const JSValue& value);
|
||||
|
@ -176,6 +182,20 @@ namespace JSTypes {
|
|||
extern const JSValue kNaN;
|
||||
extern const JSValue kTrue;
|
||||
extern const JSValue kFalse;
|
||||
extern const JSValue kNull;
|
||||
|
||||
extern const JSType Any_Type;
|
||||
extern const JSType Integer_Type;
|
||||
extern const JSType Number_Type;
|
||||
extern const JSType Character_Type;
|
||||
extern const JSType String_Type;
|
||||
extern const JSType Function_Type;
|
||||
extern const JSType Array_Type;
|
||||
extern const JSType Type_Type;
|
||||
extern const JSType Boolean_Type;
|
||||
extern const JSType Null_Type;
|
||||
extern const JSType Void_Type;
|
||||
extern const JSType None_Type;
|
||||
|
||||
/**
|
||||
* Basic behavior of all JS objects, mapping a name to a value,
|
||||
|
@ -193,7 +213,7 @@ namespace JSTypes {
|
|||
{
|
||||
return (mProperties.count(name) != 0);
|
||||
}
|
||||
|
||||
|
||||
const JSValue& getProperty(const String& name)
|
||||
{
|
||||
JSProperties::const_iterator i = mProperties.find(name);
|
||||
|
@ -203,6 +223,20 @@ namespace JSTypes {
|
|||
return mPrototype->getProperty(name);
|
||||
return kUndefinedValue;
|
||||
}
|
||||
|
||||
// return the property AND the object it's found in
|
||||
// (would rather return references, but couldn't get that to work)
|
||||
const JSValue getReference(JSValue &prop, const String& name)
|
||||
{
|
||||
JSProperties::const_iterator i = mProperties.find(name);
|
||||
if (i != mProperties.end()) {
|
||||
prop = i->second;
|
||||
return JSValue(this);
|
||||
}
|
||||
if (mPrototype)
|
||||
return mPrototype->getReference(prop, name);
|
||||
return kUndefinedValue;
|
||||
}
|
||||
|
||||
JSValue& setProperty(const String& name, const JSValue& value)
|
||||
{
|
||||
|
@ -329,6 +363,8 @@ namespace JSTypes {
|
|||
explicit JSString(const String& str);
|
||||
explicit JSString(const String* str);
|
||||
explicit JSString(const char* str);
|
||||
|
||||
operator String();
|
||||
};
|
||||
|
||||
class JSException : public gc_base {
|
||||
|
@ -413,20 +449,6 @@ namespace JSTypes {
|
|||
int32 distance(const JSType *other) const;
|
||||
};
|
||||
|
||||
|
||||
extern const JSType Any_Type;
|
||||
extern const JSType Integer_Type;
|
||||
extern const JSType Number_Type;
|
||||
extern const JSType Character_Type;
|
||||
extern const JSType String_Type;
|
||||
extern const JSType Function_Type;
|
||||
extern const JSType Array_Type;
|
||||
extern const JSType Type_Type;
|
||||
extern const JSType Boolean_Type;
|
||||
extern const JSType Null_Type;
|
||||
extern const JSType Void_Type;
|
||||
extern const JSType None_Type;
|
||||
|
||||
|
||||
} /* namespace JSTypes */
|
||||
} /* namespace JavaScript */
|
||||
|
|
|
@ -115,6 +115,12 @@ $ops{"NEW_OBJECT"} =
|
|||
rem => "dest",
|
||||
params => [ ("TypedRegister") ]
|
||||
};
|
||||
$ops{"NEW_FUNCTION"} =
|
||||
{
|
||||
super => "Instruction_2",
|
||||
rem => "dest, ICodeModule",
|
||||
params => [ ("TypedRegister", "ICodeModule *") ]
|
||||
};
|
||||
$ops{"NEW_ARRAY"} =
|
||||
{
|
||||
super => "Instruction_1",
|
||||
|
@ -216,9 +222,15 @@ $ops{"RETURN_VOID"} =
|
|||
};
|
||||
$ops{"CALL"} =
|
||||
{
|
||||
super => "Instruction_3",
|
||||
rem => "result, target, args",
|
||||
params => [ ("TypedRegister" , "TypedRegister", "RegisterList") ]
|
||||
super => "Instruction_4",
|
||||
rem => "result, target, name, args",
|
||||
params => [ ("TypedRegister" , "TypedRegister", "const StringAtom*", "RegisterList") ]
|
||||
};
|
||||
$ops{"METHOD_CALL"} =
|
||||
{
|
||||
super => "Instruction_4",
|
||||
rem => "result, target base, target value, args",
|
||||
params => [ ("TypedRegister" , "TypedRegister" , "TypedRegister", "RegisterList") ]
|
||||
};
|
||||
$ops{"THROW"} =
|
||||
{
|
||||
|
@ -452,6 +464,8 @@ sub get_print_body {
|
|||
push (@oplist, "\"'\" << *mOp$op << \"'\"");
|
||||
} elsif ($type =~ /bool/) {
|
||||
push (@oplist, "\"'\" << ((mOp$op) ? \"true\" : \"false\") << \"'\"");
|
||||
} elsif ($type =~ /ICodeModule/) {
|
||||
push (@oplist, "\"ICodeModule\"");
|
||||
} else {
|
||||
push (@oplist, "mOp$op");
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace VM {
|
|||
BRANCH, /* target label */
|
||||
BRANCH_FALSE, /* target label, condition */
|
||||
BRANCH_TRUE, /* target label, condition */
|
||||
CALL, /* result, target, args */
|
||||
CALL, /* result, target, name, args */
|
||||
COMPARE_EQ, /* dest, source1, source2 */
|
||||
COMPARE_GE, /* dest, source1, source2 */
|
||||
COMPARE_GT, /* dest, source1, source2 */
|
||||
|
@ -77,11 +77,13 @@ namespace VM {
|
|||
LOAD_IMMEDIATE, /* dest, immediate value (double) */
|
||||
LOAD_NAME, /* dest, name */
|
||||
LOAD_STRING, /* dest, immediate value (string) */
|
||||
METHOD_CALL, /* result, target base, target value, args */
|
||||
MOVE, /* dest, source */
|
||||
MULTIPLY, /* dest, source1, source2 */
|
||||
NAME_XCR, /* dest, name, value */
|
||||
NEGATE, /* dest, source */
|
||||
NEW_ARRAY, /* dest */
|
||||
NEW_FUNCTION, /* dest, ICodeModule */
|
||||
NEW_OBJECT, /* dest */
|
||||
NOP, /* do nothing and like it */
|
||||
NOT, /* dest, source */
|
||||
|
@ -139,11 +141,13 @@ namespace VM {
|
|||
"LOAD_IMMEDIATE",
|
||||
"LOAD_NAME ",
|
||||
"LOAD_STRING ",
|
||||
"METHOD_CALL ",
|
||||
"MOVE ",
|
||||
"MULTIPLY ",
|
||||
"NAME_XCR ",
|
||||
"NEGATE ",
|
||||
"NEW_ARRAY ",
|
||||
"NEW_FUNCTION ",
|
||||
"NEW_OBJECT ",
|
||||
"NOP ",
|
||||
"NOT ",
|
||||
|
@ -472,18 +476,18 @@ namespace VM {
|
|||
/* print() and printOperands() inherited from GenericBranch */
|
||||
};
|
||||
|
||||
class Call : public Instruction_3<TypedRegister, TypedRegister, RegisterList> {
|
||||
class Call : public Instruction_4<TypedRegister, TypedRegister, const StringAtom*, RegisterList> {
|
||||
public:
|
||||
/* result, target, args */
|
||||
Call (TypedRegister aOp1, TypedRegister aOp2, RegisterList aOp3) :
|
||||
Instruction_3<TypedRegister, TypedRegister, RegisterList>
|
||||
(CALL, aOp1, aOp2, aOp3) {};
|
||||
/* result, target, name, args */
|
||||
Call (TypedRegister aOp1, TypedRegister aOp2, const StringAtom* aOp3, RegisterList aOp4) :
|
||||
Instruction_4<TypedRegister, TypedRegister, const StringAtom*, RegisterList>
|
||||
(CALL, aOp1, aOp2, aOp3, aOp4) {};
|
||||
virtual Formatter& print(Formatter& f) {
|
||||
f << opcodeNames[CALL] << "\t" << "R" << mOp1.first << ", " << "R" << mOp2.first << ", " << mOp3;
|
||||
f << opcodeNames[CALL] << "\t" << "R" << mOp1.first << ", " << "R" << mOp2.first << ", " << "'" << *mOp3 << "'" << ", " << mOp4;
|
||||
return f;
|
||||
}
|
||||
virtual Formatter& printOperands(Formatter& f, const JSValues& registers) {
|
||||
f << "R" << mOp1.first << '=' << registers[mOp1.first] << ", " << "R" << mOp2.first << '=' << registers[mOp2.first] << ", " << ArgList(mOp3, registers);
|
||||
f << "R" << mOp1.first << '=' << registers[mOp1.first] << ", " << "R" << mOp2.first << '=' << registers[mOp2.first] << ", " << ArgList(mOp4, registers);
|
||||
return f;
|
||||
}
|
||||
};
|
||||
|
@ -696,6 +700,22 @@ namespace VM {
|
|||
}
|
||||
};
|
||||
|
||||
class MethodCall : public Instruction_4<TypedRegister, TypedRegister, TypedRegister, RegisterList> {
|
||||
public:
|
||||
/* result, target base, target value, args */
|
||||
MethodCall (TypedRegister aOp1, TypedRegister aOp2, TypedRegister aOp3, RegisterList aOp4) :
|
||||
Instruction_4<TypedRegister, TypedRegister, TypedRegister, RegisterList>
|
||||
(METHOD_CALL, aOp1, aOp2, aOp3, aOp4) {};
|
||||
virtual Formatter& print(Formatter& f) {
|
||||
f << opcodeNames[METHOD_CALL] << "\t" << "R" << mOp1.first << ", " << "R" << mOp2.first << ", " << "R" << mOp3.first << ", " << mOp4;
|
||||
return f;
|
||||
}
|
||||
virtual Formatter& printOperands(Formatter& f, const JSValues& registers) {
|
||||
f << "R" << mOp1.first << '=' << registers[mOp1.first] << ", " << "R" << mOp2.first << '=' << registers[mOp2.first] << ", " << "R" << mOp3.first << '=' << registers[mOp3.first] << ", " << ArgList(mOp4, registers);
|
||||
return f;
|
||||
}
|
||||
};
|
||||
|
||||
class Move : public Instruction_2<TypedRegister, TypedRegister> {
|
||||
public:
|
||||
/* dest, source */
|
||||
|
@ -769,6 +789,22 @@ namespace VM {
|
|||
}
|
||||
};
|
||||
|
||||
class NewFunction : public Instruction_2<TypedRegister, ICodeModule *> {
|
||||
public:
|
||||
/* dest, ICodeModule */
|
||||
NewFunction (TypedRegister aOp1, ICodeModule * aOp2) :
|
||||
Instruction_2<TypedRegister, ICodeModule *>
|
||||
(NEW_FUNCTION, aOp1, aOp2) {};
|
||||
virtual Formatter& print(Formatter& f) {
|
||||
f << opcodeNames[NEW_FUNCTION] << "\t" << "R" << mOp1.first << ", " << "ICodeModule";
|
||||
return f;
|
||||
}
|
||||
virtual Formatter& printOperands(Formatter& f, const JSValues& registers) {
|
||||
f << "R" << mOp1.first << '=' << registers[mOp1.first];
|
||||
return f;
|
||||
}
|
||||
};
|
||||
|
||||
class NewObject : public Instruction_1<TypedRegister> {
|
||||
public:
|
||||
/* dest */
|
||||
|
@ -1132,6 +1168,7 @@ namespace VM {
|
|||
/* print() and printOperands() inherited from Arithmetic */
|
||||
};
|
||||
|
||||
|
||||
} /* namespace VM */
|
||||
|
||||
} /* namespace JavaScript */
|
||||
|
|
|
@ -159,6 +159,14 @@ TypedRegister ICodeGenerator::loadString(String &value)
|
|||
return dest;
|
||||
}
|
||||
|
||||
TypedRegister ICodeGenerator::loadString(const StringAtom &value)
|
||||
{
|
||||
TypedRegister dest(getRegister(), &String_Type);
|
||||
LoadString *instr = new LoadString(dest, new JSString(value));
|
||||
iCode->push_back(instr);
|
||||
return dest;
|
||||
}
|
||||
|
||||
TypedRegister ICodeGenerator::loadBoolean(bool value)
|
||||
{
|
||||
TypedRegister dest(getRegister(), &Boolean_Type);
|
||||
|
@ -175,6 +183,14 @@ TypedRegister ICodeGenerator::newObject()
|
|||
return dest;
|
||||
}
|
||||
|
||||
TypedRegister ICodeGenerator::newFunction(ICodeModule *icm)
|
||||
{
|
||||
TypedRegister dest(getRegister(), &Function_Type);
|
||||
NewFunction *instr = new NewFunction(dest, icm);
|
||||
iCode->push_back(instr);
|
||||
return dest;
|
||||
}
|
||||
|
||||
TypedRegister ICodeGenerator::newArray()
|
||||
{
|
||||
TypedRegister dest(getRegister(), &Array_Type);
|
||||
|
@ -183,8 +199,6 @@ TypedRegister ICodeGenerator::newArray()
|
|||
return dest;
|
||||
}
|
||||
|
||||
|
||||
|
||||
TypedRegister ICodeGenerator::loadName(const StringAtom &name)
|
||||
{
|
||||
TypedRegister dest(getRegister(), &Any_Type);
|
||||
|
@ -343,18 +357,20 @@ TypedRegister ICodeGenerator::op(ICodeOp op, TypedRegister source1,
|
|||
return dest;
|
||||
}
|
||||
|
||||
TypedRegister ICodeGenerator::call(TypedRegister target, RegisterList args)
|
||||
TypedRegister ICodeGenerator::call(TypedRegister target, const StringAtom &name, RegisterList args)
|
||||
{
|
||||
TypedRegister dest(getRegister(), &Any_Type);
|
||||
Call *instr = new Call(dest, target, args);
|
||||
Call *instr = new Call(dest, target, &name, args);
|
||||
iCode->push_back(instr);
|
||||
return dest;
|
||||
}
|
||||
|
||||
void ICodeGenerator::callVoid(TypedRegister target, RegisterList args)
|
||||
TypedRegister ICodeGenerator::methodCall(TypedRegister targetBase, TypedRegister targetValue, RegisterList args)
|
||||
{
|
||||
Call *instr = new Call(TypedRegister(NotARegister, &Void_Type), target, args);
|
||||
TypedRegister dest(getRegister(), &Any_Type);
|
||||
MethodCall *instr = new MethodCall(dest, targetBase, targetValue, args);
|
||||
iCode->push_back(instr);
|
||||
return dest;
|
||||
}
|
||||
|
||||
void ICodeGenerator::branch(Label *label)
|
||||
|
@ -514,8 +530,6 @@ static bool generatedBoolean(ExprNode *p)
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
if trueBranch OR falseBranch are not null, the sub-expression should generate
|
||||
a conditional branch to the appropriate target. If either branch is NULL, it
|
||||
|
@ -563,14 +577,36 @@ TypedRegister ICodeGenerator::genExpr(ExprNode *p,
|
|||
case ExprNode::call :
|
||||
{
|
||||
InvokeExprNode *i = static_cast<InvokeExprNode *>(p);
|
||||
TypedRegister fn = genExpr(i->op);
|
||||
RegisterList args;
|
||||
ExprPairList *p = i->pairs;
|
||||
while (p) {
|
||||
args.push_back(genExpr(p->value));
|
||||
p = p->next;
|
||||
}
|
||||
ret = call(fn, args);
|
||||
|
||||
if (i->op->getKind() == ExprNode::dot) {
|
||||
BinaryExprNode *b = static_cast<BinaryExprNode *>(i->op);
|
||||
ret = methodCall(genExpr(b->op1), loadString(static_cast<IdentifierExprNode *>(b->op2)->name), args);
|
||||
}
|
||||
else
|
||||
if (i->op->getKind() == ExprNode::identifier) {
|
||||
if (!mWithinWith) {
|
||||
TypedRegister v = findVariable((static_cast<IdentifierExprNode *>(i->op))->name);
|
||||
if (v.first != NotARegister)
|
||||
ret = call(v, static_cast<IdentifierExprNode *>(i->op)->name, args);
|
||||
else
|
||||
ret = call(TypedRegister(NotARegister, &Null_Type), static_cast<IdentifierExprNode *>(i->op)->name, args);
|
||||
}
|
||||
else
|
||||
ret = methodCall(TypedRegister(NotARegister, &Null_Type), loadString(static_cast<IdentifierExprNode *>(i->op)->name), args);
|
||||
}
|
||||
else
|
||||
if (i->op->getKind() == ExprNode::index) {
|
||||
BinaryExprNode *b = static_cast<BinaryExprNode *>(i->op);
|
||||
ret = methodCall(genExpr(b->op1), genExpr(b->op2), args);
|
||||
}
|
||||
else
|
||||
ASSERT("WAH!");
|
||||
}
|
||||
break;
|
||||
case ExprNode::index :
|
||||
|
@ -588,6 +624,11 @@ TypedRegister ICodeGenerator::genExpr(ExprNode *p,
|
|||
ret = getProperty(base, static_cast<IdentifierExprNode *>(b->op2)->name);
|
||||
}
|
||||
break;
|
||||
case ExprNode::This :
|
||||
{
|
||||
ret = TypedRegister(0, &Any_Type);
|
||||
}
|
||||
break;
|
||||
case ExprNode::identifier :
|
||||
{
|
||||
if (!mWithinWith) {
|
||||
|
@ -644,6 +685,8 @@ TypedRegister ICodeGenerator::genExpr(ExprNode *p,
|
|||
ret = op(ADD, ret, loadImmediate(1.0));
|
||||
setElement(base, index, ret);
|
||||
}
|
||||
else
|
||||
ASSERT("WAH!");
|
||||
}
|
||||
break;
|
||||
case ExprNode::postIncrement:
|
||||
|
@ -673,6 +716,8 @@ TypedRegister ICodeGenerator::genExpr(ExprNode *p,
|
|||
TypedRegister index = genExpr(b->op2);
|
||||
ret = elementInc(base, index);
|
||||
}
|
||||
else
|
||||
ASSERT("WAH!");
|
||||
}
|
||||
break;
|
||||
case ExprNode::preDecrement:
|
||||
|
@ -712,6 +757,8 @@ TypedRegister ICodeGenerator::genExpr(ExprNode *p,
|
|||
ret = op(SUBTRACT, ret, loadImmediate(1.0));
|
||||
setElement(base, index, ret);
|
||||
}
|
||||
else
|
||||
ASSERT("WAH!");
|
||||
}
|
||||
break;
|
||||
case ExprNode::postDecrement:
|
||||
|
@ -741,6 +788,8 @@ TypedRegister ICodeGenerator::genExpr(ExprNode *p,
|
|||
TypedRegister index = genExpr(b->op2);
|
||||
ret = elementInc(base, index);
|
||||
}
|
||||
else
|
||||
ASSERT("WAH!");
|
||||
}
|
||||
break;
|
||||
case ExprNode::plus:
|
||||
|
@ -798,6 +847,8 @@ TypedRegister ICodeGenerator::genExpr(ExprNode *p,
|
|||
TypedRegister index = genExpr(lb->op2);
|
||||
setElement(base, index, ret);
|
||||
}
|
||||
else
|
||||
ASSERT("WAH!");
|
||||
}
|
||||
break;
|
||||
case ExprNode::addEquals:
|
||||
|
@ -850,6 +901,8 @@ TypedRegister ICodeGenerator::genExpr(ExprNode *p,
|
|||
ret = op(mapExprNodeToICodeOp(p->getKind()), v, ret);
|
||||
setElement(base, index, ret);
|
||||
}
|
||||
else
|
||||
ASSERT("WAH!");
|
||||
}
|
||||
break;
|
||||
case ExprNode::equal:
|
||||
|
@ -988,7 +1041,6 @@ TypedRegister ICodeGenerator::genExpr(ExprNode *p,
|
|||
case ExprNode::objectLiteral:
|
||||
{
|
||||
ret = newObject();
|
||||
|
||||
PairListExprNode *plen = static_cast<PairListExprNode *>(p);
|
||||
ExprPairList *e = plen->pairs;
|
||||
while (e) {
|
||||
|
@ -999,6 +1051,25 @@ TypedRegister ICodeGenerator::genExpr(ExprNode *p,
|
|||
}
|
||||
break;
|
||||
|
||||
case ExprNode::functionLiteral:
|
||||
{
|
||||
FunctionExprNode *f = static_cast<FunctionExprNode *>(p);
|
||||
ICodeGenerator icg(mWorld, mGlobal);
|
||||
icg.allocateParameter(mWorld->identifiers[widenCString("this")]); // always parameter #0
|
||||
VariableBinding *v = f->function.parameters;
|
||||
while (v) {
|
||||
if (v->name && (v->name->getKind() == ExprNode::identifier))
|
||||
icg.allocateParameter((static_cast<IdentifierExprNode *>(v->name))->name);
|
||||
v = v->next;
|
||||
}
|
||||
icg.preprocess(f->function.body);
|
||||
icg.genStmt(f->function.body);
|
||||
//stdOut << icg;
|
||||
ICodeModule *icm = icg.complete();
|
||||
ret = newFunction(icm);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
NOT_REACHED("Unsupported ExprNode kind");
|
||||
|
@ -1044,7 +1115,7 @@ void ICodeGenerator::preprocess(StmtNode *p)
|
|||
VariableBinding *v = vs->bindings;
|
||||
while (v) {
|
||||
if (v->name && (v->name->getKind() == ExprNode::identifier))
|
||||
if (v->type->getKind() == ExprNode::identifier)
|
||||
if (v->type && (v->type->getKind() == ExprNode::identifier))
|
||||
allocateVariable((static_cast<IdentifierExprNode *>(v->name))->name,
|
||||
(static_cast<IdentifierExprNode *>(v->type))->name);
|
||||
else
|
||||
|
@ -1141,6 +1212,7 @@ TypedRegister ICodeGenerator::genStmt(StmtNode *p, LabelSet *currentLabelSet)
|
|||
{
|
||||
FunctionStmtNode *f = static_cast<FunctionStmtNode *>(p);
|
||||
ICodeGenerator icg(mWorld, mGlobal);
|
||||
icg.allocateParameter(mWorld->identifiers[widenCString("this")]); // always parameter #0
|
||||
VariableBinding *v = f->function.parameters;
|
||||
while (v) {
|
||||
if (v->name && (v->name->getKind() == ExprNode::identifier))
|
||||
|
@ -1472,6 +1544,11 @@ TypedRegister ICodeGenerator::genStmt(StmtNode *p, LabelSet *currentLabelSet)
|
|||
}
|
||||
break;
|
||||
|
||||
case StmtNode::Class:
|
||||
{
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
NOT_REACHED("unimplemented statement kind");
|
||||
}
|
||||
|
|
|
@ -219,8 +219,8 @@ namespace ICG {
|
|||
|
||||
TypedRegister op(ICodeOp op, TypedRegister source);
|
||||
TypedRegister op(ICodeOp op, TypedRegister source1, TypedRegister source2);
|
||||
TypedRegister call(TypedRegister target, RegisterList args);
|
||||
void callVoid(TypedRegister target, RegisterList args);
|
||||
TypedRegister call(TypedRegister target, const StringAtom &name, RegisterList args);
|
||||
TypedRegister methodCall(TypedRegister targetBase, TypedRegister targetValue, RegisterList args);
|
||||
|
||||
void move(TypedRegister destination, TypedRegister source);
|
||||
TypedRegister logicalNot(TypedRegister source);
|
||||
|
@ -229,9 +229,11 @@ namespace ICG {
|
|||
TypedRegister loadBoolean(bool value);
|
||||
TypedRegister loadImmediate(double value);
|
||||
TypedRegister loadString(String &value);
|
||||
TypedRegister loadString(const StringAtom &name);
|
||||
|
||||
TypedRegister newObject();
|
||||
TypedRegister newArray();
|
||||
TypedRegister newFunction(ICodeModule *icm);
|
||||
|
||||
TypedRegister loadName(const StringAtom &name);
|
||||
void saveName(const StringAtom &name, TypedRegister value);
|
||||
|
|
|
@ -89,12 +89,13 @@ struct Activation : public gc_base {
|
|||
}
|
||||
}
|
||||
|
||||
Activation(ICodeModule* iCode, Activation* caller,
|
||||
Activation(ICodeModule* iCode, Activation* caller, const JSValue thisArg,
|
||||
const RegisterList& list)
|
||||
: mRegisters(iCode->itsMaxRegister + 1), mICode(iCode)
|
||||
{
|
||||
// copy caller's parameter list to initial registers.
|
||||
JSValues::iterator dest = mRegisters.begin();
|
||||
*dest++ = thisArg;
|
||||
const JSValues& params = caller->mRegisters;
|
||||
for (RegisterList::const_iterator src = list.begin(),
|
||||
end = list.end(); src != end; ++src, ++dest) {
|
||||
|
@ -102,12 +103,6 @@ struct Activation : public gc_base {
|
|||
}
|
||||
}
|
||||
|
||||
Activation(ICodeModule* iCode, const JSValue arg)
|
||||
: mRegisters(iCode->itsMaxRegister + 1), mICode(iCode)
|
||||
{
|
||||
mRegisters[0] = arg;
|
||||
}
|
||||
|
||||
Activation(ICodeModule* iCode, const JSValue arg1, const JSValue arg2)
|
||||
: mRegisters(iCode->itsMaxRegister + 1), mICode(iCode)
|
||||
{
|
||||
|
@ -489,26 +484,78 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args)
|
|||
}
|
||||
break;
|
||||
|
||||
case CALL:
|
||||
case METHOD_CALL:
|
||||
{
|
||||
Call* call = static_cast<Call*>(instruction);
|
||||
JSFunction *target = (*registers)[op2(call).first].function;
|
||||
MethodCall* call = static_cast<MethodCall*>(instruction);
|
||||
ASSERT((*registers)[op3(call).first].isString());
|
||||
|
||||
JSValue base;
|
||||
JSValue prop;
|
||||
if (op2(call).first == NotARegister) {
|
||||
base = mGlobal->getReference(prop, *((*registers)[op3(call).first].string));
|
||||
|
||||
}
|
||||
else {
|
||||
base = (*registers)[op2(call).first];
|
||||
ASSERT(base.tag == JSValue::object_tag); // XXX runtime error
|
||||
base = base.object->getReference(prop, *((*registers)[op3(call).first].string));
|
||||
}
|
||||
ASSERT(prop.isFunction()); // XXX runtime error
|
||||
JSFunction *target = prop.function;
|
||||
if (target->isNative()) {
|
||||
RegisterList ¶ms = op3(call);
|
||||
JSValues argv(params.size());
|
||||
JSValues::size_type i = 0;
|
||||
RegisterList ¶ms = op4(call);
|
||||
JSValues argv(params.size() + 1);
|
||||
argv[0] = base;
|
||||
JSValues::size_type i = 1;
|
||||
for (RegisterList::const_iterator src = params.begin(), end = params.end();
|
||||
src != end; ++src, ++i) {
|
||||
argv[i] = (*registers)[src->first];
|
||||
}
|
||||
if (op2(call).first != NotARegister)
|
||||
(*registers)[op2(call).first] = static_cast<JSNativeFunction*>(target)->mCode(argv);
|
||||
JSValue result = static_cast<JSNativeFunction*>(target)->mCode(argv);
|
||||
if (op1(call).first != NotARegister)
|
||||
(*registers)[op1(call).first] = result;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
mLinkage = new Linkage(mLinkage, ++mPC,
|
||||
mActivation, op1(call));
|
||||
mActivation = new Activation(target->getICode(), mActivation, op3(call));
|
||||
mActivation = new Activation(target->getICode(), mActivation, base, op4(call));
|
||||
registers = &mActivation->mRegisters;
|
||||
mPC = mActivation->mICode->its_iCode->begin();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
case CALL:
|
||||
{
|
||||
Call* call = static_cast<Call*>(instruction);
|
||||
JSFunction *target;
|
||||
if (op2(call).first == NotARegister) {
|
||||
ASSERT(mGlobal->getVariable(*op3(call)).isFunction());
|
||||
target = mGlobal->getVariable(*op3(call)).function;
|
||||
}
|
||||
else {
|
||||
ASSERT((*registers)[op2(call).first].isFunction()); // XXX runtime error
|
||||
target = (*registers)[op2(call).first].function;
|
||||
}
|
||||
if (target->isNative()) {
|
||||
RegisterList ¶ms = op4(call);
|
||||
JSValues argv(params.size() + 1);
|
||||
argv[0] = kNull;
|
||||
JSValues::size_type i = 1;
|
||||
for (RegisterList::const_iterator src = params.begin(), end = params.end();
|
||||
src != end; ++src, ++i) {
|
||||
argv[i] = (*registers)[src->first];
|
||||
}
|
||||
JSValue result = static_cast<JSNativeFunction*>(target)->mCode(argv);
|
||||
if (op1(call).first != NotARegister)
|
||||
(*registers)[op1(call).first] = result;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
mLinkage = new Linkage(mLinkage, ++mPC,
|
||||
mActivation, op1(call));
|
||||
mActivation = new Activation(target->getICode(), mActivation, kNull, op4(call));
|
||||
registers = &mActivation->mRegisters;
|
||||
mPC = mActivation->mICode->its_iCode->begin();
|
||||
continue;
|
||||
|
@ -577,6 +624,12 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args)
|
|||
(*registers)[dst(no).first] = JSValue(new JSObject());
|
||||
}
|
||||
break;
|
||||
case NEW_FUNCTION:
|
||||
{
|
||||
NewFunction* nf = static_cast<NewFunction*>(instruction);
|
||||
(*registers)[dst(nf).first] = JSValue(new JSFunction(src1(nf)));
|
||||
}
|
||||
break;
|
||||
case NEW_ARRAY:
|
||||
{
|
||||
NewArray* na = static_cast<NewArray*>(instruction);
|
||||
|
@ -591,6 +644,7 @@ JSValue Context::interpret(ICodeModule* iCode, const JSValues& args)
|
|||
JSObject* object = value.object;
|
||||
(*registers)[dst(gp).first] = object->getProperty(*src2(gp));
|
||||
}
|
||||
// XXX runtime error
|
||||
}
|
||||
break;
|
||||
case SET_PROP:
|
||||
|
@ -787,6 +841,7 @@ using JSString throughout.
|
|||
(*registers)[dst(bn).first] = JSValue(~(*registers)[src1(bn).first].toInt32().i32);
|
||||
}
|
||||
break;
|
||||
|
||||
case NOT:
|
||||
{
|
||||
Not* nt = static_cast<Not*>(instruction);
|
||||
|
@ -794,6 +849,7 @@ using JSString throughout.
|
|||
(*registers)[dst(nt).first] = JSValue(!(*registers)[src1(nt).first].boolean);
|
||||
}
|
||||
break;
|
||||
|
||||
case THROW:
|
||||
{
|
||||
Throw* thrw = static_cast<Throw*>(instruction);
|
||||
|
|
|
@ -45,6 +45,7 @@ const JSValue kUndefinedValue;
|
|||
const JSValue kNaN = JSValue(nan);
|
||||
const JSValue kTrue = JSValue(true);
|
||||
const JSValue kFalse = JSValue(false);
|
||||
const JSValue kNull = JSValue((JSObject*)NULL);
|
||||
|
||||
const JSType Any_Type = JSType(NULL);
|
||||
const JSType Integer_Type = JSType(&Any_Type);
|
||||
|
@ -94,6 +95,8 @@ const JSType *JSValue::getType() const
|
|||
return &Integer_Type;
|
||||
case JSValue::u32_tag:
|
||||
return &Integer_Type;
|
||||
case JSValue::integer_tag:
|
||||
return &Integer_Type;
|
||||
case JSValue::f64_tag:
|
||||
return &Number_Type;
|
||||
case JSValue::object_tag:
|
||||
|
@ -123,6 +126,7 @@ bool JSValue::isNaN() const
|
|||
case i32_tag:
|
||||
case u32_tag:
|
||||
return false;
|
||||
case integer_tag:
|
||||
case f64_tag:
|
||||
return JSDOUBLE_IS_NaN(f64);
|
||||
default:
|
||||
|
@ -143,6 +147,7 @@ int JSValue::operator==(const JSValue& value) const
|
|||
CASE(object); CASE(array); CASE(function); CASE(string);
|
||||
CASE(boolean);
|
||||
#undef CASE
|
||||
case integer_tag : return (this->f64 == value.f64);
|
||||
// question: are all undefined values equal to one another?
|
||||
case undefined_tag: return 1;
|
||||
}
|
||||
|
@ -173,6 +178,7 @@ Formatter& operator<<(Formatter& f, const JSValue& value)
|
|||
case JSValue::u32_tag:
|
||||
f << float64(value.u32);
|
||||
break;
|
||||
case JSValue::integer_tag:
|
||||
case JSValue::f64_tag:
|
||||
f << value.f64;
|
||||
break;
|
||||
|
@ -197,6 +203,34 @@ Formatter& operator<<(Formatter& f, const JSValue& value)
|
|||
case JSValue::undefined_tag:
|
||||
f << "undefined";
|
||||
break;
|
||||
case JSValue::type_tag:
|
||||
if (value.type == &Any_Type) // yuck (see yuck comment below)
|
||||
f << "Any_Type";
|
||||
else if (value.type == &Integer_Type)
|
||||
f << "Integer_Type";
|
||||
else if (value.type == &Number_Type)
|
||||
f << "Number_Type";
|
||||
else if (value.type == &Character_Type)
|
||||
f << "Character_Type";
|
||||
else if (value.type == &String_Type)
|
||||
f << "String_Type";
|
||||
else if (value.type == &Function_Type)
|
||||
f << "Function_Type";
|
||||
else if (value.type == &Array_Type)
|
||||
f << "Array_Type";
|
||||
else if (value.type == &Type_Type)
|
||||
f << "Type_Type";
|
||||
else if (value.type == &Boolean_Type)
|
||||
f << "Boolean_Type";
|
||||
else if (value.type == &Null_Type)
|
||||
f << "Null_Type";
|
||||
else if (value.type == &Void_Type)
|
||||
f << "Void_Type";
|
||||
else if (value.type == &None_Type)
|
||||
f << "None_Type";
|
||||
else
|
||||
f << "Unknown_Type";
|
||||
break;
|
||||
default:
|
||||
NOT_REACHED("Bad tag");
|
||||
}
|
||||
|
@ -209,6 +243,7 @@ JSValue JSValue::toPrimitive(ECMA_type /*hint*/) const
|
|||
switch (tag) {
|
||||
case i32_tag:
|
||||
case u32_tag:
|
||||
case integer_tag:
|
||||
case f64_tag:
|
||||
case string_tag:
|
||||
case boolean_tag:
|
||||
|
@ -258,6 +293,7 @@ JSValue JSValue::valueToString(const JSValue& value) // can assume value is not
|
|||
case u32_tag:
|
||||
chrp = doubleToStr(buf, dtosStandardBufferSize, value.u32, dtosStandard, 0);
|
||||
break;
|
||||
case integer_tag:
|
||||
case f64_tag:
|
||||
chrp = doubleToStr(buf, dtosStandardBufferSize, value.f64, dtosStandard, 0);
|
||||
break;
|
||||
|
@ -291,6 +327,7 @@ JSValue JSValue::valueToNumber(const JSValue& value) // can assume value is not
|
|||
return JSValue((float64)value.i32);
|
||||
case u32_tag:
|
||||
return JSValue((float64)value.u32);
|
||||
case integer_tag:
|
||||
case f64_tag:
|
||||
return value;
|
||||
case string_tag:
|
||||
|
@ -316,6 +353,18 @@ JSValue JSValue::valueToNumber(const JSValue& value) // can assume value is not
|
|||
}
|
||||
}
|
||||
|
||||
JSValue JSValue::valueToInteger(const JSValue& value)
|
||||
{
|
||||
JSValue result = valueToNumber(value);
|
||||
ASSERT(result.tag == f64_tag);
|
||||
result.tag = i64_tag;
|
||||
bool neg = (result.f64 < 0);
|
||||
result.f64 = floor((neg) ? -result.f64 : result.f64);
|
||||
result.f64 = (neg) ? -result.f64 : result.f64;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
JSValue JSValue::valueToBoolean(const JSValue& value)
|
||||
{
|
||||
switch (value.tag) {
|
||||
|
@ -323,6 +372,7 @@ JSValue JSValue::valueToBoolean(const JSValue& value)
|
|||
return JSValue(value.i32 != 0);
|
||||
case u32_tag:
|
||||
return JSValue(value.u32 != 0);
|
||||
case integer_tag:
|
||||
case f64_tag:
|
||||
return JSValue(!(value.f64 == 0.0) || JSDOUBLE_IS_NaN(value.f64));
|
||||
case string_tag:
|
||||
|
@ -354,6 +404,7 @@ JSValue JSValue::valueToInt32(const JSValue& value)
|
|||
case u32_tag:
|
||||
d = value.u32;
|
||||
break;
|
||||
case integer_tag:
|
||||
case f64_tag:
|
||||
d = value.f64;
|
||||
break;
|
||||
|
@ -425,6 +476,22 @@ JSValue JSValue::valueToUInt32(const JSValue& value)
|
|||
return JSValue((uint32)d);
|
||||
}
|
||||
|
||||
|
||||
JSValue JSValue::convert(const JSType *toType)
|
||||
{
|
||||
if (toType == &Any_Type) // yuck, something wrong with this
|
||||
// maybe the base types should be
|
||||
// a family of classes, not just instances
|
||||
// of JSType ???
|
||||
return *this;
|
||||
else if (toType == &Integer_Type)
|
||||
return valueToInteger(*this);
|
||||
else
|
||||
// etc...
|
||||
return kUndefinedValue;
|
||||
}
|
||||
|
||||
|
||||
JSFunction::~JSFunction()
|
||||
{
|
||||
delete mICode;
|
||||
|
@ -451,6 +518,13 @@ JSString::JSString(const char* str)
|
|||
std::transform(str, str + n, begin(), JavaScript::widen);
|
||||
}
|
||||
|
||||
|
||||
JSString::operator String()
|
||||
{
|
||||
return String(begin(), size());
|
||||
}
|
||||
|
||||
|
||||
// # of sub-type relationship between this type and the other type
|
||||
// (== MAX_INT if other is not a base type)
|
||||
|
||||
|
|
|
@ -93,6 +93,7 @@ namespace JSTypes {
|
|||
i32_tag, u32_tag,
|
||||
i64_tag, u64_tag,
|
||||
f32_tag, f64_tag,
|
||||
integer_tag,
|
||||
object_tag, array_tag, function_tag, string_tag, boolean_tag, type_tag,
|
||||
undefined_tag
|
||||
} tag;
|
||||
|
@ -122,10 +123,12 @@ namespace JSTypes {
|
|||
bool isObject() const { return ((tag == object_tag) || (tag == function_tag) || (tag == array_tag)); }
|
||||
bool isString() const { return (tag == string_tag); }
|
||||
bool isBoolean() const { return (tag == boolean_tag); }
|
||||
bool isNumber() const { return (tag == f64_tag); }
|
||||
bool isNumber() const { return (tag == f64_tag) || (tag == integer_tag); }
|
||||
|
||||
/* this is correct wrt ECMA, The i32 & u32 kinds
|
||||
will have to be converted to doubles anyway because
|
||||
will have to be converted (to doubles?) anyway because
|
||||
we can't have overflow happening in generic arithmetic */
|
||||
|
||||
bool isUndefined() const { return (tag == undefined_tag); }
|
||||
bool isNull() const { return ((tag == object_tag) && (this->object == NULL)); }
|
||||
bool isNaN() const;
|
||||
|
@ -139,9 +142,12 @@ namespace JSTypes {
|
|||
|
||||
JSValue toPrimitive(ECMA_type hint = NoHint) const;
|
||||
|
||||
JSValue convert(const JSType *toType);
|
||||
|
||||
|
||||
static JSValue valueToString(const JSValue& value);
|
||||
static JSValue valueToNumber(const JSValue& value);
|
||||
static JSValue valueToInteger(const JSValue& value);
|
||||
static JSValue valueToInt32(const JSValue& value);
|
||||
static JSValue valueToUInt32(const JSValue& value);
|
||||
static JSValue valueToBoolean(const JSValue& value);
|
||||
|
@ -176,6 +182,20 @@ namespace JSTypes {
|
|||
extern const JSValue kNaN;
|
||||
extern const JSValue kTrue;
|
||||
extern const JSValue kFalse;
|
||||
extern const JSValue kNull;
|
||||
|
||||
extern const JSType Any_Type;
|
||||
extern const JSType Integer_Type;
|
||||
extern const JSType Number_Type;
|
||||
extern const JSType Character_Type;
|
||||
extern const JSType String_Type;
|
||||
extern const JSType Function_Type;
|
||||
extern const JSType Array_Type;
|
||||
extern const JSType Type_Type;
|
||||
extern const JSType Boolean_Type;
|
||||
extern const JSType Null_Type;
|
||||
extern const JSType Void_Type;
|
||||
extern const JSType None_Type;
|
||||
|
||||
/**
|
||||
* Basic behavior of all JS objects, mapping a name to a value,
|
||||
|
@ -193,7 +213,7 @@ namespace JSTypes {
|
|||
{
|
||||
return (mProperties.count(name) != 0);
|
||||
}
|
||||
|
||||
|
||||
const JSValue& getProperty(const String& name)
|
||||
{
|
||||
JSProperties::const_iterator i = mProperties.find(name);
|
||||
|
@ -203,6 +223,20 @@ namespace JSTypes {
|
|||
return mPrototype->getProperty(name);
|
||||
return kUndefinedValue;
|
||||
}
|
||||
|
||||
// return the property AND the object it's found in
|
||||
// (would rather return references, but couldn't get that to work)
|
||||
const JSValue getReference(JSValue &prop, const String& name)
|
||||
{
|
||||
JSProperties::const_iterator i = mProperties.find(name);
|
||||
if (i != mProperties.end()) {
|
||||
prop = i->second;
|
||||
return JSValue(this);
|
||||
}
|
||||
if (mPrototype)
|
||||
return mPrototype->getReference(prop, name);
|
||||
return kUndefinedValue;
|
||||
}
|
||||
|
||||
JSValue& setProperty(const String& name, const JSValue& value)
|
||||
{
|
||||
|
@ -329,6 +363,8 @@ namespace JSTypes {
|
|||
explicit JSString(const String& str);
|
||||
explicit JSString(const String* str);
|
||||
explicit JSString(const char* str);
|
||||
|
||||
operator String();
|
||||
};
|
||||
|
||||
class JSException : public gc_base {
|
||||
|
@ -413,20 +449,6 @@ namespace JSTypes {
|
|||
int32 distance(const JSType *other) const;
|
||||
};
|
||||
|
||||
|
||||
extern const JSType Any_Type;
|
||||
extern const JSType Integer_Type;
|
||||
extern const JSType Number_Type;
|
||||
extern const JSType Character_Type;
|
||||
extern const JSType String_Type;
|
||||
extern const JSType Function_Type;
|
||||
extern const JSType Array_Type;
|
||||
extern const JSType Type_Type;
|
||||
extern const JSType Boolean_Type;
|
||||
extern const JSType Null_Type;
|
||||
extern const JSType Void_Type;
|
||||
extern const JSType None_Type;
|
||||
|
||||
|
||||
} /* namespace JSTypes */
|
||||
} /* namespace JavaScript */
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace VM {
|
|||
BRANCH, /* target label */
|
||||
BRANCH_FALSE, /* target label, condition */
|
||||
BRANCH_TRUE, /* target label, condition */
|
||||
CALL, /* result, target, args */
|
||||
CALL, /* result, target, name, args */
|
||||
COMPARE_EQ, /* dest, source1, source2 */
|
||||
COMPARE_GE, /* dest, source1, source2 */
|
||||
COMPARE_GT, /* dest, source1, source2 */
|
||||
|
@ -77,11 +77,13 @@ namespace VM {
|
|||
LOAD_IMMEDIATE, /* dest, immediate value (double) */
|
||||
LOAD_NAME, /* dest, name */
|
||||
LOAD_STRING, /* dest, immediate value (string) */
|
||||
METHOD_CALL, /* result, target base, target value, args */
|
||||
MOVE, /* dest, source */
|
||||
MULTIPLY, /* dest, source1, source2 */
|
||||
NAME_XCR, /* dest, name, value */
|
||||
NEGATE, /* dest, source */
|
||||
NEW_ARRAY, /* dest */
|
||||
NEW_FUNCTION, /* dest, ICodeModule */
|
||||
NEW_OBJECT, /* dest */
|
||||
NOP, /* do nothing and like it */
|
||||
NOT, /* dest, source */
|
||||
|
@ -139,11 +141,13 @@ namespace VM {
|
|||
"LOAD_IMMEDIATE",
|
||||
"LOAD_NAME ",
|
||||
"LOAD_STRING ",
|
||||
"METHOD_CALL ",
|
||||
"MOVE ",
|
||||
"MULTIPLY ",
|
||||
"NAME_XCR ",
|
||||
"NEGATE ",
|
||||
"NEW_ARRAY ",
|
||||
"NEW_FUNCTION ",
|
||||
"NEW_OBJECT ",
|
||||
"NOP ",
|
||||
"NOT ",
|
||||
|
@ -472,18 +476,18 @@ namespace VM {
|
|||
/* print() and printOperands() inherited from GenericBranch */
|
||||
};
|
||||
|
||||
class Call : public Instruction_3<TypedRegister, TypedRegister, RegisterList> {
|
||||
class Call : public Instruction_4<TypedRegister, TypedRegister, const StringAtom*, RegisterList> {
|
||||
public:
|
||||
/* result, target, args */
|
||||
Call (TypedRegister aOp1, TypedRegister aOp2, RegisterList aOp3) :
|
||||
Instruction_3<TypedRegister, TypedRegister, RegisterList>
|
||||
(CALL, aOp1, aOp2, aOp3) {};
|
||||
/* result, target, name, args */
|
||||
Call (TypedRegister aOp1, TypedRegister aOp2, const StringAtom* aOp3, RegisterList aOp4) :
|
||||
Instruction_4<TypedRegister, TypedRegister, const StringAtom*, RegisterList>
|
||||
(CALL, aOp1, aOp2, aOp3, aOp4) {};
|
||||
virtual Formatter& print(Formatter& f) {
|
||||
f << opcodeNames[CALL] << "\t" << "R" << mOp1.first << ", " << "R" << mOp2.first << ", " << mOp3;
|
||||
f << opcodeNames[CALL] << "\t" << "R" << mOp1.first << ", " << "R" << mOp2.first << ", " << "'" << *mOp3 << "'" << ", " << mOp4;
|
||||
return f;
|
||||
}
|
||||
virtual Formatter& printOperands(Formatter& f, const JSValues& registers) {
|
||||
f << "R" << mOp1.first << '=' << registers[mOp1.first] << ", " << "R" << mOp2.first << '=' << registers[mOp2.first] << ", " << ArgList(mOp3, registers);
|
||||
f << "R" << mOp1.first << '=' << registers[mOp1.first] << ", " << "R" << mOp2.first << '=' << registers[mOp2.first] << ", " << ArgList(mOp4, registers);
|
||||
return f;
|
||||
}
|
||||
};
|
||||
|
@ -696,6 +700,22 @@ namespace VM {
|
|||
}
|
||||
};
|
||||
|
||||
class MethodCall : public Instruction_4<TypedRegister, TypedRegister, TypedRegister, RegisterList> {
|
||||
public:
|
||||
/* result, target base, target value, args */
|
||||
MethodCall (TypedRegister aOp1, TypedRegister aOp2, TypedRegister aOp3, RegisterList aOp4) :
|
||||
Instruction_4<TypedRegister, TypedRegister, TypedRegister, RegisterList>
|
||||
(METHOD_CALL, aOp1, aOp2, aOp3, aOp4) {};
|
||||
virtual Formatter& print(Formatter& f) {
|
||||
f << opcodeNames[METHOD_CALL] << "\t" << "R" << mOp1.first << ", " << "R" << mOp2.first << ", " << "R" << mOp3.first << ", " << mOp4;
|
||||
return f;
|
||||
}
|
||||
virtual Formatter& printOperands(Formatter& f, const JSValues& registers) {
|
||||
f << "R" << mOp1.first << '=' << registers[mOp1.first] << ", " << "R" << mOp2.first << '=' << registers[mOp2.first] << ", " << "R" << mOp3.first << '=' << registers[mOp3.first] << ", " << ArgList(mOp4, registers);
|
||||
return f;
|
||||
}
|
||||
};
|
||||
|
||||
class Move : public Instruction_2<TypedRegister, TypedRegister> {
|
||||
public:
|
||||
/* dest, source */
|
||||
|
@ -769,6 +789,22 @@ namespace VM {
|
|||
}
|
||||
};
|
||||
|
||||
class NewFunction : public Instruction_2<TypedRegister, ICodeModule *> {
|
||||
public:
|
||||
/* dest, ICodeModule */
|
||||
NewFunction (TypedRegister aOp1, ICodeModule * aOp2) :
|
||||
Instruction_2<TypedRegister, ICodeModule *>
|
||||
(NEW_FUNCTION, aOp1, aOp2) {};
|
||||
virtual Formatter& print(Formatter& f) {
|
||||
f << opcodeNames[NEW_FUNCTION] << "\t" << "R" << mOp1.first << ", " << "ICodeModule";
|
||||
return f;
|
||||
}
|
||||
virtual Formatter& printOperands(Formatter& f, const JSValues& registers) {
|
||||
f << "R" << mOp1.first << '=' << registers[mOp1.first];
|
||||
return f;
|
||||
}
|
||||
};
|
||||
|
||||
class NewObject : public Instruction_1<TypedRegister> {
|
||||
public:
|
||||
/* dest */
|
||||
|
@ -1132,6 +1168,7 @@ namespace VM {
|
|||
/* print() and printOperands() inherited from Arithmetic */
|
||||
};
|
||||
|
||||
|
||||
} /* namespace VM */
|
||||
|
||||
} /* namespace JavaScript */
|
||||
|
|
|
@ -89,9 +89,9 @@ const bool showTokens = false;
|
|||
static JSValue print(const JSValues &argv)
|
||||
{
|
||||
size_t n = argv.size();
|
||||
if (n > 0) {
|
||||
stdOut << argv[0];
|
||||
for (size_t i = 1; i < n; ++i)
|
||||
if (n > 1) { // the 'this' parameter in un-interesting
|
||||
stdOut << argv[1];
|
||||
for (size_t i = 2; i < n; ++i)
|
||||
stdOut << ' ' << argv[i];
|
||||
}
|
||||
stdOut << "\n";
|
||||
|
@ -99,13 +99,13 @@ static JSValue print(const JSValues &argv)
|
|||
}
|
||||
|
||||
|
||||
static void genCode(World &world, Context &cx, StmtNode *p)
|
||||
static void genCode(Context &cx, StmtNode *p)
|
||||
{
|
||||
JSScope glob;
|
||||
ICodeGenerator icg(&world, &glob);
|
||||
ICodeGenerator icg(&cx.getWorld(), cx.getGlobalObject());
|
||||
icg.isScript();
|
||||
TypedRegister ret(NotARegister, &None_Type);
|
||||
while (p) {
|
||||
icg.preprocess(p);
|
||||
ret = icg.genStmt(p);
|
||||
p = p->next;
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ static void readEvalPrint(FILE *in, World &world)
|
|||
stdOut << '\n';
|
||||
#if 0
|
||||
// Generate code for parsedStatements, which is a linked list of zero or more statements
|
||||
genCode(world, cx, parsedStatements);
|
||||
genCode(cx, parsedStatements);
|
||||
#endif
|
||||
}
|
||||
clear(buffer);
|
||||
|
@ -232,9 +232,15 @@ void testCompile()
|
|||
JSScope glob;
|
||||
ICodeGenerator icg(&world, &glob);
|
||||
icg.isScript();
|
||||
while (parsedStatements) {
|
||||
icg.genStmt(parsedStatements);
|
||||
parsedStatements = parsedStatements->next;
|
||||
StmtNode *s = parsedStatements;
|
||||
while (s) {
|
||||
icg.preprocess(s);
|
||||
s = s->next;
|
||||
}
|
||||
s = parsedStatements;
|
||||
while (s) {
|
||||
icg.genStmt(s);
|
||||
s = s->next;
|
||||
}
|
||||
cx.interpret(icg.complete(), JSValues());
|
||||
}
|
||||
|
@ -253,7 +259,7 @@ int main(int argc, char **argv)
|
|||
#endif
|
||||
using namespace JavaScript;
|
||||
using namespace Shell;
|
||||
#if 0
|
||||
#if 1
|
||||
testCompile();
|
||||
#endif
|
||||
readEvalPrint(stdin, world);
|
||||
|
|
|
@ -115,6 +115,12 @@ $ops{"NEW_OBJECT"} =
|
|||
rem => "dest",
|
||||
params => [ ("TypedRegister") ]
|
||||
};
|
||||
$ops{"NEW_FUNCTION"} =
|
||||
{
|
||||
super => "Instruction_2",
|
||||
rem => "dest, ICodeModule",
|
||||
params => [ ("TypedRegister", "ICodeModule *") ]
|
||||
};
|
||||
$ops{"NEW_ARRAY"} =
|
||||
{
|
||||
super => "Instruction_1",
|
||||
|
@ -216,9 +222,15 @@ $ops{"RETURN_VOID"} =
|
|||
};
|
||||
$ops{"CALL"} =
|
||||
{
|
||||
super => "Instruction_3",
|
||||
rem => "result, target, args",
|
||||
params => [ ("TypedRegister" , "TypedRegister", "RegisterList") ]
|
||||
super => "Instruction_4",
|
||||
rem => "result, target, name, args",
|
||||
params => [ ("TypedRegister" , "TypedRegister", "const StringAtom*", "RegisterList") ]
|
||||
};
|
||||
$ops{"METHOD_CALL"} =
|
||||
{
|
||||
super => "Instruction_4",
|
||||
rem => "result, target base, target value, args",
|
||||
params => [ ("TypedRegister" , "TypedRegister" , "TypedRegister", "RegisterList") ]
|
||||
};
|
||||
$ops{"THROW"} =
|
||||
{
|
||||
|
@ -452,6 +464,8 @@ sub get_print_body {
|
|||
push (@oplist, "\"'\" << *mOp$op << \"'\"");
|
||||
} elsif ($type =~ /bool/) {
|
||||
push (@oplist, "\"'\" << ((mOp$op) ? \"true\" : \"false\") << \"'\"");
|
||||
} elsif ($type =~ /ICodeModule/) {
|
||||
push (@oplist, "\"ICodeModule\"");
|
||||
} else {
|
||||
push (@oplist, "mOp$op");
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче