diff --git a/js2/src/js2date.cpp b/js2/src/js2date.cpp index 3ddb6a79007..f1223ba60b3 100644 --- a/js2/src/js2date.cpp +++ b/js2/src/js2date.cpp @@ -817,17 +817,18 @@ static js2val Date_format(JS2Metadata *meta, float64 date, formatspec format) * requires a PRMJTime... which only has 16-bit years. Sub-ECMA. */ /* Tue Oct 31 09:41:40 GMT-0800 (PST) 2000 */ - printFormat(outf, "%s %s %.2d %.2d:%.2d:%.2d GMT%+.4d %s%s%.4d", + printFormat(outf, + "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s", days[WeekDay(local)], months[MonthFromTime(local)], DateFromTime(local), + YearFromTime(local), HourFromTime(local), MinFromTime(local), SecFromTime(local), offset, - usetz ? tzbuf : "", usetz ? " " : "", - YearFromTime(local)); + usetz ? tzbuf : ""); break; case FORMATSPEC_DATE: /* Tue Oct 31 2000 */ diff --git a/js2/src/js2error.cpp b/js2/src/js2error.cpp index 36cf9a27d4e..e7ea4d46547 100644 --- a/js2/src/js2error.cpp +++ b/js2/src/js2error.cpp @@ -57,11 +57,17 @@ namespace MetaData { js2val error_ConstructorCore(JS2Metadata *meta, JS2Class *errorClass, js2val arg) { - JS2Object *obj = new SimpleInstance(meta, OBJECT_TO_JS2VAL(errorClass->prototype), errorClass); + DEFINE_ROOTKEEPER(rk, arg); + JS2Object *obj = NULL; + DEFINE_ROOTKEEPER(rk1, obj); + obj = new SimpleInstance(meta, OBJECT_TO_JS2VAL(errorClass->prototype), errorClass); js2val thatValue = OBJECT_TO_JS2VAL(obj); if (!JS2VAL_IS_VOID(arg)) { - errorClass->WritePublic(meta, thatValue, &meta->world.identifiers["message"], true, meta->engine->allocString(meta->toString(arg))); + const String *str = NULL; + DEFINE_ROOTKEEPER(rk2, str); + str = meta->toString(arg); + errorClass->WritePublic(meta, thatValue, &meta->world.identifiers["message"], true, meta->engine->allocString(str)); } return thatValue; diff --git a/js2/src/js2metadata.h b/js2/src/js2metadata.h index 62576e3446a..2b35d1919b7 100644 --- a/js2/src/js2metadata.h +++ b/js2/src/js2metadata.h @@ -554,7 +554,7 @@ public: // The qualified name is to be inferred from the map where this binding is kept // QualifiedName qname; // The qualified name bound by this binding - virtual ~LocalBinding() { delete content; } + virtual ~LocalBinding() { /*delete content;*/ } // XXX what about aliases!!! AccessSet accesses; LocalMember *content; // The member to which this qualified name was bound diff --git a/js2/src/js2op_invocation.cpp b/js2/src/js2op_invocation.cpp index eb2f609b74b..4fb01e36688 100644 --- a/js2/src/js2op_invocation.cpp +++ b/js2/src/js2op_invocation.cpp @@ -180,6 +180,7 @@ doCall: js2val exec_fnVal; if (!meta->regexpClass->ReadPublic(meta, &b, &meta->world.identifiers["exec"], RunPhase, &exec_fnVal)) ASSERT(false); + a = b; ASSERT(JS2VAL_IS_OBJECT(exec_fnVal)); fObj = JS2VAL_TO_OBJECT(exec_fnVal); goto doCall; diff --git a/js2/src/js2regexp.cpp b/js2/src/js2regexp.cpp index 74cc723a964..f7b5389118d 100644 --- a/js2/src/js2regexp.cpp +++ b/js2/src/js2regexp.cpp @@ -149,7 +149,7 @@ namespace MetaData { return meta->engine->allocString(result); } - js2val RegExp_exec(JS2Metadata *meta, const js2val thisValue, js2val *argv, uint32 argc) + js2val RegExp_exec_sub(JS2Metadata *meta, const js2val thisValue, js2val *argv, uint32 argc, bool test) { if (!JS2VAL_IS_OBJECT(thisValue) || (JS2VAL_TO_OBJECT(thisValue)->kind != SimpleInstanceKind) @@ -157,59 +157,115 @@ namespace MetaData { meta->reportError(Exception::typeError, "RegExp.exec can only be applied to RegExp objects", meta->engine->errorPos()); RegExpInstance *thisInst = checked_cast(JS2VAL_TO_OBJECT(thisValue)); - js2val result = JS2VAL_NULL; - if (argc > 0) { - uint32 index = 0; + const String *str = NULL; + DEFINE_ROOTKEEPER(rk0, str); - const String *str = meta->toString(argv[0]); - js2val globalMultiline = thisInst->getMultiline(meta); + js2val regexpClassVal = OBJECT_TO_JS2VAL(meta->regexpClass); + js2val result = JS2VAL_NULL; + if (argc == 0) { + js2val inputVal; + if (!meta->classClass->ReadPublic(meta, ®expClassVal, meta->engine->allocStringPtr("input"), RunPhase, &inputVal)) + ASSERT(false); + str = meta->toString(inputVal); + } + else + str = meta->toString(argv[0]); - if (meta->toBoolean(thisInst->getGlobal(meta))) { - js2val lastIndexVal = thisInst->getLastIndex(meta); - float64 lastIndex = meta->toFloat64(lastIndexVal); - if ((lastIndex < 0) || (lastIndex > str->length())) { - thisInst->setLastIndex(meta, meta->engine->allocNumber(0.0)); - return result; - } - index = meta->engine->float64toUInt32(lastIndex); + uint32 index = 0; + js2val globalMultiline; + if (!meta->classClass->ReadPublic(meta, ®expClassVal, meta->engine->allocStringPtr("multiline"), RunPhase, &globalMultiline)) + ASSERT(false); + + if (meta->toBoolean(thisInst->getGlobal(meta))) { + js2val lastIndexVal = thisInst->getLastIndex(meta); + float64 lastIndex = meta->toFloat64(lastIndexVal); + if ((lastIndex < 0) || (lastIndex > str->length())) { + thisInst->setLastIndex(meta, meta->engine->allocNumber(0.0)); + return result; } - REMatchResult *match = REExecute(meta, thisInst->mRegExp, str->begin(), index, toUInt32(str->length()), meta->toBoolean(globalMultiline)); - if (match) { - ArrayInstance *A = new ArrayInstance(meta, meta->arrayClass->prototype, meta->arrayClass); - DEFINE_ROOTKEEPER(rk, A); - result = OBJECT_TO_JS2VAL(A); - js2val matchStr = meta->engine->allocString(str->substr((uint32)match->startIndex, (uint32)match->endIndex - match->startIndex)); - meta->createDynamicProperty(A, meta->engine->numberToString((long)0), matchStr, ReadWriteAccess, false, true); - for (int32 i = 0; i < match->parenCount; i++) { - if (match->parens[i].index != -1) { - js2val parenStr = meta->engine->allocString(str->substr((uint32)(match->parens[i].index), (uint32)(match->parens[i].length))); - meta->createDynamicProperty(A, meta->engine->numberToString(i + 1), parenStr, ReadWriteAccess, false, true); - } - else - meta->createDynamicProperty(A, meta->engine->numberToString(i + 1), JS2VAL_UNDEFINED, ReadWriteAccess, false, true); - } - setLength(meta, A, match->parenCount + 1); - - meta->createDynamicProperty(A, meta->engine->allocStringPtr("index"), meta->engine->allocNumber((float64)(match->startIndex)), ReadWriteAccess, false, true); - meta->createDynamicProperty(A, meta->engine->allocStringPtr("input"), meta->engine->allocString(str), ReadWriteAccess, false, true); - - meta->stringClass->WritePublic(meta, OBJECT_TO_JS2VAL(meta->regexpClass), meta->engine->allocStringPtr("lastMatch"), true, matchStr); - js2val leftContextVal = meta->engine->allocString(str->substr(0, (uint32)match->startIndex)); - meta->stringClass->WritePublic(meta, OBJECT_TO_JS2VAL(meta->regexpClass), meta->engine->allocStringPtr("leftContext"), true, matchStr); - js2val rightContextVal = meta->engine->allocString(str->substr((uint32)match->endIndex, (uint32)str->length() - match->endIndex)); - meta->stringClass->WritePublic(meta, OBJECT_TO_JS2VAL(meta->regexpClass), meta->engine->allocStringPtr("rightContext"), true, matchStr); - - if (meta->toBoolean(thisInst->getGlobal(meta))) { - index = match->endIndex; - thisInst->setLastIndex(meta, meta->engine->allocNumber((float64)index)); - } - + index = meta->engine->float64toUInt32(lastIndex); + } + REMatchResult *match = REExecute(meta, thisInst->mRegExp, str->begin(), index, toUInt32(str->length()), meta->toBoolean(globalMultiline)); + if (match) { + if (meta->toBoolean(thisInst->getGlobal(meta))) { + index = match->endIndex; + thisInst->setLastIndex(meta, meta->engine->allocNumber((float64)index)); } - +// construct the result array and set $1.. in RegExp statics + ArrayInstance *A = NULL; + DEFINE_ROOTKEEPER(rk, A); + if (test) + result = JS2VAL_TRUE; + else { + A = new ArrayInstance(meta, 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)); + DEFINE_ROOTKEEPER(rk1, matchStr); + js2val inputStr = meta->engine->allocString(str); + DEFINE_ROOTKEEPER(rk2, inputStr); + if (!test) + meta->createDynamicProperty(A, meta->engine->numberToString((long)0), matchStr, ReadWriteAccess, false, true); + js2val parenStr = JS2VAL_VOID; + DEFINE_ROOTKEEPER(rk3, parenStr); + if (match->parenCount == 0) // arrange to set the lastParen to "", not undefined (it's a non-ecma 1.2 thing) + parenStr = meta->engine->allocString(""); + for (int32 i = 0; i < match->parenCount; i++) { + if (match->parens[i].index != -1) { + parenStr = meta->engine->allocString(str->substr((uint32)(match->parens[i].index), (uint32)(match->parens[i].length))); + if (!test) + meta->createDynamicProperty(A, meta->engine->numberToString(i + 1), parenStr, ReadWriteAccess, false, true); + if (i < 9) { // 0-->8 maps to $1 thru $9 + char name[3] = "$0"; + name[1] = '1' + i; + String staticName(widenCString(name)); + meta->classClass->WritePublic(meta, regexpClassVal, meta->engine->allocStringPtr(&staticName), true, parenStr); + } + } + else { + if (!test) + meta->createDynamicProperty(A, meta->engine->numberToString(i + 1), JS2VAL_UNDEFINED, ReadWriteAccess, false, true); + if (i < 9) { // 0-->8 maps to $1 thru $9 + char name[3] = "$0"; + name[1] = '1' + i; + String staticName(widenCString(name)); + meta->classClass->WritePublic(meta, regexpClassVal, meta->engine->allocStringPtr(&staticName), true, JS2VAL_UNDEFINED); + } + } + } + if (!test) { + setLength(meta, A, match->parenCount + 1); + +// add 'index' and 'input' properties to the result array + meta->createDynamicProperty(A, meta->engine->allocStringPtr("index"), meta->engine->allocNumber((float64)(match->startIndex)), ReadWriteAccess, false, true); + meta->createDynamicProperty(A, meta->engine->allocStringPtr("input"), inputStr, ReadWriteAccess, false, true); + } +// set other RegExp statics + meta->classClass->WritePublic(meta, regexpClassVal, meta->engine->allocStringPtr("input"), true, inputStr); + meta->classClass->WritePublic(meta, regexpClassVal, meta->engine->allocStringPtr("lastMatch"), true, matchStr); + meta->classClass->WritePublic(meta, regexpClassVal, meta->engine->allocStringPtr("lastParen"), true, parenStr); + js2val leftContextVal = meta->engine->allocString(str->substr(0, (uint32)match->startIndex)); + DEFINE_ROOTKEEPER(rk4, leftContextVal); + meta->classClass->WritePublic(meta, regexpClassVal, meta->engine->allocStringPtr("leftContext"), true, leftContextVal); + js2val rightContextVal = meta->engine->allocString(str->substr((uint32)match->endIndex, (uint32)str->length() - match->endIndex)); + DEFINE_ROOTKEEPER(rk5, rightContextVal); + meta->classClass->WritePublic(meta, regexpClassVal, meta->engine->allocStringPtr("rightContext"), true, rightContextVal); } return result; + } + js2val RegExp_exec(JS2Metadata *meta, const js2val thisValue, js2val *argv, uint32 argc) + { + return RegExp_exec_sub(meta, thisValue, argv, argc, false); } + js2val RegExp_test(JS2Metadata *meta, const js2val thisValue, js2val *argv, uint32 argc) + { + js2val result = RegExp_exec_sub(meta, thisValue, argv, argc, true); + if (result != JS2VAL_TRUE) + result = JS2VAL_FALSE; + return result; + } + js2val RegExp_Call(JS2Metadata *meta, const js2val thisValue, js2val *argv, uint32 argc) { if ((argc > 0) @@ -222,11 +278,13 @@ namespace MetaData { return RegExp_Constructor(meta, thisValue, argv, argc); } - js2val RegExp_Constructor(JS2Metadata *meta, const js2val /* thisValue */, js2val *argv, uint32 argc) + js2val RegExp_compile(JS2Metadata *meta, const js2val thisValue, js2val *argv, uint32 argc) { - RegExpInstance *thisInst = new RegExpInstance(meta, meta->regexpClass->prototype, meta->regexpClass); - DEFINE_ROOTKEEPER(rk, thisInst); - js2val thatValue = OBJECT_TO_JS2VAL(thisInst); + if (!JS2VAL_IS_OBJECT(thisValue) + || (JS2VAL_TO_OBJECT(thisValue)->kind != SimpleInstanceKind) + || (checked_cast(JS2VAL_TO_OBJECT(thisValue))->type != meta->regexpClass)) + meta->reportError(Exception::typeError, "RegExp.compile can only be applied to RegExp objects", meta->engine->errorPos()); + RegExpInstance *thisInst = checked_cast(JS2VAL_TO_OBJECT(thisValue)); uint32 flags = 0; const String *regexpStr = meta->engine->Empty_StringAtom; @@ -267,29 +325,71 @@ namespace MetaData { } else meta->reportError(Exception::syntaxError, "Failed to parse RegExp : '{0}'", meta->engine->errorPos(), "/" + *regexpStr + "/" + *flagStr); // XXX what about the RE parser error message? - return thatValue; + return thisValue; + } + + js2val RegExp_Constructor(JS2Metadata *meta, const js2val /* thisValue */, js2val *argv, uint32 argc) + { + RegExpInstance *thisInst = new RegExpInstance(meta, meta->regexpClass->prototype, meta->regexpClass); + DEFINE_ROOTKEEPER(rk, thisInst); + js2val thatValue = OBJECT_TO_JS2VAL(thisInst); + return RegExp_compile(meta, thatValue, argv, argc); } void initRegExpObject(JS2Metadata *meta) { + uint32 i; FunctionData prototypeFunctions[] = { { "toString", 0, RegExp_toString }, { "exec", 0, RegExp_exec }, + { "test", 0, RegExp_test }, + { "compile", 0, RegExp_compile }, { NULL } }; + +#define STATIC_VAR_COUNT (15) + + struct { + char *name; + char *aliasName; + JS2Class *type; + } RegExpStaticVars[STATIC_VAR_COUNT] = { + { "input", "$_", meta->stringClass }, + { "multiline", "$*", meta->booleanClass }, + { "lastMatch", "$&", meta->stringClass }, + { "lastParen", "$+", meta->objectClass }, + { "leftContext", "$`", meta->stringClass }, + { "rightContext", "$'", meta->stringClass }, + + { "$1", NULL, meta->objectClass }, + { "$2", NULL, meta->objectClass }, + { "$3", NULL, meta->objectClass }, + { "$4", NULL, meta->objectClass }, + { "$5", NULL, meta->objectClass }, + { "$6", NULL, meta->objectClass }, + { "$7", NULL, meta->objectClass }, + { "$8", NULL, meta->objectClass }, + { "$9", NULL, meta->objectClass }, + }; + meta->initBuiltinClass(meta->regexpClass, NULL, RegExp_Constructor, RegExp_Call); meta->env->addFrame(meta->regexpClass); + for (i = 0; i < STATIC_VAR_COUNT; i++) { - Variable *v = new Variable(meta->stringClass, meta->engine->allocString(""), false); - meta->defineLocalMember(meta->env, &meta->world.identifiers["lastMatch"], NULL, Attribute::NoOverride, false, ReadWriteAccess, v, 0, false); - v = new Variable(meta->stringClass, meta->engine->allocString(""), false); - meta->defineLocalMember(meta->env, &meta->world.identifiers["leftContext"], NULL, Attribute::NoOverride, false, ReadWriteAccess, v, 0, false); - v = new Variable(meta->stringClass, meta->engine->allocString(""), false); - meta->defineLocalMember(meta->env, &meta->world.identifiers["rightContext"], NULL, Attribute::NoOverride, false, ReadWriteAccess, v, 0, false); - } + Variable *v = new Variable(); + v->type = RegExpStaticVars[i].type; + if (RegExpStaticVars[i].type == meta->stringClass) + v->value = meta->engine->allocString(""); + else + if (RegExpStaticVars[i].type == meta->booleanClass) + v->value = JS2VAL_FALSE; + meta->defineLocalMember(meta->env, &meta->world.identifiers[RegExpStaticVars[i].name], NULL, Attribute::NoOverride, false, ReadWriteAccess, v, 0, false); + if (RegExpStaticVars[i].aliasName) + meta->defineLocalMember(meta->env, &meta->world.identifiers[RegExpStaticVars[i].aliasName], NULL, Attribute::NoOverride, false, ReadWriteAccess, v, 0, false); + } meta->env->removeTopFrame(); NamespaceList publicNamespaceList; @@ -308,7 +408,7 @@ namespace MetaData { { "lastIndex", meta->numberClass }, }; - for (uint32 i = 0; i < INSTANCE_VAR_COUNT; i++) + for (i = 0; i < INSTANCE_VAR_COUNT; i++) { Multiname *mn = new Multiname(meta->engine->allocStringPtr(RegExpInstanceVars[i].name), &publicNamespaceList); InstanceMember *m = new InstanceVariable(mn, RegExpInstanceVars[i].type, false, true, true, meta->regexpClass->slotCount++); diff --git a/js2/src/js2string.cpp b/js2/src/js2string.cpp index 246d15802b5..f581c070f33 100644 --- a/js2/src/js2string.cpp +++ b/js2/src/js2string.cpp @@ -160,7 +160,7 @@ static js2val String_match(JS2Metadata *meta, const js2val thisValue, js2val *ar js2val S = STRING_TO_JS2VAL(meta->toString(thisValue)); js2val regexp = argv[0]; - if ((argc == 0) || (meta->objectType(thisValue) != meta->regexpClass)) { + if ((argc == 0) || (meta->objectType(argv[0]) != meta->regexpClass)) { regexp = JS2VAL_NULL; regexp = RegExp_Constructor(meta, regexp, argv, 1); } @@ -172,12 +172,18 @@ static js2val String_match(JS2Metadata *meta, const js2val thisValue, js2val *ar return RegExp_exec(meta, regexp, &S, 1); } else { + js2val globalMultilineVal; + js2val regexpClassVal = OBJECT_TO_JS2VAL(meta->regexpClass); + if (!meta->classClass->ReadPublic(meta, ®expClassVal, meta->engine->allocStringPtr("multiline"), RunPhase, &globalMultilineVal)) + ASSERT(false); + bool globalMultiline = meta->toBoolean(globalMultilineVal); + ArrayInstance *A = new ArrayInstance(meta, meta->arrayClass->prototype, meta->arrayClass); DEFINE_ROOTKEEPER(rk, A); int32 index = 0; int32 lastIndex = 0; while (true) { - REMatchResult *match = REExecute(meta, re, JS2VAL_TO_STRING(S)->begin(), lastIndex, toInt32(JS2VAL_TO_STRING(S)->length()), false); + REMatchResult *match = REExecute(meta, re, JS2VAL_TO_STRING(S)->begin(), lastIndex, toInt32(JS2VAL_TO_STRING(S)->length()), globalMultiline); if (match == NULL) break; if (lastIndex == match->endIndex)