From 35c3192b275d1c07e2f5a959cf3589b1e7d76023 Mon Sep 17 00:00:00 2001 From: "rogerl%netscape.com" Date: Mon, 16 Sep 2002 06:33:47 +0000 Subject: [PATCH] Arithmetic. Global properties. --- js2/src/bytecodecontainer.h | 2 +- js2/src/js2engine.cpp | 60 ++++++++- js2/src/js2engine.h | 19 ++- js2/src/js2metadata.cpp | 238 ++++++++++++++++++++++++++++++---- js2/src/js2metadata.h | 58 ++++----- js2/src/js2op_access.cpp | 9 ++ js2/src/js2op_arithmetic.cpp | 159 ++++++++++++++++++++++- js2/src/js2op_flowcontrol.cpp | 12 ++ js2/src/js2op_invocation.cpp | 65 ++++++++-- js2/src/numerics.h | 4 + 10 files changed, 550 insertions(+), 76 deletions(-) diff --git a/js2/src/bytecodecontainer.h b/js2/src/bytecodecontainer.h index bd4993d18609..e03724339171 100644 --- a/js2/src/bytecodecontainer.h +++ b/js2/src/bytecodecontainer.h @@ -115,7 +115,7 @@ public: void addPointer(const void *v) { ASSERT(sizeof(void *) == sizeof(uint32)); addLong((uint32)(v)); } static void *getPointer(void *pc) { return (void *)getLong(pc); } - void addFloat64(float64 v) { mBuffer.insert(mBuffer.end(), (uint8 *)&v, (uint8 *)(&v) + sizeof(float64)); } + void addFloat64(float64 v, size_t pos) { emitOp(eNumber, pos); mBuffer.insert(mBuffer.end(), (uint8 *)&v, (uint8 *)(&v) + sizeof(float64)); } static float64 getFloat64(void *pc) { return *((float64 *)pc); } void addLong(const uint32 v) { mBuffer.insert(mBuffer.end(), (uint8 *)&v, (uint8 *)(&v) + sizeof(uint32)); } diff --git a/js2/src/js2engine.cpp b/js2/src/js2engine.cpp index 7cc797d8b82b..02dadcce6d64 100644 --- a/js2/src/js2engine.cpp +++ b/js2/src/js2engine.cpp @@ -49,6 +49,7 @@ #include "utilities.h" #include "js2value.h" #include "numerics.h" +#include "fdlibm_ns.h" #include #include @@ -62,6 +63,7 @@ namespace JavaScript { namespace MetaData { + // Begin execution of a bytecodeContainer js2val JS2Engine::interpret(JS2Metadata *metadata, Phase execPhase, BytecodeContainer *targetbCon) { bCon = targetbCon; @@ -71,6 +73,7 @@ namespace MetaData { return interpreterLoop(); } + // Execute the opcode sequence at pc. js2val JS2Engine::interpreterLoop() { js2val retval = JS2VAL_VOID; @@ -83,7 +86,7 @@ namespace MetaData { #include "js2op_literal.cpp" #include "js2op_flowcontrol.cpp" } - JS2Object::gc(meta); + JS2Object::gc(meta); // XXX temporarily, for testing } return retval; } @@ -249,6 +252,9 @@ namespace MetaData { activationStackTop = activationStack = new ActivationFrame[MAX_ACTIVATION_STACK]; } + // Return the effect of an opcode on the execution stack. + // Some ops (e.g. eCall) have a variable effect, those are handled separately + // (see emitOp) int getStackEffect(JS2Op op) { switch (op) { @@ -257,19 +263,31 @@ namespace MetaData { case eAdd: // pop two, push one case eSubtract: + case eMultiply: + case eDivide: + case eModulo: case eEqual: case eNotEqual: case eLess: case eGreater: case eLessEqual: case eGreaterEqual: + case eXor: + case eLogicalXor: return -1; + case eMinus: // pop one, push one + case ePlus: + case eComplement: + case eTypeof: + return 0; + case eString: case eTrue: case eFalse: case eNumber: case eNull: + case eThis: return 1; // push literal value case eLexicalRead: @@ -297,11 +315,17 @@ namespace MetaData { case eBranch: return 0; + case eDup: // duplicate top item + return 1; + + case ePop: // remove top item + return -1; + case ePopv: // pop a statement result value return -1; - // case eToBoolean: // pop object, push boolean - // return 0; + case eToBoolean: // pop object, push boolean + return 0; case ePushFrame: // affect the frame stack... case ePopFrame: // ...not the exec stack @@ -320,29 +344,41 @@ namespace MetaData { case eLexicalPreDec: return 1; // push the new/old value + case eLexicalAssignOp: // pop value, push result + return 0; + case eDotPostInc: case eDotPostDec: case eDotPreInc: case eDotPreDec: return 0; // pop the base, push the new/old value + case eDotAssignOp: // pop base, pop value, push result + return -1; + case eBracketPostInc: case eBracketPostDec: case eBracketPreInc: case eBracketPreDec: return -1; // pop the base, pop the index, push the new/old value + case eBracketAssignOp: // pop base, pop index, push result + return 0; + default: ASSERT(false); } return 0; } - + + // Return the mapped source location for the current pc size_t JS2Engine::errorPos() { return bCon->getPosition(pc - bCon->getCodeStart()); } + // XXX Default construction of an instance of the class + // that is the value at the top of the execution stack JS2Object *JS2Engine::defaultConstructor(JS2Engine *engine) { js2val v = engine->pop(); @@ -359,6 +395,8 @@ namespace MetaData { return new FixedInstance(c); } + // Save current engine state (pc, environment top) and + // jump to start of new bytecodeContainer void JS2Engine::jsr(BytecodeContainer *new_bCon) { ASSERT(activationStackTop < (activationStack + MAX_ACTIVATION_STACK)); @@ -372,6 +410,7 @@ namespace MetaData { } + // Return to previously saved execution state void JS2Engine::rts() { ASSERT(activationStackTop > activationStack); @@ -384,6 +423,8 @@ namespace MetaData { } + // GC-mark any JS2Objects in the activation frame stack, the execution stack + // and in structures contained in those locations. void JS2Engine::mark() { if (bCon) @@ -404,6 +445,17 @@ namespace MetaData { } } + int32 JS2Engine::toInt32(float64 d) + { + if ((d == 0.0) || !JSDOUBLE_IS_FINITE(d) ) + return 0; + d = fd::fmod(d, two32); + d = (d >= 0) ? d : d + two32; + if (d >= two31) + return (int32)(d - two32); + else + return (int32)(d); + } } } \ No newline at end of file diff --git a/js2/src/js2engine.h b/js2/src/js2engine.h index 653abb52bb63..76e2346f976b 100644 --- a/js2/src/js2engine.h +++ b/js2/src/js2engine.h @@ -51,17 +51,26 @@ namespace MetaData { enum JS2Op { eAdd, eSubtract, + eMultiply, + eDivide, + eModulo, eEqual, eNotEqual, eLess, eGreater, eLessEqual, eGreaterEqual, + eXor, + eLogicalXor, + eMinus, + ePlus, + eComplement, eTrue, eFalse, eNull, eNumber, eString, // + eThis, eNewObject, // eLexicalRead, // eLexicalWrite, // @@ -76,27 +85,33 @@ enum JS2Op { eReturnVoid, ePushFrame, // ePopFrame, -// eToBoolean, + eToBoolean, eBranchFalse, // XXX save space with short and long versions instead ? eBranchTrue, // eBranch, // eNew, // eCall, // + eTypeof, ePopv, + ePop, + eDup, eLexicalPostInc, // eLexicalPostDec, // eLexicalPreInc, // eLexicalPreDec, // + eLexicalAssignOp, // eDotPostInc, // eDotPostDec, // eDotPreInc, // eDotPreDec, // + eDotAssignOp, // eBracketPostInc, eBracketPostDec, eBracketPreInc, eBracketPreDec, + eBracketAssignOp, // }; @@ -129,7 +144,7 @@ public: js2val toPrimitive(js2val x) { if (JS2VAL_IS_PRIMITIVE(x)) return x; else return convertValueToPrimitive(x); } float64 toNumber(js2val x) { if (JS2VAL_IS_INT(x)) return JS2VAL_TO_INT(x); else if (JS2VAL_IS_DOUBLE(x)) return *JS2VAL_TO_DOUBLE(x); else return convertValueToDouble(x); } bool toBoolean(js2val x) { if (JS2VAL_IS_BOOLEAN(x)) return JS2VAL_TO_BOOLEAN(x); else return convertValueToBoolean(x); } - + int32 toInt32(float64 f); js2val assignmentConversion(js2val val, JS2Class *type) { return val; } // XXX s'more code, please diff --git a/js2/src/js2metadata.cpp b/js2/src/js2metadata.cpp index 047739fe0ce6..a04b435b647f 100644 --- a/js2/src/js2metadata.cpp +++ b/js2/src/js2metadata.cpp @@ -46,6 +46,7 @@ #include "world.h" #include "utilities.h" #include "js2value.h" +#include "numerics.h" #include #include @@ -1035,6 +1036,8 @@ namespace MetaData { { } + // Convert an attribute to a compoundAttribute. If the attribute + // is NULL, return a default compoundAttribute CompoundAttribute *Attribute::toCompoundAttribute(Attribute *a) { if (a) @@ -1043,6 +1046,7 @@ namespace MetaData { return new CompoundAttribute(); } + // Convert a simple namespace to a compoundAttribute with that namespace CompoundAttribute *Namespace::toCompoundAttribute() { CompoundAttribute *t = new CompoundAttribute(); @@ -1050,11 +1054,13 @@ namespace MetaData { return t; } + // Convert a 'true' attribute to a default compoundAttribute CompoundAttribute *TrueAttribute::toCompoundAttribute() { return new CompoundAttribute(); } + // gc-mark all contained JS2Objects and visit contained structures to do likewise void CompoundAttribute::markChildren() { if (namespaces) { @@ -1074,10 +1080,11 @@ namespace MetaData { // Validate the entire expression rooted at p void JS2Metadata::ValidateExpression(Context *cxt, Environment *env, ExprNode *p) { - JS2Object::gc(this); + JS2Object::gc(this); // XXX testing stability switch (p->getKind()) { case ExprNode::Null: case ExprNode::number: + case ExprNode::string: case ExprNode::boolean: break; case ExprNode::This: @@ -1124,6 +1131,17 @@ namespace MetaData { case ExprNode::assignment: case ExprNode::add: case ExprNode::subtract: + case ExprNode::multiply: + case ExprNode::divide: + case ExprNode::modulo: + case ExprNode::addEquals: + case ExprNode::subtractEquals: + case ExprNode::multiplyEquals: + case ExprNode::divideEquals: + case ExprNode::moduloEquals: + case ExprNode::logicalAnd: + case ExprNode::logicalXor: + case ExprNode::logicalOr: { BinaryExprNode *b = checked_cast(p); ValidateExpression(cxt, env, b->op1); @@ -1131,10 +1149,15 @@ namespace MetaData { } break; + case ExprNode::minus: + case ExprNode::plus: + case ExprNode::complement: case ExprNode::postIncrement: case ExprNode::postDecrement: case ExprNode::preIncrement: case ExprNode::preDecrement: + case ExprNode::parentheses: + case ExprNode::Typeof: { UnaryExprNode *u = checked_cast(p); ValidateExpression(cxt, env, u->op); @@ -1177,7 +1200,7 @@ namespace MetaData { /* - * Evaluate an expression 'p' and execute the associated bytecode + * Evaluate an expression 'p' AND execute the associated bytecode */ js2val JS2Metadata::EvalExpression(Environment *env, Phase phase, ExprNode *p) { @@ -1192,15 +1215,21 @@ namespace MetaData { } /* - * Evaluate the expression rooted at p. + * Evaluate the expression (i.e. generate bytecode, but don't execute) rooted at p. */ Reference *JS2Metadata::EvalExprNode(Environment *env, Phase phase, ExprNode *p) { Reference *returnRef = NULL; - JS2Op binaryOp; + JS2Op op; switch (p->getKind()) { + case ExprNode::parentheses: + { + UnaryExprNode *u = checked_cast(p); + EvalExprNode(env, phase, u->op); + } + break; case ExprNode::assignment: { if (phase == CompilePhase) reportError(Exception::compileExpressionError, "Inappropriate compile time expression", p->pos); @@ -1215,30 +1244,68 @@ namespace MetaData { reportError(Exception::semanticError, "Assignment needs an lValue", p->pos); } break; + case ExprNode::addEquals: + op = eAdd; + goto doAssignBinary; + case ExprNode::subtractEquals: + op = eSubtract; + goto doAssignBinary; + case ExprNode::multiplyEquals: + op = eMultiply; + goto doAssignBinary; + case ExprNode::divideEquals: + op = eDivide; + goto doAssignBinary; + case ExprNode::moduloEquals: + op = eModulo; + goto doAssignBinary; +doAssignBinary: + { + if (phase == CompilePhase) reportError(Exception::compileExpressionError, "Inappropriate compile time expression", p->pos); + BinaryExprNode *b = checked_cast(p); + Reference *lVal = EvalExprNode(env, phase, b->op1); + if (lVal) { + Reference *rVal = EvalExprNode(env, phase, b->op2); + if (rVal) rVal->emitReadBytecode(bCon, p->pos); + lVal->emitAssignOpBytecode(bCon, op, p->pos); + } + else + reportError(Exception::semanticError, "Assignment needs an lValue", p->pos); + } + break; case ExprNode::lessThan: - binaryOp = eLess; + op = eLess; goto doBinary; case ExprNode::lessThanOrEqual: - binaryOp = eLessEqual; + op = eLessEqual; goto doBinary; case ExprNode::greaterThan: - binaryOp = eGreater; + op = eGreater; goto doBinary; case ExprNode::greaterThanOrEqual: - binaryOp = eGreaterEqual; + op = eGreaterEqual; goto doBinary; case ExprNode::equal: - binaryOp = eEqual; + op = eEqual; goto doBinary; case ExprNode::notEqual: - binaryOp = eNotEqual; + op = eNotEqual; goto doBinary; case ExprNode::add: - binaryOp = eAdd; + op = eAdd; goto doBinary; case ExprNode::subtract: - binaryOp = eSubtract; + op = eSubtract; + goto doBinary; + case ExprNode::multiply: + op = eMultiply; + goto doBinary; + case ExprNode::divide: + op = eDivide; + goto doBinary; + case ExprNode::modulo: + op = eModulo; goto doBinary; doBinary: { @@ -1247,12 +1314,74 @@ doBinary: if (lVal) lVal->emitReadBytecode(bCon, p->pos); Reference *rVal = EvalExprNode(env, phase, b->op2); if (rVal) rVal->emitReadBytecode(bCon, p->pos); - bCon->emitOp(binaryOp, p->pos); + bCon->emitOp(op, p->pos); } break; + case ExprNode::minus: + op = eMinus; + goto doUnary; + case ExprNode::plus: + op = ePlus; + goto doUnary; + case ExprNode::complement: + op = eComplement; + goto doUnary; +doUnary: + { + UnaryExprNode *u = checked_cast(p); + Reference *rVal = EvalExprNode(env, phase, u->op); + if (rVal) rVal->emitReadBytecode(bCon, p->pos); + bCon->emitOp(op, p->pos); + } + break; + + case ExprNode::logicalAnd: + { + BinaryExprNode *b = checked_cast(p); + BytecodeContainer::LabelID skipOverSecondHalf = bCon->getLabel(); + Reference *lVal = EvalExprNode(env, phase, b->op1); + if (lVal) lVal->emitReadBytecode(bCon, p->pos); + bCon->emitOp(eDup, p->pos); + bCon->emitBranch(eBranchFalse, skipOverSecondHalf, p->pos); + bCon->emitOp(ePop, p->pos); + Reference *rVal = EvalExprNode(env, phase, b->op2); + if (rVal) rVal->emitReadBytecode(bCon, p->pos); + bCon->setLabel(skipOverSecondHalf); + } + break; + + case ExprNode::logicalXor: + { + BinaryExprNode *b = checked_cast(p); + Reference *lVal = EvalExprNode(env, phase, b->op1); + if (lVal) lVal->emitReadBytecode(bCon, p->pos); + bCon->emitOp(eToBoolean, p->pos); + Reference *rVal = EvalExprNode(env, phase, b->op2); + if (rVal) rVal->emitReadBytecode(bCon, p->pos); + bCon->emitOp(eToBoolean, p->pos); + bCon->emitOp(eLogicalXor, p->pos); + } + break; + + case ExprNode::logicalOr: + { + BinaryExprNode *b = checked_cast(p); + BytecodeContainer::LabelID skipOverSecondHalf = bCon->getLabel(); + Reference *lVal = EvalExprNode(env, phase, b->op1); + if (lVal) lVal->emitReadBytecode(bCon, p->pos); + bCon->emitOp(eDup, p->pos); + bCon->emitBranch(eBranchTrue, skipOverSecondHalf, p->pos); + bCon->emitOp(ePop, p->pos); + Reference *rVal = EvalExprNode(env, phase, b->op2); + if (rVal) rVal->emitReadBytecode(bCon, p->pos); + bCon->setLabel(skipOverSecondHalf); + } + break; + + case ExprNode::This: { - + bCon->emitOp(eThis, p->pos); } break; case ExprNode::Null: @@ -1262,8 +1391,12 @@ doBinary: break; case ExprNode::number: { - bCon->emitOp(eNumber, p->pos); - bCon->addFloat64(checked_cast(p)->value); + bCon->addFloat64(checked_cast(p)->value, p->pos); + } + break; + case ExprNode::string: + { + bCon->addString(checked_cast(p)->str, p->pos); } break; case ExprNode::qualify: @@ -1398,6 +1531,14 @@ doBinary: bCon->addShort(argCount); } break; + case ExprNode::Typeof: + { + UnaryExprNode *u = checked_cast(p); + Reference *rVal = EvalExprNode(env, phase, u->op); + if (rVal) rVal->emitReadBytecode(bCon, p->pos); + bCon->emitOp(eTypeof, p->pos); + } + break; case ExprNode::call: { InvokeExprNode *i = checked_cast(p); @@ -1436,6 +1577,7 @@ doBinary: return returnRef; } + // Execute an expression and return the result, which must be a type JS2Class *JS2Metadata::EvalTypeExpression(Environment *env, Phase phase, ExprNode *p) { js2val retval = EvalExpression(env, phase, p); @@ -1608,6 +1750,7 @@ doBinary: return false; } + // add all the open namespaces from the given context void Multiname::addNamespace(Context &cxt) { addNamespace(&cxt.openNamespaces); @@ -1622,6 +1765,7 @@ doBinary: nsList.push_back(*nli); } + // gc-mark all contained JS2Objects and visit contained structures to do likewise void Multiname::markChildren() { for (NamespaceListIterator n = nsList.begin(), end = nsList.end(); (n != end); n++) { @@ -1920,6 +2064,9 @@ doBinary: //else A hoisted binding of the same var already exists, so there is no need to create another one } + void t(JS2Engine *engine) + { + } #define MAKEBUILTINCLASS(c, super, dynamic, allowNull, final, name) c = new JS2Class(super, new JS2Object(PrototypeInstanceKind), new Namespace(engine->private_StringAtom), dynamic, allowNull, final, name); c->complete = true @@ -1950,6 +2097,20 @@ doBinary: MAKEBUILTINCLASS(packageClass, objectClass, true, true, true, world.identifiers["package"]); forbiddenMember = new StaticMember(Member::Forbidden); + + writeDynamicProperty(glob, new Multiname(engine->undefined_StringAtom, publicNamespace), true, JS2VAL_UNDEFINED, RunPhase); + writeDynamicProperty(glob, new Multiname(world.identifiers["NaN"], publicNamespace), true, engine->allocNumber(nan), RunPhase); + writeDynamicProperty(glob, new Multiname(world.identifiers["Infinity"], publicNamespace), true, engine->allocNumber(positiveInfinity), RunPhase); + + FixedInstance *fInst = new FixedInstance(functionClass); + fInst->fWrap = new FunctionWrapper(true, new ParameterFrame(JS2VAL_VOID, true), t); + env.addFrame(objectClass); + NamespaceList publicNamespaceList; + publicNamespaceList.push_back(publicNamespace); + Variable *v = new Variable(functionClass, OBJECT_TO_JS2VAL(fInst), true); + defineStaticMember(&env, world.identifiers["toString"], &publicNamespaceList, Attribute::NoOverride, false, ReadWriteAccess, v, 0); + env.removeTopFrame(); + } // objectType(o) returns an OBJECT o's most specific type. @@ -2098,7 +2259,7 @@ doBinary: return false; // 'None' } - // Read the static member + // Read a value from the static member bool JS2Metadata::readStaticMember(StaticMember *m, Phase phase, js2val *rval) { if (m == NULL) @@ -2122,7 +2283,7 @@ doBinary: return false; } - // Write the static member + // Write a value to the static member bool JS2Metadata::writeStaticMember(StaticMember *m, js2val newValue, Phase phase) { if (m == NULL) @@ -2316,7 +2477,9 @@ readClassProperty: } } - + + // Write the value of an instance member into a container instance. + // Only instanceVariables are writable. bool JS2Metadata::writeInstanceMember(js2val containerVal, JS2Class *c, QualifiedName *qname, js2val newValue, Phase phase) { if (phase == CompilePhase) @@ -2380,7 +2543,7 @@ readClassProperty: } } - // Find a binding in the frame that matches the multiname and access + // Find a binding that matches the multiname and access. // It's an error if more than one such binding exists. StaticMember *JS2Metadata::findFlatMember(Frame *container, Multiname *multiname, Access access, Phase phase) { @@ -2416,7 +2579,8 @@ readClassProperty: return found; } - + // Find the multiname in the class - either in the static bindings (first) or + // in the instance bindings. If not there, look in the super class. bool JS2Metadata::findStaticMember(JS2Class *c, Multiname *multiname, Access access, Phase phase, MemberDescriptor *result) { JS2Class *s = c; @@ -2522,6 +2686,8 @@ readClassProperty: // and then invoke mark on all other structures that contain JS2Objects void JS2Metadata::mark() { + // XXX - maybe have a separate pool to allocate chunks + // that are meant to be never collected? GCMARKOBJECT(publicNamespace); GCMARKOBJECT(forbiddenMember); GCMARKOBJECT(undefinedClass); @@ -2574,6 +2740,7 @@ readClassProperty: } inline char narrow(char16 ch) { return char(ch); } + // Accepts a String as the error argument and converts to char * void JS2Metadata::reportError(Exception::Kind kind, const char *message, size_t pos, const String& name) { @@ -2700,6 +2867,7 @@ readClassProperty: } + // gc-mark all contained JS2Objects and visit contained structures to do likewise void JS2Class::markChildren() { GCMARKOBJECT(super) @@ -2720,7 +2888,8 @@ readClassProperty: * ************************************************************************************/ - + // Construct a dynamic instance of a class. Set the + // initial value of all slots to uninitialized. DynamicInstance::DynamicInstance(JS2Class *type) : JS2Object(DynamicInstanceKind), type(type), @@ -2735,6 +2904,7 @@ readClassProperty: } } + // gc-mark all contained JS2Objects and visit contained structures to do likewise void DynamicInstance::markChildren() { GCMARKOBJECT(type) @@ -2764,6 +2934,8 @@ readClassProperty: ************************************************************************************/ + // Construct a fixed instance of a class. Set the + // initial value of all slots to uninitialized. FixedInstance::FixedInstance(JS2Class *type) : JS2Object(FixedInstanceKind), type(type), @@ -2779,12 +2951,14 @@ readClassProperty: } } + // gc-mark all contained JS2Objects and visit contained structures to do likewise void FixedInstance::markChildren() { GCMARKOBJECT(type) if (fWrap) { GCMARKOBJECT(fWrap->compileFrame); - fWrap->bCon->mark(); + if (fWrap->bCon) + fWrap->bCon->mark(); } if (slots) { ASSERT(type); @@ -2804,7 +2978,7 @@ readClassProperty: * ************************************************************************************/ - + // gc-mark all contained JS2Objects and visit contained structures to do likewise void PrototypeInstance::markChildren() { GCMARKOBJECT(parent) @@ -2823,6 +2997,7 @@ readClassProperty: * ************************************************************************************/ + // gc-mark all contained JS2Objects and visit contained structures to do likewise void MethodClosure::markChildren() { if (JS2VAL_IS_OBJECT(thisObject)) { @@ -2838,6 +3013,7 @@ readClassProperty: * ************************************************************************************/ + // gc-mark all contained JS2Objects and visit contained structures to do likewise void Frame::markChildren() { GCMARKOBJECT(nextFrame) @@ -2858,6 +3034,7 @@ readClassProperty: * ************************************************************************************/ + // gc-mark all contained JS2Objects and visit contained structures to do likewise void GlobalObject::markChildren() { Frame::markChildren(); @@ -2882,6 +3059,7 @@ readClassProperty: env->instantiateFrame(pluralFrame, this); } + // gc-mark all contained JS2Objects and visit contained structures to do likewise void ParameterFrame::markChildren() { Frame::markChildren(); @@ -2921,6 +3099,7 @@ readClassProperty: type->mark(); } + // gc-mark all contained JS2Objects and visit contained structures to do likewise void InstanceMember::markChildren() { type->markChildren(); @@ -2933,6 +3112,8 @@ readClassProperty: * ************************************************************************************/ + // An instance variable type could be future'd when a gc runs (i.e. validate + // has executed, but the pre-eval stage has yet to determine the actual type) bool InstanceVariable::isMarked() { if (type != FUTURE_TYPE) @@ -2947,6 +3128,7 @@ readClassProperty: type->mark(); } + // gc-mark all contained JS2Objects and visit contained structures to do likewise void InstanceVariable::markChildren() { if (type != FUTURE_TYPE) @@ -2970,6 +3152,7 @@ readClassProperty: fInst->mark(); } + // gc-mark all contained JS2Objects and visit contained structures to do likewise void InstanceMethod::markChildren() { fInst->markChildren(); @@ -2985,6 +3168,9 @@ readClassProperty: Pond JS2Object::pond(POND_SIZE, NULL); std::list JS2Object::rootList; + // Add a pointer to a gc-allocated object to the root list + // (Note - we hand out an iterator, so it's essential to + // use something like std::list that doesn't mess with locations) JS2Object::RootIterator JS2Object::addRoot(void *t) { PondScum **p = (PondScum **)t; @@ -2992,11 +3178,13 @@ readClassProperty: return rootList.insert(rootList.end(), p); } + // Remove a root pointer void JS2Object::removeRoot(RootIterator ri) { rootList.erase(ri); } + // Mark all reachable objects and put the rest back on the freelist void JS2Object::gc(JS2Metadata *meta) { pond.resetMarks(); @@ -3016,6 +3204,7 @@ readClassProperty: pond.moveUnmarkedToFreeList(); } + // Allocate a chink of size s and mark whether it's a JS2Object or not void *JS2Object::alloc(size_t s, bool isJS2Object) { s += sizeof(PondScum); @@ -3025,6 +3214,7 @@ readClassProperty: return pond.allocFromPond((int32)s, isJS2Object); } + // Release a chunk back to it's pond void JS2Object::unalloc(void *t) { PondScum *p = (PondScum *)t - 1; diff --git a/js2/src/js2metadata.h b/js2/src/js2metadata.h index da7bc29053aa..81a3997543df 100644 --- a/js2/src/js2metadata.h +++ b/js2/src/js2metadata.h @@ -57,16 +57,25 @@ typedef void (Invokable)(); typedef Invokable Callor; typedef JS2Object *(Constructor)(JS2Engine *engine); + +// OBJECT is the semantic domain of all possible objects and is defined as: +// OBJECT = UNDEFINED | NULL | BOOLEAN | FLOAT64 | LONG | ULONG | CHARACTER | STRING | NAMESPACE | +// COMPOUNDATTRIBUTE | CLASS | METHODCLOSURE | PROTOTYPE | INSTANCE | PACKAGE | GLOBAL +// +// In this implementation, the primitive types are distinguished by the tag value +// of a JS2Value (see js2value.h). Non-primitive types are distinguished by calling +// 'kind()' on the object to recover one of the values below: +// enum ObjectKind { - AttributeObjectKind, - SystemKind, + AttributeObjectKind, + SystemKind, GlobalObjectKind, PackageKind, ParameterKind, ClassKind, BlockKind, - PrototypeInstanceKind, - FixedInstanceKind, + PrototypeInstanceKind, + FixedInstanceKind, DynamicInstanceKind, MultinameKind, MethodClosureKind @@ -147,10 +156,6 @@ public: static void mark(void *p) { ((PondScum *)p)[-1].mark(); } - -#ifdef DEBUG - virtual void uselessVirtual() { } // want the checked_cast stuff to work, so need a virtual function -#endif }; class Attribute : public JS2Object { @@ -191,10 +196,9 @@ public: const StringAtom &id; // The name }; -// MULTINAME is the semantic domain of sets of qualified names. Multinames are used internally in property lookup. +// A MULTINAME is the semantic domain of sets of qualified names. Multinames are used internally in property lookup. // We keep Multinames as a basename and a list of namespace qualifiers (XXX is that right - would the basename // ever be different for the same multiname?) -// Pointers to Multiname instances get embedded in the bytecode. typedef std::vector NamespaceList; typedef NamespaceList::iterator NamespaceListIterator; class Multiname : public JS2Object { @@ -216,12 +220,6 @@ public: }; -class Object_Uninit_Future { -public: - enum { Object, Uninitialized, Future } state; - js2val value; -}; - class NamedParameter { public: StringAtom &name; // This parameter's name @@ -259,7 +257,7 @@ public: StaticMember(MemberKind kind) : Member(kind) { } StaticMember *cloneContent; // Used during cloning operation to prevent cloning of duplicates (i.e. once - // a clone exists for this member it's stored here and used for any other + // a clone exists for this member it's recorded here and used for any other // bindings that refer to this member.) virtual StaticMember *clone() { ASSERT(false); return NULL; } @@ -312,19 +310,8 @@ public: Invokable *code; // calling this object does the read or write }; -/* -class StaticMethod : public StaticMember { -public: - StaticMethod() : StaticMember( - Signature type; // This function's signature - Invokable *code; // This function itself (a callable object) - enum { Static, Constructor } - modifier; // static if this is a function or a static method; constructor if this is a constructor for a class -}; -*/ - -// DYNAMICPROPERTY record describes one dynamic property of one (prototype or class) instance. +// A DYNAMICPROPERTY record describes one dynamic property of one (prototype or class) instance. typedef std::map DynamicPropertyMap; typedef DynamicPropertyMap::iterator DynamicPropertyIterator; @@ -559,6 +546,9 @@ public: virtual void emitPostDecBytecode(BytecodeContainer *bCon, size_t pos) { ASSERT(false); } virtual void emitPreIncBytecode(BytecodeContainer *bCon, size_t pos) { ASSERT(false); } virtual void emitPreDecBytecode(BytecodeContainer *bCon, size_t pos) { ASSERT(false); } + + virtual void emitAssignOpBytecode(BytecodeContainer *bCon, JS2Op op, size_t pos){ ASSERT(false); } + }; class LexicalReference : public Reference { @@ -584,6 +574,7 @@ public: virtual void emitPreIncBytecode(BytecodeContainer *bCon, size_t pos) { bCon->emitOp(eLexicalPreInc, pos); bCon->addMultiname(variableMultiname); } virtual void emitPreDecBytecode(BytecodeContainer *bCon, size_t pos) { bCon->emitOp(eLexicalPreDec, pos); bCon->addMultiname(variableMultiname); } + virtual void emitAssignOpBytecode(BytecodeContainer *bCon, JS2Op op, size_t pos){ bCon->emitOp(eLexicalAssignOp, pos); bCon->addByte((uint8)op); bCon->addMultiname(variableMultiname); } }; class DotReference : public Reference { @@ -610,6 +601,7 @@ public: virtual void emitPreIncBytecode(BytecodeContainer *bCon, size_t pos) { bCon->emitOp(eDotPreInc, pos); bCon->addMultiname(propertyMultiname); } virtual void emitPreDecBytecode(BytecodeContainer *bCon, size_t pos) { bCon->emitOp(eDotPreDec, pos); bCon->addMultiname(propertyMultiname); } + virtual void emitAssignOpBytecode(BytecodeContainer *bCon, JS2Op op, size_t pos){ bCon->emitOp(eDotAssignOp, pos); bCon->addByte((uint8)op); bCon->addMultiname(propertyMultiname); } }; @@ -647,6 +639,7 @@ public: virtual void emitPreIncBytecode(BytecodeContainer *bCon, size_t pos) { bCon->emitOp(eBracketPreInc, pos); } virtual void emitPreDecBytecode(BytecodeContainer *bCon, size_t pos) { bCon->emitOp(eBracketPreDec, pos); } + virtual void emitAssignOpBytecode(BytecodeContainer *bCon, JS2Op op, size_t pos){ bCon->emitOp(eBracketAssignOp, pos); bCon->addByte((uint8)op); } }; @@ -723,12 +716,17 @@ private: }; +typedef void (NativeCode)(JS2Engine *engine); + class FunctionWrapper { public: FunctionWrapper(bool unchecked, ParameterFrame *compileFrame) - : bCon(new BytecodeContainer), unchecked(unchecked), compileFrame(compileFrame) { } + : bCon(new BytecodeContainer), code(NULL), unchecked(unchecked), compileFrame(compileFrame) { } + FunctionWrapper(bool unchecked, ParameterFrame *compileFrame, NativeCode *code) + : bCon(NULL), code(code), unchecked(unchecked), compileFrame(compileFrame) { } BytecodeContainer *bCon; + NativeCode *code; bool unchecked; // true if the function is untyped, non-method, normal ParameterFrame *compileFrame; }; diff --git a/js2/src/js2op_access.cpp b/js2/src/js2op_access.cpp index 5bc16ce64942..6bf4e27f94de 100644 --- a/js2/src/js2op_access.cpp +++ b/js2/src/js2op_access.cpp @@ -149,4 +149,13 @@ } break; + case eThis: + { + js2val rval = meta->env.findThis(true); + if (JS2VAL_IS_INACCESSIBLE(rval)) + meta->reportError(Exception::compileExpressionError, "'this' not available", errorPos()); + push(rval); + } + break; + diff --git a/js2/src/js2op_arithmetic.cpp b/js2/src/js2op_arithmetic.cpp index db81871bf615..d61cae48d785 100644 --- a/js2/src/js2op_arithmetic.cpp +++ b/js2/src/js2op_arithmetic.cpp @@ -32,12 +32,31 @@ * file under either the NPL or the GPL. */ + case eMinus: + { + js2val a = pop(); + pushNumber(-toNumber(a)); + } + break; + case ePlus: + { + js2val a = pop(); + pushNumber(toNumber(a)); + } + break; + + case eComplement: + { + js2val a = pop(); + pushNumber(~toInt32(toNumber(a))); + } + break; case eAdd: { - js2val a = pop(); js2val b = pop(); + js2val a = pop(); a = toPrimitive(a); b = toPrimitive(b); if (JS2VAL_IS_STRING(a) || JS2VAL_IS_STRING(b)) { @@ -54,20 +73,76 @@ } } break; + case eSubtract: { - js2val a = pop(); js2val b = pop(); + js2val a = pop(); float64 anum = toNumber(a); float64 bnum = toNumber(b); pushNumber(anum - bnum); } break; + case eMultiply: + { + js2val b = pop(); + js2val a = pop(); + float64 anum = toNumber(a); + float64 bnum = toNumber(b); + pushNumber(anum * bnum); + } + break; + + case eDivide: + { + js2val b = pop(); + js2val a = pop(); + float64 anum = toNumber(a); + float64 bnum = toNumber(b); + pushNumber(anum / bnum); + } + break; + + case eModulo: + { + js2val b = pop(); + js2val a = pop(); + float64 anum = toNumber(a); + float64 bnum = toNumber(b); +#ifdef XP_PC + /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */ + if (JSDOUBLE_IS_FINITE(anum) && JSDOUBLE_IS_INFINITE(bnum)) + pushNumber(anum); + else +#endif + pushNumber(fd::fmod(anum, bnum)); + } + break; + + case eXor: + { + js2val b = pop(); + js2val a = pop(); + float64 anum = toNumber(a); + float64 bnum = toNumber(b); + pushNumber(toInt32(anum) ^ toInt32(bnum)); + } + break; + + case eLogicalXor: + { + js2val b = pop(); + js2val a = pop(); + ASSERT(JS2VAL_IS_BOOLEAN(a) && JS2VAL_IS_BOOLEAN(b)); + push(BOOLEAN_TO_JS2VAL(JS2VAL_TO_BOOLEAN(a) ^ JS2VAL_TO_BOOLEAN(b))); + } + break; + case eLess: { - js2val a = pop(); js2val b = pop(); + js2val a = pop(); js2val ap = toPrimitive(a); js2val bp = toPrimitive(b); bool rval; @@ -81,8 +156,8 @@ case eLessEqual: { - js2val a = pop(); js2val b = pop(); + js2val a = pop(); js2val ap = toPrimitive(a); js2val bp = toPrimitive(b); bool rval; @@ -96,8 +171,8 @@ case eGreater: { - js2val a = pop(); js2val b = pop(); + js2val a = pop(); js2val ap = toPrimitive(a); js2val bp = toPrimitive(b); bool rval; @@ -111,8 +186,8 @@ case eGreaterEqual: { - js2val a = pop(); js2val b = pop(); + js2val a = pop(); js2val ap = toPrimitive(a); js2val bp = toPrimitive(b); bool rval; @@ -128,8 +203,8 @@ case eEqual: { bool rval; - js2val a = pop(); js2val b = pop(); + js2val a = pop(); if (JS2VAL_IS_NULL(a) || JS2VAL_IS_UNDEFINED(a)) rval = (JS2VAL_IS_NULL(b) || JS2VAL_IS_UNDEFINED(b)); else @@ -206,6 +281,76 @@ } break; + case eLexicalAssignOp: + { + op = (JS2Op)*pc++; + Multiname *mn = bCon->mMultinameList[BytecodeContainer::getShort(pc)]; + pc += sizeof(short); + js2val a = meta->env.lexicalRead(meta, mn, phase); + js2val b = pop(); + js2val rval; + switch (op) { + case eAdd: + { + a = toPrimitive(a); + b = toPrimitive(b); + if (JS2VAL_IS_STRING(a) || JS2VAL_IS_STRING(b)) { + String *astr = toString(a); + String *bstr = toString(b); + String *c = new String(*astr); + *c += *bstr; + rval = STRING_TO_JS2VAL(c); + } + else { + float64 anum = toNumber(a); + float64 bnum = toNumber(b); + rval = allocNumber(anum + bnum); + } + } + break; + case eSubtract: + { + float64 anum = toNumber(a); + float64 bnum = toNumber(b); + rval = allocNumber(anum - bnum); + } + break; + case eMultiply: + { + float64 anum = toNumber(a); + float64 bnum = toNumber(b); + rval = allocNumber(anum * bnum); + } + break; + + case eDivide: + { + float64 anum = toNumber(a); + float64 bnum = toNumber(b); + rval = allocNumber(anum / bnum); + } + break; + + case eModulo: + { + float64 anum = toNumber(a); + float64 bnum = toNumber(b); +#ifdef XP_PC + /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */ + if (JSDOUBLE_IS_FINITE(anum) && JSDOUBLE_IS_INFINITE(bnum)) + rval = anum; + else +#endif + rval = allocNumber(fd::fmod(anum, bnum)); + } + break; + + } + meta->env.lexicalWrite(meta, mn, rval, true, phase); + push(rval); + } + break; + case eLexicalPostInc: { Multiname *mn = bCon->mMultinameList[BytecodeContainer::getShort(pc)]; diff --git a/js2/src/js2op_flowcontrol.cpp b/js2/src/js2op_flowcontrol.cpp index 094e50f7e87e..c0661edc5e43 100644 --- a/js2/src/js2op_flowcontrol.cpp +++ b/js2/src/js2op_flowcontrol.cpp @@ -63,3 +63,15 @@ pc += offset; } break; + + case ePop: + { + pop(); + } + break; + + case eDup: + { + push(top()); + } + break; diff --git a/js2/src/js2op_invocation.cpp b/js2/src/js2op_invocation.cpp index 313133a8283e..10be7a25e654 100644 --- a/js2/src/js2op_invocation.cpp +++ b/js2/src/js2op_invocation.cpp @@ -50,16 +50,15 @@ push(OBJECT_TO_JS2VAL(pInst)); } break; -/* + case eToBoolean: { js2val v = pop(); bool b = toBoolean(v); - retval = BOOLEAN_TO_JS2VAL(b); - push(retval); + push(BOOLEAN_TO_JS2VAL(b)); } break; -*/ + case eNew: { uint16 argCount = BytecodeContainer::getShort(pc); @@ -101,12 +100,16 @@ runtimeFrame->instantiate(&meta->env); runtimeFrame->thisObject = runtimeThis; // assignArguments(runtimeFrame, fWrap->compileFrame->signature); - jsr(fWrap->bCon); // seems out of order, but we need to catch the current top frame + if (!fWrap->code) + jsr(fWrap->bCon); // seems out of order, but we need to catch the current top frame meta->env.addFrame(runtimeFrame); - + if (fWrap->code) { + fWrap->code(this); + meta->env.removeTopFrame(); + } } else - if (fObj->kind == MethodClosureKind) { + if (fObj->kind == MethodClosureKind) { // XXX I made this up (particularly the push of the objectType) MethodClosure *mc = checked_cast(fObj); FixedInstance *fInst = mc->method->fInst; FunctionWrapper *fWrap = fInst->fWrap; @@ -129,7 +132,6 @@ case eReturn: { -// retval = pop(); if (activationStackEmpty()) return pop(); else @@ -161,3 +163,50 @@ } break; + case eTypeof: + { + js2val a = pop(); + js2val rval; + if (JS2VAL_IS_UNDEFINED(a)) + rval = STRING_TO_JS2VAL(&undefined_StringAtom); + else + if (JS2VAL_IS_BOOLEAN(a)) + rval = STRING_TO_JS2VAL(&world.identifiers["boolean"]); + else + if (JS2VAL_IS_NUMBER(a)) + rval = STRING_TO_JS2VAL(&world.identifiers["number"]); + else + if (JS2VAL_IS_STRING(a)) + rval = STRING_TO_JS2VAL(&world.identifiers["string"]); + else { + ASSERT(JS2VAL_IS_OBJECT(a)); + if (JS2VAL_IS_NULL(a)) + rval = STRING_TO_JS2VAL(&object_StringAtom); + JS2Object *obj = JS2VAL_TO_OBJECT(a); + switch (obj->kind) { + case MultinameKind: + rval = STRING_TO_JS2VAL(&world.identifiers["namespace"]); + break; + case AttributeObjectKind: + rval = STRING_TO_JS2VAL(&world.identifiers["attribute"]); + break; + case ClassKind: + case MethodClosureKind: + rval = STRING_TO_JS2VAL(&function_StringAtom); + break; + case PrototypeInstanceKind: + case PackageKind: + case GlobalObjectKind: + rval = STRING_TO_JS2VAL(&object_StringAtom); + break; + case FixedInstanceKind: + rval = STRING_TO_JS2VAL(&checked_cast(obj)->typeofString); + break; + case DynamicInstanceKind: + rval = STRING_TO_JS2VAL(&checked_cast(obj)->typeofString); + break; + } + } + push(rval); + } + break; \ No newline at end of file diff --git a/js2/src/numerics.h b/js2/src/numerics.h index c2bc5ecb368d..d36b04a8bb1b 100644 --- a/js2/src/numerics.h +++ b/js2/src/numerics.h @@ -69,6 +69,10 @@ namespace JavaScript { extern double minValue; extern double maxValue; + static const double two32minus1 = 4294967295.0; + static const double two32 = 4294967296.0; + static const double two16 = 65536.0; + static const double two31 = 2147483648.0; // // Portable double-precision floating point to string and back conversions //