diff --git a/js2/src/js2execution.cpp b/js2/src/js2execution.cpp index 82343c4e66f4..8b417512a955 100644 --- a/js2/src/js2execution.cpp +++ b/js2/src/js2execution.cpp @@ -238,11 +238,12 @@ JSValue Context::interpret(JS2Runtime::ByteCodeModule *bcm, int offset, ScopeCha mStack = prev->mStack; mStackTop = 0; // we're processing an exception, no need to preserve the stack - if (jsx.hasKind(Exception::userException)) - pushValue(x); - if (mCurModule) - mStackMax = mCurModule->mStackDepth; + if (mCurModule) { + mStackMax = mCurModule->mStackDepth; + if (jsx.hasKind(Exception::userException)) + pushValue(x); + } mLocals = prev->mLocals; mArgumentBase = prev->mArgumentBase; mThis = prev->mThis; @@ -331,7 +332,7 @@ JSValue *Context::buildArgumentBlock(JSFunction *target, uint32 &argCount) needDefault = false; } } - if (needDefault) + if (needDefault && target->isChecked()) reportError(Exception::referenceError, "missing required argument"); } else { diff --git a/js2/src/js2runtime.cpp b/js2/src/js2runtime.cpp index 253fef1aa1cf..56c90447a89f 100644 --- a/js2/src/js2runtime.cpp +++ b/js2/src/js2runtime.cpp @@ -636,7 +636,8 @@ void JSInstance::initInstance(Context *cx, JSType *type) JSInstance *JSType::newInstance(Context *cx) { JSInstance *result = new JSInstance(cx, this); - result->mPrototype = mPrototypeObject; + result->mPrototype = mPrototypeObject; + result->defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, Object_Type, JSValue(mPrototypeObject)); return result; } @@ -666,6 +667,7 @@ JSInstance *JSArrayType::newInstance(Context *cx) { JSInstance *result = new JSArrayInstance(cx, this); result->mPrototype = mPrototypeObject; + result->defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, Object_Type, JSValue(mPrototypeObject)); return result; } @@ -673,6 +675,7 @@ JSInstance *JSStringType::newInstance(Context *cx) { JSInstance *result = new JSStringInstance(cx, this); result->mPrototype = mPrototypeObject; + result->defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, Object_Type, JSValue(mPrototypeObject)); return result; } @@ -1575,7 +1578,7 @@ Reference *JSType::genReference(bool hasBase, const String& name, NamespaceList return NULL; } -JSType::JSType(Context *cx, const StringAtom *name, JSType *super, JSObject *protoObj) +JSType::JSType(Context *cx, const StringAtom *name, JSType *super, JSObject *protoObj, JSObject *typeProto) : JSObject(Type_Type), mSuperType(super), mVariableCount(0), @@ -1610,11 +1613,17 @@ JSType::JSType(Context *cx, const StringAtom *name, JSType *super, JSObject *pro else // must be Object_Type being initialized defineVariable(cx, cx->Prototype_StringAtom, (NamespaceList *)NULL, Property::ReadOnly | Property::DontDelete, this, JSValue(mPrototypeObject)); - // the __proto__ of a type is the super-type's prototype object, or for 'class Object' type object it's the Object's prototype object - if (mSuperType) - mPrototype = mSuperType->mPrototypeObject; - else // must be Object_Type being initialized - mPrototype = mPrototypeObject; + if (typeProto) + mPrototype = typeProto; + else { + // the __proto__ of a type is the super-type's prototype object, or for 'class Object' type object it's the Object's prototype object + if (mSuperType) + mPrototype = mSuperType->mPrototypeObject; + else // must be Object_Type being initialized + mPrototype = mPrototypeObject; + } + defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, Object_Type, JSValue(mPrototype)); + defineVariable(cx, cx->Constructor_StringAtom, (NamespaceList *)NULL, 0, Object_Type, JSValue(this)); } JSType::JSType(JSType *xClass) // used for constructing the static component type @@ -2953,8 +2962,7 @@ void Context::initBuiltins() Object_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[0].name)], NULL); Object_Type->mPrototype->mType = Object_Type; - Object_Type->mIsDynamic = true; - // XXX aren't all the built-ins thus? + Object_Type->mIsDynamic = true; // XXX aren't all the built-ins thus? Type_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[1].name)], Object_Type); Object_Type->mType = Type_Type; @@ -2966,9 +2974,15 @@ void Context::initBuiltins() // (which we don't actually have, hmm). // For String, etc. this same issue needs to be finessed - JSFunction *funProto = new JSFunction(this, Object_Type, NULL); + JSFunction *funProto = new JSFunction(this, Object_Type, NULL); + funProto->mPrototype = Object_Type->mPrototypeObject; Function_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[2].name)], Object_Type, funProto); - funProto->mType = Function_Type; + Function_Type->mPrototype = Function_Type->mPrototypeObject; + funProto->mType = Function_Type; + + // now we can bootstrap the Object prototype (__proto__) to be the Function prototype + Object_Type->mPrototype = Function_Type->mPrototypeObject; + JSObject *numProto = new JSObject(); Number_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[3].name)], Object_Type, numProto); @@ -2982,8 +2996,8 @@ void Context::initBuiltins() strProto->mType = String_Type; strProto->mPrivate = (void *)(&Empty_StringAtom); - JSArrayInstance *arrayProto = new JSArrayInstance(this, NULL); - Array_Type = new JSArrayType(this, Object_Type, &mWorld.identifiers[widenCString(builtInClasses[6].name)], Object_Type, arrayProto); + JSArrayInstance *arrayProto = new JSArrayInstance(this, NULL); + Array_Type = new JSArrayType(this, Object_Type, &mWorld.identifiers[widenCString(builtInClasses[6].name)], Object_Type, arrayProto, Function_Type->mPrototypeObject); arrayProto->mType = Array_Type; Boolean_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[7].name)], Object_Type); @@ -3001,7 +3015,7 @@ void Context::initBuiltins() TypeError_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[19].name)], Error_Type); UriError_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[20].name)], Error_Type); - // XXX RegExp.prototype is set to a RegExp instance, which isn't ECMA (it's supposed to be an Object instance) bu + // XXX RegExp.prototype is set to a RegExp instance, which isn't ECMA (it's supposed to be an Object instance) but // is SpiderMonkey compatible. RegExp_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[21].name)], Object_Type); Package_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[22].name)], Object_Type); @@ -3241,6 +3255,7 @@ Context::Context(JSObject **global, World &world, Arena &a, Pragma::Flags flags) LeftContext_StringAtom (world.identifiers["leftContext"]), RightContext_StringAtom (world.identifiers["rightContext"]), Dollar_StringAtom (world.identifiers["$"]), + UnderbarPrototype_StringAtom (world.identifiers["__proto__"]), mWorld(world), mScopeChain(NULL), diff --git a/js2/src/js2runtime.h b/js2/src/js2runtime.h index 791f70ebb772..46f57a0fbf27 100644 --- a/js2/src/js2runtime.h +++ b/js2/src/js2runtime.h @@ -753,7 +753,7 @@ XXX ...couldn't get this to work... class JSType : public JSObject { public: - JSType(Context *cx, const StringAtom *name, JSType *super, JSObject *protoObj = NULL); + JSType(Context *cx, const StringAtom *name, JSType *super, JSObject *protoObj = NULL, JSObject *typeProto = NULL); JSType(JSType *xClass); // used for constructing the static component type virtual ~JSType() { } // keeping gcc happy @@ -918,8 +918,8 @@ XXX ...couldn't get this to work... class JSArrayType : public JSType { public: - JSArrayType(Context *cx, JSType *elementType, const StringAtom *name, JSType *super, JSObject *protoObj = NULL) - : JSType(cx, name, super, protoObj), mElementType(elementType) + JSArrayType(Context *cx, JSType *elementType, const StringAtom *name, JSType *super, JSObject *protoObj = NULL, JSObject *typeProto = NULL) + : JSType(cx, name, super, protoObj, typeProto), mElementType(elementType) { } virtual ~JSArrayType() { } // keeping gcc happy @@ -954,8 +954,8 @@ XXX ...couldn't get this to work... class JSStringType : public JSType { public: - JSStringType(Context *cx, const StringAtom *name, JSType *super, JSObject *protoObj = NULL) - : JSType(cx, name, super, protoObj) + JSStringType(Context *cx, const StringAtom *name, JSType *super, JSObject *protoObj = NULL, JSObject *typeProto = NULL) + : JSType(cx, name, super, protoObj, typeProto) { } virtual ~JSStringType() { } // keeping gcc happy @@ -1687,7 +1687,8 @@ XXX ...couldn't get this to work... StringAtom& LastParen_StringAtom; StringAtom& LeftContext_StringAtom; StringAtom& RightContext_StringAtom; - StringAtom& Dollar_StringAtom; + StringAtom& Dollar_StringAtom; + StringAtom& UnderbarPrototype_StringAtom; void initBuiltins(); void initClass(JSType *type, ClassDef *cdef, PrototypeFunctions *pdef); diff --git a/js2/src/jsarray.cpp b/js2/src/jsarray.cpp index 626d80dff646..25bf5d638183 100644 --- a/js2/src/jsarray.cpp +++ b/js2/src/jsarray.cpp @@ -218,10 +218,15 @@ static JSValue Array_join(Context *cx, const JSValue& thisValue, JSValue *argv, thisObj->getProperty(cx, cx->Length_StringAtom, CURRENT_ATTR); JSValue result = cx->popValue(); uint32 length = (uint32)(result.toUInt32(cx).f64); + +/* XXX ECMA says that if separator is undefined we use ',', but SpiderMonkey and + the test suite want 'undefined' in that case, and only use the ',' when the + separator is actually is missing +*/ const String *separator; if (argc == 0) - separator = new String(',', 1); + separator = new String(widenCString(",")); else separator = argv[0].toString(cx).string; @@ -231,7 +236,7 @@ static JSValue Array_join(Context *cx, const JSValue& thisValue, JSValue *argv, String *S = new String(); for (uint32 k = 0; k < length; k++) { - thisObj->getProperty(cx, *numberToString(0), CURRENT_ATTR); + thisObj->getProperty(cx, *numberToString(k), CURRENT_ATTR); result = cx->popValue(); if (!result.isUndefined() && !result.isNull()) *S += *result.toString(cx).string; diff --git a/js2/src/jsmath.cpp b/js2/src/jsmath.cpp index 04f68ba9ad4b..bc4dd7b0f003 100644 --- a/js2/src/jsmath.cpp +++ b/js2/src/jsmath.cpp @@ -43,6 +43,8 @@ #include "parser.h" #include "numerics.h" #include "js2runtime.h" +#include "jslong.h" +#include "prmjtime.h" #include "jsmath.h" @@ -175,10 +177,82 @@ static JSValue Math_pow(Context *cx, const JSValue& /*thisValue*/, JSValue *argv if (argc < 1) return kNaNValue; return JSValue(fd::pow(argv[0].toNumber(cx).f64, argv[1].toNumber(cx).f64)); -} -static JSValue Math_random(Context * /*cx*/, const JSValue& /*thisValue*/, JSValue * /*argv*/, uint32 /*argc*/) +} + +/* + * Math.random() support, lifted from java.util.Random.java. + */ +static void random_setSeed(Context *cx, int64 seed) +{ + int64 tmp; + + JSLL_I2L(tmp, 1000); + JSLL_DIV(seed, seed, tmp); + JSLL_XOR(tmp, seed, cx->mWorld.rngMultiplier); + JSLL_AND(cx->mWorld.rngSeed, tmp, cx->mWorld.rngMask); +} + +static void random_init(Context *cx) +{ + int64 tmp, tmp2; + + /* Do at most once. */ + if (cx->mWorld.rngInitialized) + return; + cx->mWorld.rngInitialized = true; + + /* cx->mWorld.rngMultiplier = 0x5DEECE66DL */ + JSLL_ISHL(tmp, 0x5D, 32); + JSLL_UI2L(tmp2, 0xEECE66DL); + JSLL_OR(cx->mWorld.rngMultiplier, tmp, tmp2); + + /* cx->mWorld.rngAddend = 0xBL */ + JSLL_I2L(cx->mWorld.rngAddend, 0xBL); + + /* cx->mWorld.rngMask = (1L << 48) - 1 */ + JSLL_I2L(tmp, 1); + JSLL_SHL(tmp2, tmp, 48); + JSLL_SUB(cx->mWorld.rngMask, tmp2, tmp); + + /* cx->mWorld.rngDscale = (jsdouble)(1L << 54) */ + JSLL_SHL(tmp2, tmp, 54); + JSLL_L2D(cx->mWorld.rngDscale, tmp2); + + /* Finally, set the seed from current time. */ + random_setSeed(cx, PRMJ_Now()); +} + +static uint32 random_next(Context *cx, int bits) +{ + int64 nextseed, tmp; + uint32 retval; + + JSLL_MUL(nextseed, cx->mWorld.rngSeed, cx->mWorld.rngMultiplier); + JSLL_ADD(nextseed, nextseed, cx->mWorld.rngAddend); + JSLL_AND(nextseed, nextseed, cx->mWorld.rngMask); + cx->mWorld.rngSeed = nextseed; + JSLL_USHR(tmp, nextseed, 48 - bits); + JSLL_L2I(retval, tmp); + return retval; +} + +static float64 random_nextDouble(Context *cx) +{ + int64 tmp, tmp2; + float64 d; + + JSLL_ISHL(tmp, random_next(cx, 27), 27); + JSLL_UI2L(tmp2, random_next(cx, 27)); + JSLL_ADD(tmp, tmp, tmp2); + JSLL_L2D(d, tmp); + return d / cx->mWorld.rngDscale; +} + + +static JSValue Math_random(Context *cx, const JSValue& /*thisValue*/, JSValue * /*argv*/, uint32 /*argc*/) { - return JSValue(42.0); + random_init(cx); + return JSValue(random_nextDouble(cx)); } static JSValue Math_round(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 argc) { diff --git a/js2/src/jsstring.cpp b/js2/src/jsstring.cpp index dc3dbb4ea180..f778ba0f03c8 100644 --- a/js2/src/jsstring.cpp +++ b/js2/src/jsstring.cpp @@ -105,6 +105,32 @@ static JSValue String_valueOf(Context *cx, const JSValue& thisValue, JSValue * / return JSValue((String *)thisObj->mPrivate); } +static JSValue String_search(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc) +{ + ContextStackReplacement csr(cx); + + ASSERT(thisValue.isObject() || thisValue.isFunction() || thisValue.isType()); + JSValue S = thisValue.toString(cx); + + JSValue regexp = argv[0]; + if ((argc == 0) || !regexp.isObject() || (regexp.object->mType != RegExp_Type)) { + regexp = kNullValue; + regexp = RegExp_Constructor(cx, regexp, argv, 1); + } + REParseState *parseResult = (REParseState *)(regexp.object->mPrivate); + + /* save & restore lastIndex as it's not to be modified */ + uint32 lastIndex = parseResult->lastIndex; + parseResult->lastIndex = 0; + REState *regexp_result = REExecute(parseResult, S.string->begin(), S.string->length()); + parseResult->lastIndex = lastIndex; + + if (regexp_result) + return JSValue((float64)(regexp_result->endIndex)); + else + return JSValue(-1.0); + +} static JSValue String_match(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc) { @@ -273,27 +299,60 @@ static JSValue String_replace(Context *cx, const JSValue& thisValue, JSValue *ar struct MatchResult { bool failure; - uint32 endIndex; - String **captures; + uint32 endIndex; + uint32 capturesCount; + JSValue *captures; }; -static void splitMatch(const String *S, uint32 q, const String *R, MatchResult &result) -{ - result.failure = true; - result.captures = NULL; - - uint32 r = R->size(); - uint32 s = S->size(); - if ((q + r) > s) - return; - for (uint32 i = 0; i < r; i++) { - if ((*S)[q + i] != (*R)[i]) - return; - } - result.endIndex = q + r; - result.failure = false; -} - +static void strSplitMatch(const String *S, uint32 q, const String *R, MatchResult &result) +{ + result.failure = true; + result.captures = NULL; + result.capturesCount = 0; + + uint32 r = R->size(); + uint32 s = S->size(); + if ((q + r) > s) + return; + for (uint32 i = 0; i < r; i++) { + if ((*S)[q + i] != (*R)[i]) + return; + } + result.endIndex = q + r; + result.failure = false; +} + +static void regexpSplitMatch(const String *S, uint32 q, REParseState *RE, MatchResult &result) +{ + result.failure = true; + result.captures = NULL; + + /* save & restore lastIndex as it's not to be modified */ + uint32 lastIndex = RE->lastIndex; + RE->lastIndex = 0; + REState *regexp_result = REExecute(RE, S->begin() + q, S->length() - q); + RE->lastIndex = lastIndex; + + + if (regexp_result) { + result.endIndex = regexp_result->endIndex + q; + result.failure = false; + result.capturesCount = regexp_result->n; + if (regexp_result->n) { + result.captures = new JSValue[regexp_result->n]; + for (uint32 i = 0; i < regexp_result->n; i++) { + if (regexp_result->parens[i].index != -1) { + String *parenStr = new String(S->substr((uint32)(regexp_result->parens[i].index + q), (uint32)(regexp_result->parens[i].length))); + result.captures[i] = JSValue(parenStr); + } + else + result.captures[i] = kUndefinedValue; + } + } + } + +} + static JSValue String_split(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc) { ContextStackReplacement csr(cx); @@ -313,10 +372,13 @@ static JSValue String_split(Context *cx, const JSValue& thisValue, JSValue *argv uint32 s = S.string->size(); uint32 p = 0; - - // XXX if separatorV.isRegExp() --> - - const String *R = separatorV.toString(cx).string; + + REParseState *RE = NULL; + const String *R = NULL; + if (separatorV.isObject() && (separatorV.object->mType == RegExp_Type)) + RE = (REParseState *)(separatorV.object->mPrivate); + else + R = separatorV.toString(cx).string; if (lim == 0) return JSValue(A); @@ -330,13 +392,16 @@ static JSValue String_split(Context *cx, const JSValue& thisValue, JSValue *argv */ if (s == 0) { MatchResult z; - splitMatch(S.string, 0, R, z); + if (RE) + regexpSplitMatch(S.string, 0, RE, z); + else + strSplitMatch(S.string, 0, R, z); if (!z.failure) return JSValue(A); A->setProperty(cx, widenCString("0"), NULL, S); return JSValue(A); } - + while (true) { uint32 q = p; step11: @@ -346,8 +411,11 @@ step11: A->setProperty(cx, *numberToString(A->mLength), NULL, v); return JSValue(A); } - MatchResult z; - splitMatch(S.string, q, R, z); + MatchResult z; + if (RE) + regexpSplitMatch(S.string, q, RE, z); + else + strSplitMatch(S.string, q, R, z); if (z.failure) { q = q + 1; goto step11; @@ -362,10 +430,14 @@ step11: A->setProperty(cx, *numberToString(A->mLength), NULL, v); if (A->mLength == lim) return JSValue(A); - p = e; - // step 20 --> 27, handle captures array (we know it's empty for non regexp) + p = e; + + for (uint32 i = 0; i < z.capturesCount; i++) { + A->setProperty(cx, *numberToString(A->mLength), NULL, JSValue(z.captures[i])); + if (A->mLength == lim) + return JSValue(A); + } } - } static JSValue String_charAt(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc) @@ -616,7 +688,8 @@ Context::PrototypeFunctions *getStringProtos() { "localeCompare", Number_Type, 1, String_localeCompare }, { "match", Array_Type, 1, String_match }, { "replace", String_Type, 2, String_replace }, - { "slice", String_Type, 2, String_slice }, + { "search", Number_Type, 1, String_search }, + { "slice", String_Type, 2, String_slice }, { "split", Array_Type, 1, String_split }, // XXX ECMA spec says 2, but tests want 1 XXX { "substring", String_Type, 2, String_substring }, { "toSource", String_Type, 0, String_toString }, diff --git a/js2/src/world.h b/js2/src/world.h index 8f372b4873ac..71d30b44a14e 100644 --- a/js2/src/world.h +++ b/js2/src/world.h @@ -75,7 +75,15 @@ namespace JavaScript { public: StringAtomTable identifiers; - World(); + World(); + + /* Random number generator state, used by jsmath.c. */ + bool rngInitialized; + int64 rngMultiplier; + int64 rngAddend; + int64 rngMask; + int64 rngSeed; + float64 rngDscale; }; } #endif