diff --git a/js/js2/icodegenerator.cpp b/js/js2/icodegenerator.cpp index df7d0ccaffa..d7219084f43 100644 --- a/js/js2/icodegenerator.cpp +++ b/js/js2/icodegenerator.cpp @@ -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(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(i->op); + ret = methodCall(genExpr(b->op1), loadString(static_cast(b->op2)->name), args); + } + else + if (i->op->getKind() == ExprNode::identifier) { + if (!mWithinWith) { + TypedRegister v = findVariable((static_cast(i->op))->name); + if (v.first != NotARegister) + ret = call(v, static_cast(i->op)->name, args); + else + ret = call(TypedRegister(NotARegister, &Null_Type), static_cast(i->op)->name, args); + } + else + ret = methodCall(TypedRegister(NotARegister, &Null_Type), loadString(static_cast(i->op)->name), args); + } + else + if (i->op->getKind() == ExprNode::index) { + BinaryExprNode *b = static_cast(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(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(p); ExprPairList *e = plen->pairs; while (e) { @@ -999,6 +1051,25 @@ TypedRegister ICodeGenerator::genExpr(ExprNode *p, } break; + case ExprNode::functionLiteral: + { + FunctionExprNode *f = static_cast(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(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(v->name))->name, (static_cast(v->type))->name); else @@ -1141,6 +1212,7 @@ TypedRegister ICodeGenerator::genStmt(StmtNode *p, LabelSet *currentLabelSet) { FunctionStmtNode *f = static_cast(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"); } diff --git a/js/js2/icodegenerator.h b/js/js2/icodegenerator.h index 82c3046a59c..44e5a5a63fe 100644 --- a/js/js2/icodegenerator.h +++ b/js/js2/icodegenerator.h @@ -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); diff --git a/js/js2/interpreter.cpp b/js/js2/interpreter.cpp index b3e3bd54f0b..b4ced189003 100644 --- a/js/js2/interpreter.cpp +++ b/js/js2/interpreter.cpp @@ -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(instruction); - JSFunction *target = (*registers)[op2(call).first].function; + MethodCall* call = static_cast(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(target)->mCode(argv); + JSValue result = static_cast(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(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(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(instruction); + (*registers)[dst(nf).first] = JSValue(new JSFunction(src1(nf))); + } + break; case NEW_ARRAY: { NewArray* na = static_cast(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(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(instruction); diff --git a/js/js2/js2.cpp b/js/js2/js2.cpp index e97b01f0f44..f26823ef529 100644 --- a/js/js2/js2.cpp +++ b/js/js2/js2.cpp @@ -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); diff --git a/js/js2/jstypes.cpp b/js/js2/jstypes.cpp index 8ff86330c24..283c7ff99c7 100644 --- a/js/js2/jstypes.cpp +++ b/js/js2/jstypes.cpp @@ -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) diff --git a/js/js2/jstypes.h b/js/js2/jstypes.h index a232bf19183..68446a05b30 100644 --- a/js/js2/jstypes.h +++ b/js/js2/jstypes.h @@ -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 */ diff --git a/js/js2/tools/gencode.pl b/js/js2/tools/gencode.pl index 2e7868ab746..0ab8cf64708 100644 --- a/js/js2/tools/gencode.pl +++ b/js/js2/tools/gencode.pl @@ -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"); } diff --git a/js/js2/vmtypes.h b/js/js2/vmtypes.h index 295ea7f20d3..d354dda1898 100644 --- a/js/js2/vmtypes.h +++ b/js/js2/vmtypes.h @@ -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 { + class Call : public Instruction_4 { public: - /* result, target, args */ - Call (TypedRegister aOp1, TypedRegister aOp2, RegisterList aOp3) : - Instruction_3 - (CALL, aOp1, aOp2, aOp3) {}; + /* result, target, name, args */ + Call (TypedRegister aOp1, TypedRegister aOp2, const StringAtom* aOp3, RegisterList aOp4) : + Instruction_4 + (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 { + public: + /* result, target base, target value, args */ + MethodCall (TypedRegister aOp1, TypedRegister aOp2, TypedRegister aOp3, RegisterList aOp4) : + Instruction_4 + (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 { public: /* dest, source */ @@ -769,6 +789,22 @@ namespace VM { } }; + class NewFunction : public Instruction_2 { + public: + /* dest, ICodeModule */ + NewFunction (TypedRegister aOp1, ICodeModule * aOp2) : + Instruction_2 + (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 { public: /* dest */ @@ -1132,6 +1168,7 @@ namespace VM { /* print() and printOperands() inherited from Arithmetic */ }; + } /* namespace VM */ } /* namespace JavaScript */ diff --git a/js2/src/icodegenerator.cpp b/js2/src/icodegenerator.cpp index df7d0ccaffa..d7219084f43 100644 --- a/js2/src/icodegenerator.cpp +++ b/js2/src/icodegenerator.cpp @@ -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(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(i->op); + ret = methodCall(genExpr(b->op1), loadString(static_cast(b->op2)->name), args); + } + else + if (i->op->getKind() == ExprNode::identifier) { + if (!mWithinWith) { + TypedRegister v = findVariable((static_cast(i->op))->name); + if (v.first != NotARegister) + ret = call(v, static_cast(i->op)->name, args); + else + ret = call(TypedRegister(NotARegister, &Null_Type), static_cast(i->op)->name, args); + } + else + ret = methodCall(TypedRegister(NotARegister, &Null_Type), loadString(static_cast(i->op)->name), args); + } + else + if (i->op->getKind() == ExprNode::index) { + BinaryExprNode *b = static_cast(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(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(p); ExprPairList *e = plen->pairs; while (e) { @@ -999,6 +1051,25 @@ TypedRegister ICodeGenerator::genExpr(ExprNode *p, } break; + case ExprNode::functionLiteral: + { + FunctionExprNode *f = static_cast(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(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(v->name))->name, (static_cast(v->type))->name); else @@ -1141,6 +1212,7 @@ TypedRegister ICodeGenerator::genStmt(StmtNode *p, LabelSet *currentLabelSet) { FunctionStmtNode *f = static_cast(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"); } diff --git a/js2/src/icodegenerator.h b/js2/src/icodegenerator.h index 82c3046a59c..44e5a5a63fe 100644 --- a/js2/src/icodegenerator.h +++ b/js2/src/icodegenerator.h @@ -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); diff --git a/js2/src/interpreter.cpp b/js2/src/interpreter.cpp index b3e3bd54f0b..b4ced189003 100644 --- a/js2/src/interpreter.cpp +++ b/js2/src/interpreter.cpp @@ -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(instruction); - JSFunction *target = (*registers)[op2(call).first].function; + MethodCall* call = static_cast(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(target)->mCode(argv); + JSValue result = static_cast(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(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(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(instruction); + (*registers)[dst(nf).first] = JSValue(new JSFunction(src1(nf))); + } + break; case NEW_ARRAY: { NewArray* na = static_cast(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(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(instruction); diff --git a/js2/src/jstypes.cpp b/js2/src/jstypes.cpp index 8ff86330c24..283c7ff99c7 100644 --- a/js2/src/jstypes.cpp +++ b/js2/src/jstypes.cpp @@ -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) diff --git a/js2/src/jstypes.h b/js2/src/jstypes.h index a232bf19183..68446a05b30 100644 --- a/js2/src/jstypes.h +++ b/js2/src/jstypes.h @@ -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 */ diff --git a/js2/src/vmtypes.h b/js2/src/vmtypes.h index 295ea7f20d3..d354dda1898 100644 --- a/js2/src/vmtypes.h +++ b/js2/src/vmtypes.h @@ -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 { + class Call : public Instruction_4 { public: - /* result, target, args */ - Call (TypedRegister aOp1, TypedRegister aOp2, RegisterList aOp3) : - Instruction_3 - (CALL, aOp1, aOp2, aOp3) {}; + /* result, target, name, args */ + Call (TypedRegister aOp1, TypedRegister aOp2, const StringAtom* aOp3, RegisterList aOp4) : + Instruction_4 + (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 { + public: + /* result, target base, target value, args */ + MethodCall (TypedRegister aOp1, TypedRegister aOp2, TypedRegister aOp3, RegisterList aOp4) : + Instruction_4 + (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 { public: /* dest, source */ @@ -769,6 +789,22 @@ namespace VM { } }; + class NewFunction : public Instruction_2 { + public: + /* dest, ICodeModule */ + NewFunction (TypedRegister aOp1, ICodeModule * aOp2) : + Instruction_2 + (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 { public: /* dest */ @@ -1132,6 +1168,7 @@ namespace VM { /* print() and printOperands() inherited from Arithmetic */ }; + } /* namespace VM */ } /* namespace JavaScript */ diff --git a/js2/tests/cpp/js2_shell.cpp b/js2/tests/cpp/js2_shell.cpp index e97b01f0f44..f26823ef529 100644 --- a/js2/tests/cpp/js2_shell.cpp +++ b/js2/tests/cpp/js2_shell.cpp @@ -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); diff --git a/js2/tools/gencode.pl b/js2/tools/gencode.pl index 2e7868ab746..0ab8cf64708 100644 --- a/js2/tools/gencode.pl +++ b/js2/tools/gencode.pl @@ -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"); }