diff --git a/mozilla/js2/src/js2array.cpp b/mozilla/js2/src/js2array.cpp index b7f4ea2a9dc..c3934078038 100644 --- a/mozilla/js2/src/js2array.cpp +++ b/mozilla/js2/src/js2array.cpp @@ -76,7 +76,7 @@ js2val setLength(JS2Metadata *meta, JS2Object *obj, uint32 length) js2val Array_Constructor(JS2Metadata *meta, const js2val /*thisValue*/, js2val *argv, uint32 argc) { - js2val thatValue = OBJECT_TO_JS2VAL(new ArrayInstance(meta->arrayClass)); + js2val thatValue = OBJECT_TO_JS2VAL(new ArrayInstance(meta->arrayClass->prototype, meta->arrayClass)); ArrayInstance *arrInst = checked_cast(JS2VAL_TO_OBJECT(thatValue)); if (argc > 0) { if (argc == 1) { @@ -107,7 +107,9 @@ js2val Array_Constructor(JS2Metadata *meta, const js2val /*thisValue*/, js2val * static js2val Array_toString(JS2Metadata *meta, const js2val thisValue, js2val * /*argv*/, uint32 /*argc*/) { - if (meta->objectType(thisValue) != meta->arrayClass) + if (!JS2VAL_IS_OBJECT(thisValue) + || (JS2VAL_TO_OBJECT(thisValue)->kind != PrototypeInstanceKind) + || ((checked_cast(JS2VAL_TO_OBJECT(thisValue)))->type != meta->arrayClass)) meta->reportError(Exception::typeError, "Array.prototype.toString called on a non Array", meta->engine->errorPos()); ArrayInstance *arrInst = checked_cast(JS2VAL_TO_OBJECT(thisValue)); @@ -137,7 +139,9 @@ static js2val Array_toString(JS2Metadata *meta, const js2val thisValue, js2val * static js2val Array_toSource(JS2Metadata *meta, const js2val thisValue, js2val * /*argv*/, uint32 /*argc*/) { - if (meta->objectType(thisValue) != meta->arrayClass) + if (!JS2VAL_IS_OBJECT(thisValue) + || (JS2VAL_TO_OBJECT(thisValue)->kind != PrototypeInstanceKind) + || ((checked_cast(JS2VAL_TO_OBJECT(thisValue)))->type != meta->arrayClass)) meta->reportError(Exception::typeError, "Array.prototype.toString called on a non Array", meta->engine->errorPos()); ArrayInstance *arrInst = checked_cast(JS2VAL_TO_OBJECT(thisValue)); @@ -204,7 +208,7 @@ js2val Array_concat(JS2Metadata *meta, const js2val thisValue, js2val *argv, uin { js2val E = thisValue; - js2val result = OBJECT_TO_JS2VAL(new ArrayInstance(meta->arrayClass)); + js2val result = OBJECT_TO_JS2VAL(new ArrayInstance(meta->arrayClass->prototype, meta->arrayClass)); ArrayInstance *A = checked_cast(JS2VAL_TO_OBJECT(result)); uint32 n = 0; uint32 i = 0; @@ -354,7 +358,7 @@ static js2val Array_slice(JS2Metadata *meta, const js2val thisValue, js2val *arg ASSERT(JS2VAL_IS_OBJECT(thisValue)); JS2Object *thisObj = JS2VAL_TO_OBJECT(thisValue); - js2val result = OBJECT_TO_JS2VAL(new ArrayInstance(meta->arrayClass)); + js2val result = OBJECT_TO_JS2VAL(new ArrayInstance(meta->arrayClass->prototype, meta->arrayClass)); ArrayInstance *A = checked_cast(JS2VAL_TO_OBJECT(result)); uint32 length = getLength(meta, thisObj); @@ -580,7 +584,7 @@ static js2val Array_splice(JS2Metadata *meta, const js2val thisValue, js2val *ar JS2Object *thisObj = JS2VAL_TO_OBJECT(thisValue); uint32 length = getLength(meta, thisObj); - js2val result = OBJECT_TO_JS2VAL(new ArrayInstance(meta->arrayClass)); + js2val result = OBJECT_TO_JS2VAL(new ArrayInstance(meta->arrayClass->prototype, meta->arrayClass)); ArrayInstance *A = checked_cast(JS2VAL_TO_OBJECT(result)); int32 arg0 = meta->toInteger(argv[0]); @@ -803,6 +807,15 @@ void initArrayObject(JS2Metadata *meta) publicNamespaceList.push_back(meta->publicNamespace); meta->arrayClass->construct = Array_Constructor; + meta->arrayClass->prototype = new ArrayInstance(meta->objectClass->prototype, meta->arrayClass); + + // Adding "prototype" & "length" as static members of the class - not dynamic properties; XXX + meta->env->addFrame(meta->arrayClass); + Variable *v = new Variable(meta->arrayClass, OBJECT_TO_JS2VAL(meta->arrayClass->prototype), true); + meta->defineLocalMember(meta->env, meta->engine->prototype_StringAtom, &publicNamespaceList, Attribute::NoOverride, false, ReadWriteAccess, v, 0); + v = new Variable(meta->numberClass, INT_TO_JS2VAL(1), true); + meta->defineLocalMember(meta->env, meta->engine->length_StringAtom, &publicNamespaceList, Attribute::NoOverride, false, ReadWriteAccess, v, 0); + meta->env->removeTopFrame(); PrototypeFunction *pf = &arrayProtos[0]; while (pf->name) { diff --git a/mozilla/js2/src/js2engine.cpp b/mozilla/js2/src/js2engine.cpp index a0d86ce43c6..b11dac55628 100644 --- a/mozilla/js2/src/js2engine.cpp +++ b/mozilla/js2/src/js2engine.cpp @@ -488,6 +488,7 @@ namespace MetaData { { eNew, "New", U16 }, // { eCall, "Call", U16 }, // { eTypeof, "Typeof", 0 }, + { eInstanceof, "Instanceof", 0 }, { eIs, "Is", 0 }, { ePopv, "Popv", 0 }, @@ -633,6 +634,7 @@ namespace MetaData { return 0; case eIs: // pop expr, pop type, push boolean + case eInstanceof: return 1; case eCoerce: // pop value, push coerced value (type is arg) diff --git a/mozilla/js2/src/js2engine.h b/mozilla/js2/src/js2engine.h index b16b43e9aa5..00201fcdcd8 100644 --- a/mozilla/js2/src/js2engine.h +++ b/mozilla/js2/src/js2engine.h @@ -128,6 +128,7 @@ enum JS2Op { eNew, // eCall, // eTypeof, + eInstanceof, eIs, ePopv, diff --git a/mozilla/js2/src/js2function.cpp b/mozilla/js2/src/js2function.cpp index 07db847a28e..8aa5efa2941 100644 --- a/mozilla/js2/src/js2function.cpp +++ b/mozilla/js2/src/js2function.cpp @@ -57,35 +57,35 @@ namespace MetaData { js2val Function_Constructor(JS2Metadata *meta, const js2val thisValue, js2val argv[], uint32 argc) { - if (argc) { - const String &srcLoc = widenCString("Function constructor source"); - const String *bodyStr = meta->toString(argv[argc - 1]); - String functionExpr(widenCString("(")); - if (argc > 1) { - for (uint32 i = 0; i < (argc - 1); i++) { - functionExpr += *meta->toString(argv[i]); - if (i < (argc - 2)) - functionExpr += ","; - } - } - functionExpr += widenCString("){") + *bodyStr + widenCString("}"); + if (argc) { + const String &srcLoc = widenCString("Function constructor source"); + const String *bodyStr = meta->toString(argv[argc - 1]); + String functionExpr(widenCString("(")); + if (argc > 1) { + for (uint32 i = 0; i < (argc - 1); i++) { + functionExpr += *meta->toString(argv[i]); + if (i < (argc - 2)) + functionExpr += ","; + } + } + functionExpr += widenCString("){") + *bodyStr + widenCString("}"); - Arena a; - Pragma::Flags flags = Pragma::js1; // XXX get flags from meta/context ? - Parser parser(meta->world, a, flags, functionExpr, srcLoc); + Arena a; + Pragma::Flags flags = Pragma::js1; // XXX get flags from meta/context ? + Parser parser(meta->world, a, flags, functionExpr, srcLoc); FunctionExprNode *fnExpr = parser.parseFunctionExpression(meta->engine->errorPos()); - ASSERT(parser.lexer.peek(true).hasKind(Token::end)); - ASSERT(fnExpr); // otherwise, an exception would have been thrown out of here - JS2Class *exprType; - meta->ValidateExpression(&meta->cxt, meta->env, fnExpr); - meta->SetupExprNode(meta->env, RunPhase, fnExpr, &exprType); + ASSERT(parser.lexer.peek(true).hasKind(Token::end)); + ASSERT(fnExpr); // otherwise, an exception would have been thrown out of here + JS2Class *exprType; + meta->ValidateExpression(&meta->cxt, meta->env, fnExpr); + meta->SetupExprNode(meta->env, RunPhase, fnExpr, &exprType); ASSERT(fnExpr); return OBJECT_TO_JS2VAL(fnExpr->obj); - } + } else { // construct an empty function wrapper js2val thatValue = OBJECT_TO_JS2VAL(new FunctionInstance(meta->functionClass->prototype, meta->functionClass)); FunctionInstance *fnInst = checked_cast(JS2VAL_TO_OBJECT(thatValue)); - fnInst->fWrap = new FunctionWrapper(true, new ParameterFrame(JS2VAL_INACCESSIBLE, true)); + fnInst->fWrap = new FunctionWrapper(true, new ParameterFrame(JS2VAL_INACCESSIBLE, true)); return thatValue; } } diff --git a/mozilla/js2/src/js2metadata.cpp b/mozilla/js2/src/js2metadata.cpp index 915c6361e49..4204b4d5d09 100644 --- a/mozilla/js2/src/js2metadata.cpp +++ b/mozilla/js2/src/js2metadata.cpp @@ -1593,6 +1593,8 @@ namespace MetaData { case ExprNode::logicalAndEquals: case ExprNode::logicalXorEquals: case ExprNode::logicalOrEquals: + case ExprNode::comma: + case ExprNode::Instanceof: { BinaryExprNode *b = checked_cast(p); @@ -1656,19 +1658,12 @@ namespace MetaData { } } break; - case ExprNode::comma: + case ExprNode::functionLiteral: { - BinaryExprNode *b = checked_cast(p); - ValidateExpression(cxt, env, b->op1); - ValidateExpression(cxt, env, b->op2); + FunctionExprNode *f = checked_cast(p); + f->obj = validateStaticFunction(&f->function, JS2VAL_INACCESSIBLE, true, true, cxt, env); } break; - case ExprNode::functionLiteral: - { - FunctionExprNode *f = checked_cast(p); - f->obj = validateStaticFunction(&f->function, JS2VAL_INACCESSIBLE, true, true, cxt, env); - } - break; default: NOT_REACHED("Not Yet Implemented"); } // switch (p->getKind()) @@ -2273,9 +2268,19 @@ doUnary: SetupExprNode(env, phase, b->op2, exprType); } break; + case ExprNode::Instanceof: + { + BinaryExprNode *b = checked_cast(p); + Reference *rVal = SetupExprNode(env, phase, b->op1, exprType); + if (rVal) rVal->emitReadBytecode(bCon, p->pos); + rVal = SetupExprNode(env, phase, b->op2, exprType); + if (rVal) rVal->emitReadBytecode(bCon, p->pos); + bCon->emitOp(eInstanceof, p->pos); + } + break; case ExprNode::functionLiteral: - { - FunctionExprNode *f = checked_cast(p); + { + FunctionExprNode *f = checked_cast(p); CompilationData *oldData = startCompilationUnit(f->function.fWrap->bCon, bCon->mSource, bCon->mSourceLocation); env->addFrame(f->function.fWrap->compileFrame); SetupStmt(env, phase, f->function.body); @@ -2283,8 +2288,8 @@ doUnary: bCon->emitOp(eReturnVoid, p->pos); env->removeTopFrame(); restoreCompilationUnit(oldData); - } - break; + } + break; default: NOT_REACHED("Not Yet Implemented"); } @@ -3293,9 +3298,8 @@ XXX see EvalAttributeExpression, where identifiers are being handled for now... // // ToString(ToUint32(name)) == name // - ASSERT(dynamicProperties); const DynamicPropertyMap::value_type e(*name, newValue); - dynamicProperties->insert(e); + dynamicProperties.insert(e); const char16 *numEnd; float64 f = stringToDouble(name->data(), name->data() + name->length(), numEnd); @@ -4628,7 +4632,7 @@ deleteClassProperty: ParameterFrame *plural = checked_cast(pluralFrame); ASSERT((plural->positionalCount == 0) || (plural->positional != NULL)); - js2val argumentsVal = OBJECT_TO_JS2VAL(new ArrayInstance(meta->arrayClass)); + js2val argumentsVal = OBJECT_TO_JS2VAL(new ArrayInstance(meta->arrayClass->prototype, meta->arrayClass)); ArrayInstance *arrInst = checked_cast(JS2VAL_TO_OBJECT(argumentsVal)); uint32 i; for (i = 0; ((i < argCount) && (i < plural->positionalCount)); i++) { diff --git a/mozilla/js2/src/js2metadata.h b/mozilla/js2/src/js2metadata.h index bd166245eef..3603ccbb590 100644 --- a/mozilla/js2/src/js2metadata.h +++ b/mozilla/js2/src/js2metadata.h @@ -579,7 +579,7 @@ public: JS2Object *parent; // If this instance was created by calling new on a prototype function, // the value of the function’s prototype property at the time of the call; - // none otherwise. + // none otherwise. (aka [[prototype]], aka __prototype__) JS2Class *type; // XXX used to determine [[class]] value DynamicPropertyMap dynamicProperties; // A set of this instance's dynamic properties virtual void markChildren(); @@ -639,12 +639,12 @@ public: virtual ~FunctionInstance() { } }; -// Array instances are simple instances created by the Array class, they +// Array instances are PrototypeInstances created by the Array class, they // maintain the value of the 'length' property when 'indexable' elements // are added. -class ArrayInstance : public SimpleInstance { +class ArrayInstance : public PrototypeInstance { public: - ArrayInstance(JS2Class *type) : SimpleInstance(type) { } + ArrayInstance(JS2Object *parent, JS2Class *type) : PrototypeInstance(parent, type) { } virtual void writeProperty(JS2Metadata *meta, const String *name, js2val newValue); virtual ~ArrayInstance() { } @@ -925,6 +925,7 @@ typedef FrameList::iterator FrameListIterator; class Environment : public JS2Object { public: Environment(SystemFrame *systemFrame, Frame *nextToLast) : JS2Object(EnvironmentKind) { frameList.push_back(nextToLast); frameList.push_back(systemFrame); } + virtual ~Environment() { } JS2Class *getEnclosingClass(); FrameListIterator getRegionalFrame(); diff --git a/mozilla/js2/src/js2op_invocation.cpp b/mozilla/js2/src/js2op_invocation.cpp index 222b67009ac..7d52adf7391 100644 --- a/mozilla/js2/src/js2op_invocation.cpp +++ b/mozilla/js2/src/js2op_invocation.cpp @@ -235,16 +235,86 @@ case eIs: { - a = pop(); // catch variable type - b = pop(); // exception object - JS2Class *c = meta->objectType(b); - if (!JS2VAL_IS_OBJECT(a)) + b = pop(); + a = pop(); // doing a is b + + if (!JS2VAL_IS_OBJECT(b)) meta->reportError(Exception::badValueError, "Type expected", errorPos()); - JS2Object *obj = JS2VAL_TO_OBJECT(a); + JS2Object *obj = JS2VAL_TO_OBJECT(b); if (obj->kind != ClassKind) meta->reportError(Exception::badValueError, "Type expected", errorPos()); JS2Class *isClass = checked_cast(obj); - push(c == isClass); + push(BOOLEAN_TO_JS2VAL(meta->objectType(a) == isClass)); + } + break; + + case eInstanceof: // XXX prototype version + { + b = pop(); + a = pop(); // doing a instanceof b + + if (!JS2VAL_IS_OBJECT(b)) + meta->reportError(Exception::typeError, "Object expected for instanceof", errorPos()); + JS2Object *obj = JS2VAL_TO_OBJECT(b); + if ((obj->kind == PrototypeInstanceKind) + && (checked_cast(obj)->type == meta->functionClass)) { + // XXX this is [[hasInstance]] from ECMA3 + if (!JS2VAL_IS_OBJECT(a)) + push(JS2VAL_FALSE); + else { + if (JS2VAL_TO_OBJECT(a)->kind != PrototypeInstanceKind) + meta->reportError(Exception::typeError, "PrototypeInstance expected for instanceof", errorPos()); + JS2Object *a_protoObj = checked_cast(JS2VAL_TO_OBJECT(a))->parent; + + js2val b_protoVal; + JS2Object *b_protoObj = NULL; + Multiname mn(prototype_StringAtom); // gc safe because the content is rooted elsewhere + LookupKind lookup(true, JS2VAL_NULL); // make it a lexical lookup since we want it to + // fail if 'prototype' hasn't been defined + // XXX (prototype should always exist for functions) + if (meta->readProperty(&a, &mn, &lookup, RunPhase, &b_protoVal)) { + if (!JS2VAL_IS_OBJECT(b_protoVal)) + meta->reportError(Exception::typeError, "Non-object prototype value in instanceOf", errorPos()); + b_protoObj = JS2VAL_TO_OBJECT(b_protoVal); + } + bool result = false; + while (a_protoObj) { + if (b_protoObj == a_protoObj) { + result = true; + break; + } + a_protoObj = checked_cast(a_protoObj)->parent; + } + push(BOOLEAN_TO_JS2VAL(result)); + } + } + else { // XXX also support a instanceof <> since some of these are + // the ECMA3 builtins. + if (obj->kind == ClassKind) { + if (!JS2VAL_IS_OBJECT(a)) + push(JS2VAL_FALSE); + else { + if (JS2VAL_TO_OBJECT(a)->kind != PrototypeInstanceKind) + meta->reportError(Exception::typeError, "PrototypeInstance expected for instanceof", errorPos()); + JS2Object *a_protoObj = checked_cast(JS2VAL_TO_OBJECT(a))->parent; + + JS2Object *b_protoObj = checked_cast(obj)->prototype; + + bool result = false; + while (a_protoObj) { + if (b_protoObj == a_protoObj) { + result = true; + break; + } + a_protoObj = checked_cast(a_protoObj)->parent; + } + push(BOOLEAN_TO_JS2VAL(result)); + } + } + else + meta->reportError(Exception::typeError, "Function or Class expected in instanceOf", errorPos()); + } + } break; diff --git a/mozilla/js2/src/js2op_literal.cpp b/mozilla/js2/src/js2op_literal.cpp index 9dbb12d5a82..463178f363a 100644 --- a/mozilla/js2/src/js2op_literal.cpp +++ b/mozilla/js2/src/js2op_literal.cpp @@ -135,12 +135,12 @@ { uint16 argCount = BytecodeContainer::getShort(pc); pc += sizeof(uint16); - ArrayInstance *aInst = new ArrayInstance(meta->arrayClass); + ArrayInstance *aInst = new ArrayInstance(meta->arrayClass->prototype, meta->arrayClass); baseVal = OBJECT_TO_JS2VAL(aInst); for (uint16 i = 0; i < argCount; i++) { b = pop(); const DynamicPropertyMap::value_type e(*numberToString((argCount - 1) - i), b); - aInst->dynamicProperties->insert(e); + aInst->dynamicProperties.insert(e); } setLength(meta, aInst, argCount); push(baseVal); diff --git a/mozilla/js2/src/js2regexp.cpp b/mozilla/js2/src/js2regexp.cpp index fee9890e155..8958f8f8a04 100644 --- a/mozilla/js2/src/js2regexp.cpp +++ b/mozilla/js2/src/js2regexp.cpp @@ -142,7 +142,7 @@ namespace MetaData { REMatchState *match = REExecute(thisInst->mRegExp, str->begin(), index, toInt32(str->length()), meta->toBoolean(globalMultiline)); if (match) { - PrototypeInstance *A = new PrototypeInstance(meta->objectClass->prototype, meta->objectClass); + PrototypeInstance *A = new ArrayInstance(meta->arrayClass->prototype, meta->arrayClass); result = OBJECT_TO_JS2VAL(A); js2val matchStr = meta->engine->allocString(str->substr((uint32)match->startIndex, (uint32)match->endIndex - match->startIndex)); Multiname mname(&meta->world.identifiers[*meta->toString((long)0)], meta->publicNamespace); diff --git a/mozilla/js2/src/js2string.cpp b/mozilla/js2/src/js2string.cpp index f7547803b5b..1edb105e5c1 100644 --- a/mozilla/js2/src/js2string.cpp +++ b/mozilla/js2/src/js2string.cpp @@ -170,7 +170,7 @@ static js2val String_match(JS2Metadata *meta, const js2val thisValue, js2val *ar return RegExp_exec(meta, regexp, &S, 1); } else { - PrototypeInstance *A = new PrototypeInstance(meta->objectClass->prototype, meta->objectClass); + PrototypeInstance *A = new ArrayInstance(meta->arrayClass->prototype, meta->arrayClass); int32 index = 0; int32 lastIndex = 0; while (true) { @@ -405,7 +405,7 @@ static js2val String_split(JS2Metadata *meta, const js2val thisValue, js2val *ar { const String *S = meta->toString(thisValue); - js2val result = OBJECT_TO_JS2VAL(new ArrayInstance(meta->arrayClass)); + js2val result = OBJECT_TO_JS2VAL(new ArrayInstance(meta->arrayClass->prototype, meta->arrayClass)); ArrayInstance *A = checked_cast(JS2VAL_TO_OBJECT(result)); uint32 lim;