diff --git a/mozilla/js2/src/js2array.cpp b/mozilla/js2/src/js2array.cpp index a914ea0270f..af5d1078397 100644 --- a/mozilla/js2/src/js2array.cpp +++ b/mozilla/js2/src/js2array.cpp @@ -68,9 +68,20 @@ uint32 getLength(JS2Metadata *meta, JS2Object *obj) return length; } -js2val setLength(JS2Metadata *meta, JS2Object *obj, uint32 length) +js2val setLength(JS2Metadata *meta, JS2Object *obj, uint32 newLength) { - js2val result = meta->engine->allocNumber(length); + js2val result = meta->engine->allocNumber(newLength); + + uint32 length = getLength(meta, obj); + if (newLength < length) { + // need to delete all the elements above the new length + LookupKind lookup(false, JS2VAL_NULL); + bool deleteResult; + for (uint32 i = newLength; i < length; i++) { + meta->mn1->name = meta->engine->numberToString(i); + meta->deleteProperty(OBJECT_TO_JS2VAL(obj), meta->mn1, &lookup, RunPhase, &deleteResult); + } + } if (obj->kind == PrototypeInstanceKind) { // Can't call 'writeDynamicProperty' as that'll just cycle back here for @@ -81,14 +92,6 @@ js2val setLength(JS2Metadata *meta, JS2Object *obj, uint32 length) i->second.value = result; return result; } -/* - for (DynamicPropertyIterator i = dMap->begin(), end = dMap->end(); (i != end); i++) { - if (i->first == *meta->engine->length_StringAtom) { - i->second.value = result; - return result; - } - } -*/ const DynamicPropertyMap::value_type e(*meta->engine->length_StringAtom, DynamicPropertyValue(result, DynamicPropertyValue::PERMANENT)); checked_cast(obj)->dynamicProperties.insert(e); } @@ -540,7 +543,7 @@ static int32 sort_compare(js2val *a, js2val *b, CompareArgs *arg) js2val argv[2]; argv[0] = av; argv[1] = bv; - js2val v = JS2VAL_UNDEFINED;// XXX = cx->invokeFunction(ca->target, kNullValue, argv, 2); + js2val v = meta->invokeFunction(ca->target, JS2VAL_NULL, argv, 2); float64 f = meta->toFloat64(v); if (JSDOUBLE_IS_NaN(f) || (f == 0)) result = 0; diff --git a/mozilla/js2/src/js2date.cpp b/mozilla/js2/src/js2date.cpp index 0cf6849da41..8cb8298f486 100644 --- a/mozilla/js2/src/js2date.cpp +++ b/mozilla/js2/src/js2date.cpp @@ -392,7 +392,7 @@ static js2val Date_makeTime(JS2Metadata *meta, const js2val thisValue, js2val *a *date = nan; return meta->engine->nanValue; } - args[i] = (float64)(JS2Engine::float64toInt32(f)); + args[i] = JS2Engine::truncateFloat64(f); } if (local) @@ -853,6 +853,18 @@ static js2val Date_format(JS2Metadata *meta, float64 date, formatspec format) return meta->engine->allocString(outf.getString()); } +js2val Date_Call(JS2Metadata *meta, const js2val /* thisValue */, js2val *argv, uint32 argc) +{ + int64 us, ms, us2ms; + float64 msec_time; + + us = PRMJ_Now(); + JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC); + JSLL_DIV(ms, us, us2ms); + JSLL_L2D(msec_time, ms); + + return Date_format(meta, msec_time, FORMATSPEC_FULL); +} #define MAXARGS 7 js2val Date_Constructor(JS2Metadata *meta, const js2val /* thisValue */, js2val *argv, uint32 argc) @@ -902,7 +914,7 @@ js2val Date_Constructor(JS2Metadata *meta, const js2val /* thisValue */, js2val JS2Object::removeRoot(ri); return thatValue; } - array[loop] = meta->engine->allocNumber(JS2Engine::float64toInt32(double_arg)); + array[loop] = JS2Engine::float64toInt32(double_arg); } else { if (loop == 2) { array[loop] = 1; /* Default the date argument to 1. */ @@ -1471,6 +1483,7 @@ void initDateObject(JS2Metadata *meta) LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond); meta->dateClass->construct = Date_Constructor; + meta->dateClass->call = Date_Call; NamespaceList publicNamespaceList; publicNamespaceList.push_back(meta->publicNamespace); @@ -1484,6 +1497,16 @@ void initDateObject(JS2Metadata *meta) 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); + + // "parse" & "UTC" as static members: + SimpleInstance *callInst = new SimpleInstance(meta->functionClass); + callInst->fWrap = new FunctionWrapper(true, new ParameterFrame(JS2VAL_INACCESSIBLE, true), Date_parse); + v = new Variable(meta->functionClass, OBJECT_TO_JS2VAL(callInst), true); + meta->defineLocalMember(meta->env, &meta->world.identifiers["parse"], &publicNamespaceList, Attribute::NoOverride, false, ReadWriteAccess, v, 0); + callInst = new SimpleInstance(meta->functionClass); + callInst->fWrap = new FunctionWrapper(true, new ParameterFrame(JS2VAL_INACCESSIBLE, true), Date_UTC); + v = new Variable(meta->functionClass, OBJECT_TO_JS2VAL(callInst), true); + meta->defineLocalMember(meta->env, &meta->world.identifiers["UTC"], &publicNamespaceList, Attribute::NoOverride, false, ReadWriteAccess, v, 0); meta->env->removeTopFrame(); diff --git a/mozilla/js2/src/js2eval.cpp b/mozilla/js2/src/js2eval.cpp index 55447b07d39..5bfb64f43ab 100644 --- a/mozilla/js2/src/js2eval.cpp +++ b/mozilla/js2/src/js2eval.cpp @@ -195,6 +195,379 @@ namespace MetaData { return checked_cast(obj); } + // Invoke the named function on the thisValue object (it is an object) + // Returns false if no callable function exists. Otherwise return the + // function result value. + bool JS2Metadata::invokeFunctionOnObject(js2val thisValue, const String *fnName, js2val &result) + { + Multiname mn(fnName, publicNamespace); + LookupKind lookup(true, JS2VAL_NULL); // XXX using lexical lookup since we want readProperty to fail + // if the function isn't defined + js2val fnVal; + + if (readProperty(&thisValue, &mn, &lookup, RunPhase, &fnVal)) { + if (JS2VAL_IS_OBJECT(fnVal)) { + JS2Object *fnObj = JS2VAL_TO_OBJECT(fnVal); + result = invokeFunction(fnObj, thisValue, NULL, 0); + return true; +/* + FunctionWrapper *fWrap = NULL; + if ((fnObj->kind == SimpleInstanceKind) + && (objectType(fnVal) == functionClass)) { + fWrap = (checked_cast(fnObj))->fWrap; + } + else + if ((fnObj->kind == PrototypeInstanceKind) + && ((checked_cast(fnObj))->type == functionClass)) { + fWrap = (checked_cast(fnObj))->fWrap; + } + else + if (fnObj->kind == MethodClosureKind) { + // XXX here we ignore the bound this, can that be right? + MethodClosure *mc = checked_cast(fnObj); + fWrap = mc->method->fInst->fWrap; + } + if (fWrap) { + if (fWrap->code) { + result = (fWrap->code)(this, thisValue, NULL, 0); + return true; + } + else { + uint8 *savePC = NULL; + BytecodeContainer *bCon = fWrap->bCon; + + CompilationData *oldData = startCompilationUnit(bCon, bCon->mSource, bCon->mSourceLocation); + ParameterFrame *runtimeFrame = new ParameterFrame(fWrap->compileFrame); + runtimeFrame->instantiate(env); + runtimeFrame->thisObject = thisValue; + Frame *oldTopFrame = env->getTopFrame(); + env->addFrame(runtimeFrame); + try { + savePC = engine->pc; + engine->pc = NULL; + result = engine->interpret(RunPhase, bCon); + } + catch (Exception &x) { + engine->pc = savePC; + restoreCompilationUnit(oldData); + env->setTopFrame(oldTopFrame); + throw x; + } + engine->pc = savePC; + restoreCompilationUnit(oldData); + env->setTopFrame(oldTopFrame); + return true; + } + } +*/ + } + } + return false; + } + + // Invoke the named function, no args, by looking it up + // the current scope. (Do this by emitting and executing the + // appropriate bytecode sequence) + js2val JS2Metadata::invokeFunction(const char *fname) + { + js2val retval; + uint8 *savePC = NULL; + + CompilationData *oldData = startCompilationUnit(NULL, bCon->mSource, bCon->mSourceLocation); + try { + LexicalReference rVal(&world.identifiers[widenCString(fname)], false); + rVal.emitReadForInvokeBytecode(bCon, 0); + bCon->emitOp(eCall, 0, -(0 + 2) + 1); // pop argCount args, the base & function, and push a result + bCon->addShort(0); + bCon->emitOp(eReturn, 0); + savePC = engine->pc; + engine->pc = NULL; + retval = engine->interpret(RunPhase, bCon); + } + catch (Exception &x) { + engine->pc = savePC; + restoreCompilationUnit(oldData); + throw x; + } + engine->pc = savePC; + restoreCompilationUnit(oldData); + return retval; + } + + js2val JS2Metadata::invokeFunction(JS2Object *fnObj, js2val thisValue, js2val *argv, uint32 argc) + { + js2val result = JS2VAL_UNDEFINED; + + FunctionWrapper *fWrap = NULL; + if ((fnObj->kind == SimpleInstanceKind) + && ((checked_cast(fnObj))->type == functionClass)) { + fWrap = (checked_cast(fnObj))->fWrap; + } + else + if ((fnObj->kind == PrototypeInstanceKind) + && ((checked_cast(fnObj))->type == functionClass)) { + fWrap = (checked_cast(fnObj))->fWrap; + } + else + if (fnObj->kind == MethodClosureKind) { + // XXX here we ignore the bound this, can that be right? + MethodClosure *mc = checked_cast(fnObj); + fWrap = mc->method->fInst->fWrap; + } + if (fWrap) { + if (fWrap->code) { + result = (fWrap->code)(this, thisValue, argv, argc); + } + else { + uint8 *savePC = NULL; + BytecodeContainer *bCon = fWrap->bCon; + + CompilationData *oldData = startCompilationUnit(bCon, bCon->mSource, bCon->mSourceLocation); + ParameterFrame *runtimeFrame = new ParameterFrame(fWrap->compileFrame); + JS2Object::RootIterator ri = JS2Object::addRoot(&runtimeFrame); + runtimeFrame->instantiate(env); + runtimeFrame->thisObject = thisValue; + runtimeFrame->assignArguments(this, argv, argc); + Frame *oldTopFrame = env->getTopFrame(); + env->addFrame(runtimeFrame); + try { + savePC = engine->pc; + engine->pc = NULL; + result = engine->interpret(RunPhase, bCon); + } + catch (Exception &x) { + engine->pc = savePC; + restoreCompilationUnit(oldData); + env->setTopFrame(oldTopFrame); + JS2Object::removeRoot(ri); + throw x; + } + engine->pc = savePC; + restoreCompilationUnit(oldData); + env->setTopFrame(oldTopFrame); + JS2Object::removeRoot(ri); + } + } + return result; + } + + // Save off info about the current compilation and begin a + // new one - using the given parser. + CompilationData *JS2Metadata::startCompilationUnit(BytecodeContainer *newBCon, const String &source, const String &sourceLocation) + { + CompilationData *result = new CompilationData(); + result->compilation_bCon = bCon; + result->execution_bCon = engine->bCon; + bConList.push_back(bCon); + + if (newBCon) + bCon = newBCon; + else + bCon = new BytecodeContainer(); + bCon->mSource = source; + bCon->mSourceLocation = sourceLocation; + engine->bCon = bCon; + + return result; + } + + // Restore the compilation data, and then delete the cached copy. + void JS2Metadata::restoreCompilationUnit(CompilationData *oldData) + { + BytecodeContainer *xbCon = bConList.back(); + ASSERT(oldData->compilation_bCon == xbCon); + bConList.pop_back(); + + bCon = oldData->compilation_bCon; + engine->bCon = oldData->execution_bCon; + + delete oldData; + } + + // x is not a String + const String *JS2Metadata::convertValueToString(js2val x) + { + if (JS2VAL_IS_UNDEFINED(x)) + return engine->undefined_StringAtom; + if (JS2VAL_IS_NULL(x)) + return engine->null_StringAtom; + if (JS2VAL_IS_BOOLEAN(x)) + return (JS2VAL_TO_BOOLEAN(x)) ? engine->true_StringAtom : engine->false_StringAtom; + if (JS2VAL_IS_INT(x)) + return engine->numberToString(JS2VAL_TO_INT(x)); + if (JS2VAL_IS_LONG(x)) { + float64 d; + JSLL_L2D(d, *JS2VAL_TO_LONG(x)); + return engine->numberToString(&d); + } + if (JS2VAL_IS_ULONG(x)) { + float64 d; + JSLL_UL2D(d, *JS2VAL_TO_ULONG(x)); + return engine->numberToString(&d); + } + if (JS2VAL_IS_FLOAT(x)) { + float64 d = *JS2VAL_TO_FLOAT(x); + return engine->numberToString(&d); + } + if (JS2VAL_IS_DOUBLE(x)) + return engine->numberToString(JS2VAL_TO_DOUBLE(x)); + return toString(toPrimitive(x, StringHint)); + } + + // x is not a primitive (it is an object and not null) + js2val JS2Metadata::convertValueToPrimitive(js2val x, Hint hint) + { + // return [[DefaultValue]] --> get property 'toString' and invoke it, + // if not available or result is not primitive then try property 'valueOf' + // if that's not available or returns a non primitive, throw a TypeError + + if (hint == StringHint) { + js2val result; + if (invokeFunctionOnObject(x, engine->toString_StringAtom, result)) { + if (JS2VAL_IS_PRIMITIVE(result)) + return result; + } + if (invokeFunctionOnObject(x, engine->valueOf_StringAtom, result)) { + if (JS2VAL_IS_PRIMITIVE(result)) + return result; + } + reportError(Exception::typeError, "DefaultValue failure", engine->errorPos()); + } + else { + js2val result; + if (invokeFunctionOnObject(x, engine->valueOf_StringAtom, result)) { + if (JS2VAL_IS_PRIMITIVE(result)) + return result; + } + if (invokeFunctionOnObject(x, engine->toString_StringAtom, result)) { + if (JS2VAL_IS_PRIMITIVE(result)) + return result; + } + reportError(Exception::typeError, "DefaultValue failure", engine->errorPos()); + } + return JS2VAL_VOID; + } + + // x is not a number + float64 JS2Metadata::convertValueToDouble(js2val x) + { + if (JS2VAL_IS_UNDEFINED(x)) + return nan; + if (JS2VAL_IS_NULL(x)) + return 0; + if (JS2VAL_IS_BOOLEAN(x)) + return (JS2VAL_TO_BOOLEAN(x)) ? 1.0 : 0.0; + if (JS2VAL_IS_STRING(x)) { + String *str = JS2VAL_TO_STRING(x); + const char16 *numEnd; + return stringToDouble(str->data(), str->data() + str->length(), numEnd); + } + if (JS2VAL_IS_INACCESSIBLE(x)) + reportError(Exception::compileExpressionError, "Inappropriate compile time expression", engine->errorPos()); + if (JS2VAL_IS_UNINITIALIZED(x)) + reportError(Exception::compileExpressionError, "Inappropriate compile time expression", engine->errorPos()); + return toFloat64(toPrimitive(x, NumberHint)); + } + + // x is not a number, convert it to one + js2val JS2Metadata::convertValueToGeneralNumber(js2val x) + { + // XXX Assuming convert to float64, rather than long/ulong + return engine->allocNumber(toFloat64(x)); + } + + // x is not an Object, it needs to be wrapped in one + js2val JS2Metadata::convertValueToObject(js2val x) + { + if (JS2VAL_IS_UNDEFINED(x) || JS2VAL_IS_NULL(x) || JS2VAL_IS_SPECIALREF(x)) + reportError(Exception::typeError, "Can't convert to Object", engine->errorPos()); + if (JS2VAL_IS_STRING(x)) + return String_Constructor(this, JS2VAL_NULL, &x, 1); + if (JS2VAL_IS_BOOLEAN(x)) + return Boolean_Constructor(this, JS2VAL_NULL, &x, 1); + if (JS2VAL_IS_NUMBER(x)) + return Number_Constructor(this, JS2VAL_NULL, &x, 1); + NOT_REACHED("unsupported value type"); + return JS2VAL_VOID; + } + + // x is any js2val + float64 JS2Metadata::toFloat64(js2val x) + { + if (JS2VAL_IS_INT(x)) + return JS2VAL_TO_INT(x); + else + if (JS2VAL_IS_DOUBLE(x)) + return *JS2VAL_TO_DOUBLE(x); + else + if (JS2VAL_IS_LONG(x)) { + float64 d; + JSLL_L2D(d, *JS2VAL_TO_LONG(x)); + return d; + } + else + if (JS2VAL_IS_ULONG(x)) { + float64 d; + JSLL_UL2D(d, *JS2VAL_TO_ULONG(x)); + return d; + } + else + if (JS2VAL_IS_FLOAT(x)) + return *JS2VAL_TO_FLOAT(x); + else + return convertValueToDouble(x); + } + + // x is not a bool + bool JS2Metadata::convertValueToBoolean(js2val x) + { + if (JS2VAL_IS_UNDEFINED(x)) + return false; + if (JS2VAL_IS_NULL(x)) + return false; + if (JS2VAL_IS_INT(x)) + return (JS2VAL_TO_INT(x) != 0); + if (JS2VAL_IS_LONG(x) || JS2VAL_IS_ULONG(x)) + return (!JSLL_IS_ZERO(x)); + if (JS2VAL_IS_FLOAT(x)) { + float64 xd = *JS2VAL_TO_FLOAT(x); + return ! (JSDOUBLE_IS_POSZERO(xd) || JSDOUBLE_IS_NEGZERO(xd) || JSDOUBLE_IS_NaN(xd)); + } + if (JS2VAL_IS_DOUBLE(x)) { + float64 xd = *JS2VAL_TO_DOUBLE(x); + return ! (JSDOUBLE_IS_POSZERO(xd) || JSDOUBLE_IS_NEGZERO(xd) || JSDOUBLE_IS_NaN(xd)); + } + if (JS2VAL_IS_STRING(x)) { + String *str = JS2VAL_TO_STRING(x); + return (str->length() != 0); + } + return true; + } + + // x is not an int + int32 JS2Metadata::convertValueToInteger(js2val x) + { + int32 i; + if (JS2VAL_IS_LONG(x)) { + JSLL_L2I(i, *JS2VAL_TO_LONG(x)); + return i; + } + if (JS2VAL_IS_ULONG(x)) { + JSLL_UL2I(i, *JS2VAL_TO_ULONG(x)); + return i; + } + if (JS2VAL_IS_FLOAT(x)) { + float64 f = *JS2VAL_TO_FLOAT(x); + return JS2Engine::float64toInt32(f); + } + if (JS2VAL_IS_DOUBLE(x)) { + float64 d = *JS2VAL_TO_DOUBLE(x); + return JS2Engine::float64toInt32(d); + } + float64 d = convertValueToDouble(x); + return JS2Engine::float64toInt32(d); + } + }; // namespace MetaData }; // namespace Javascript diff --git a/mozilla/js2/src/js2metadata.cpp b/mozilla/js2/src/js2metadata.cpp index ab41b95fdc9..7b565c7aa08 100644 --- a/mozilla/js2/src/js2metadata.cpp +++ b/mozilla/js2/src/js2metadata.cpp @@ -3392,13 +3392,6 @@ XXX see EvalAttributeExpression, where identifiers are being handled for now... DynamicPropertyIterator i = dMap->find(*name); if (i != dMap->end()) return obj; -/* - for (DynamicPropertyIterator i = dMap->begin(), end = dMap->end(); (i != end); i++) { - if (i->first == *name) { - return obj; - } - } -*/ if (isPrototypeInstance) { PrototypeInstance *pInst = checked_cast(obj); if (pInst->parent) @@ -3427,13 +3420,6 @@ XXX see EvalAttributeExpression, where identifiers are being handled for now... DynamicPropertyIterator i = dMap->find(*name); if (i != dMap->end()) return true; -/* - for (DynamicPropertyIterator i = dMap->begin(), end = dMap->end(); (i != end); i++) { - if (i->first == *name) { - return true; - } - } -*/ return false; } @@ -3467,14 +3453,6 @@ XXX see EvalAttributeExpression, where identifiers are being handled for now... *rval = i->second.value; return true; } -/* - for (DynamicPropertyIterator i = dMap->begin(), end = dMap->end(); (i != end); i++) { - if (i->first == *name) { - *rval = i->second; - return true; - } - } -*/ if (isPrototypeInstance) { PrototypeInstance *pInst = checked_cast(container); if (pInst->parent) @@ -3551,17 +3529,23 @@ XXX see EvalAttributeExpression, where identifiers are being handled for now... return false; // 'None' DynamicPropertyIterator i = dMap->find(*name); if (i != dMap->end()) { - i->second.value = newValue; + // special case handling for setting 'length' property of ArrayInstances + // XXX should handle this with dispatch to 'writeProperty' of each of + // the different dynamic map containing objects, and change current + // 'writeProperty' functions to 'addProperty'. + if ((container->kind == PrototypeInstanceKind) + && ((checked_cast(container))->type == arrayClass) + && (*name == *engine->length_StringAtom)) { + float64 f = toFloat64(newValue); + uint32 newLength = f; + if (f != newLength) + reportError(Exception::rangeError, "Invalid length value", engine->errorPos()); + setLength(this, container, newLength); + } + else + i->second.value = newValue; return true; } -/* - for (DynamicPropertyIterator i = dMap->begin(), end = dMap->end(); (i != end); i++) { - if (i->first == *name) { - i->second.value = newValue; - return true; - } - } -*/ if (!createIfMissing) return false; if (container->kind == SimpleInstanceKind) { @@ -3997,15 +3981,6 @@ deleteClassProperty: return true; } } -/* - for (DynamicPropertyIterator i = dMap->begin(), end = dMap->end(); (i != end); i++) { - if (i->first == *name) { - dMap->erase(i); - *result = true; - return true; - } - } -*/ return false; } @@ -4227,315 +4202,6 @@ deleteClassProperty: } - // x is not a String - const String *JS2Metadata::convertValueToString(js2val x) - { - if (JS2VAL_IS_UNDEFINED(x)) - return engine->undefined_StringAtom; - if (JS2VAL_IS_NULL(x)) - return engine->null_StringAtom; - if (JS2VAL_IS_BOOLEAN(x)) - return (JS2VAL_TO_BOOLEAN(x)) ? engine->true_StringAtom : engine->false_StringAtom; - if (JS2VAL_IS_INT(x)) - return engine->numberToString(JS2VAL_TO_INT(x)); - if (JS2VAL_IS_LONG(x)) { - float64 d; - JSLL_L2D(d, *JS2VAL_TO_LONG(x)); - return engine->numberToString(&d); - } - if (JS2VAL_IS_ULONG(x)) { - float64 d; - JSLL_UL2D(d, *JS2VAL_TO_ULONG(x)); - return engine->numberToString(&d); - } - if (JS2VAL_IS_FLOAT(x)) { - float64 d = *JS2VAL_TO_FLOAT(x); - return engine->numberToString(&d); - } - if (JS2VAL_IS_DOUBLE(x)) - return engine->numberToString(JS2VAL_TO_DOUBLE(x)); - return toString(toPrimitive(x, StringHint)); - } - - // Invoke the named function on the thisValue object (it is an object) - // Returns false if no callable function exists. Otherwise return the - // function result value. - bool JS2Metadata::invokeFunctionOnObject(js2val thisValue, const String *fnName, js2val &result) - { - Multiname mn(fnName, publicNamespace); - LookupKind lookup(true, JS2VAL_NULL); // XXX using lexical lookup since we want readProperty to fail - // if the function isn't defined - js2val fnVal; - - if (readProperty(&thisValue, &mn, &lookup, RunPhase, &fnVal)) { - if (JS2VAL_IS_OBJECT(fnVal)) { - JS2Object *fnObj = JS2VAL_TO_OBJECT(fnVal); - FunctionWrapper *fWrap = NULL; - if ((fnObj->kind == SimpleInstanceKind) - && (objectType(fnVal) == functionClass)) { - fWrap = (checked_cast(fnObj))->fWrap; - } - else - if ((fnObj->kind == PrototypeInstanceKind) - && ((checked_cast(fnObj))->type == functionClass)) { - fWrap = (checked_cast(fnObj))->fWrap; - } - else - if (fnObj->kind == MethodClosureKind) { - // XXX here we ignore the bound this, can that be right? - MethodClosure *mc = checked_cast(fnObj); - fWrap = mc->method->fInst->fWrap; - } - if (fWrap) { - if (fWrap->code) { - result = (fWrap->code)(this, thisValue, NULL, 0); - return true; - } - else { - uint8 *savePC = NULL; - BytecodeContainer *bCon = fWrap->bCon; - - CompilationData *oldData = startCompilationUnit(bCon, bCon->mSource, bCon->mSourceLocation); - ParameterFrame *runtimeFrame = new ParameterFrame(fWrap->compileFrame); - runtimeFrame->instantiate(env); - runtimeFrame->thisObject = thisValue; - Frame *oldTopFrame = env->getTopFrame(); - env->addFrame(runtimeFrame); - try { - savePC = engine->pc; - engine->pc = NULL; - result = engine->interpret(RunPhase, bCon); - } - catch (Exception &x) { - engine->pc = savePC; - restoreCompilationUnit(oldData); - env->setTopFrame(oldTopFrame); - throw x; - } - engine->pc = savePC; - restoreCompilationUnit(oldData); - env->setTopFrame(oldTopFrame); - return true; - } - } - } - } - return false; - } - - // x is not a primitive (it is an object and not null) - js2val JS2Metadata::convertValueToPrimitive(js2val x, Hint hint) - { - // return [[DefaultValue]] --> get property 'toString' and invoke it, - // if not available or result is not primitive then try property 'valueOf' - // if that's not available or returns a non primitive, throw a TypeError - - if (hint == StringHint) { - js2val result; - if (invokeFunctionOnObject(x, engine->toString_StringAtom, result)) { - if (JS2VAL_IS_PRIMITIVE(result)) - return result; - } - if (invokeFunctionOnObject(x, engine->valueOf_StringAtom, result)) { - if (JS2VAL_IS_PRIMITIVE(result)) - return result; - } - reportError(Exception::typeError, "DefaultValue failure", engine->errorPos()); - } - else { - js2val result; - if (invokeFunctionOnObject(x, engine->valueOf_StringAtom, result)) { - if (JS2VAL_IS_PRIMITIVE(result)) - return result; - } - if (invokeFunctionOnObject(x, engine->toString_StringAtom, result)) { - if (JS2VAL_IS_PRIMITIVE(result)) - return result; - } - reportError(Exception::typeError, "DefaultValue failure", engine->errorPos()); - } - return JS2VAL_VOID; - } - - // x is not a number - float64 JS2Metadata::convertValueToDouble(js2val x) - { - if (JS2VAL_IS_UNDEFINED(x)) - return nan; - if (JS2VAL_IS_NULL(x)) - return 0; - if (JS2VAL_IS_BOOLEAN(x)) - return (JS2VAL_TO_BOOLEAN(x)) ? 1.0 : 0.0; - if (JS2VAL_IS_STRING(x)) { - String *str = JS2VAL_TO_STRING(x); - const char16 *numEnd; - return stringToDouble(str->data(), str->data() + str->length(), numEnd); - } - if (JS2VAL_IS_INACCESSIBLE(x)) - reportError(Exception::compileExpressionError, "Inappropriate compile time expression", engine->errorPos()); - if (JS2VAL_IS_UNINITIALIZED(x)) - reportError(Exception::compileExpressionError, "Inappropriate compile time expression", engine->errorPos()); - return toFloat64(toPrimitive(x, NumberHint)); - } - - // x is not a number, convert it to one - js2val JS2Metadata::convertValueToGeneralNumber(js2val x) - { - // XXX Assuming convert to float64, rather than long/ulong - return engine->allocNumber(toFloat64(x)); - } - - // x is not an Object, it needs to be wrapped in one - js2val JS2Metadata::convertValueToObject(js2val x) - { - if (JS2VAL_IS_UNDEFINED(x) || JS2VAL_IS_NULL(x) || JS2VAL_IS_SPECIALREF(x)) - reportError(Exception::typeError, "Can't convert to Object", engine->errorPos()); - if (JS2VAL_IS_STRING(x)) - return String_Constructor(this, JS2VAL_NULL, &x, 1); - if (JS2VAL_IS_BOOLEAN(x)) - return Boolean_Constructor(this, JS2VAL_NULL, &x, 1); - if (JS2VAL_IS_NUMBER(x)) - return Number_Constructor(this, JS2VAL_NULL, &x, 1); - NOT_REACHED("unsupported value type"); - return JS2VAL_VOID; - } - - // x is any js2val - float64 JS2Metadata::toFloat64(js2val x) - { - if (JS2VAL_IS_INT(x)) - return JS2VAL_TO_INT(x); - else - if (JS2VAL_IS_DOUBLE(x)) - return *JS2VAL_TO_DOUBLE(x); - else - if (JS2VAL_IS_LONG(x)) { - float64 d; - JSLL_L2D(d, *JS2VAL_TO_LONG(x)); - return d; - } - else - if (JS2VAL_IS_ULONG(x)) { - float64 d; - JSLL_UL2D(d, *JS2VAL_TO_ULONG(x)); - return d; - } - else - if (JS2VAL_IS_FLOAT(x)) - return *JS2VAL_TO_FLOAT(x); - else - return convertValueToDouble(x); - } - - // x is not a bool - bool JS2Metadata::convertValueToBoolean(js2val x) - { - if (JS2VAL_IS_UNDEFINED(x)) - return false; - if (JS2VAL_IS_NULL(x)) - return false; - if (JS2VAL_IS_INT(x)) - return (JS2VAL_TO_INT(x) != 0); - if (JS2VAL_IS_LONG(x) || JS2VAL_IS_ULONG(x)) - return (!JSLL_IS_ZERO(x)); - if (JS2VAL_IS_FLOAT(x)) { - float64 xd = *JS2VAL_TO_FLOAT(x); - return ! (JSDOUBLE_IS_POSZERO(xd) || JSDOUBLE_IS_NEGZERO(xd) || JSDOUBLE_IS_NaN(xd)); - } - if (JS2VAL_IS_DOUBLE(x)) { - float64 xd = *JS2VAL_TO_DOUBLE(x); - return ! (JSDOUBLE_IS_POSZERO(xd) || JSDOUBLE_IS_NEGZERO(xd) || JSDOUBLE_IS_NaN(xd)); - } - if (JS2VAL_IS_STRING(x)) { - String *str = JS2VAL_TO_STRING(x); - return (str->length() != 0); - } - return true; - } - - // x is not an int - int32 JS2Metadata::convertValueToInteger(js2val x) - { - int32 i; - if (JS2VAL_IS_LONG(x)) { - JSLL_L2I(i, *JS2VAL_TO_LONG(x)); - return i; - } - if (JS2VAL_IS_ULONG(x)) { - JSLL_UL2I(i, *JS2VAL_TO_ULONG(x)); - return i; - } - if (JS2VAL_IS_FLOAT(x)) { - float64 f = *JS2VAL_TO_FLOAT(x); - return JS2Engine::float64toInt32(f); - } - if (JS2VAL_IS_DOUBLE(x)) { - float64 d = *JS2VAL_TO_DOUBLE(x); - return JS2Engine::float64toInt32(d); - } - float64 d = convertValueToDouble(x); - return JS2Engine::float64toInt32(d); - } - - // Save off info about the current compilation and begin a - // new one - using the given parser. - CompilationData *JS2Metadata::startCompilationUnit(BytecodeContainer *newBCon, const String &source, const String &sourceLocation) - { - CompilationData *result = new CompilationData(); - result->compilation_bCon = bCon; - result->execution_bCon = engine->bCon; - bConList.push_back(bCon); - - if (newBCon) - bCon = newBCon; - else - bCon = new BytecodeContainer(); - bCon->mSource = source; - bCon->mSourceLocation = sourceLocation; - engine->bCon = bCon; - - return result; - } - - // Restore the compilation data, and then delete the cached copy. - void JS2Metadata::restoreCompilationUnit(CompilationData *oldData) - { - BytecodeContainer *xbCon = bConList.back(); - ASSERT(oldData->compilation_bCon == xbCon); - bConList.pop_back(); - - bCon = oldData->compilation_bCon; - engine->bCon = oldData->execution_bCon; - - delete oldData; - } - - js2val JS2Metadata::invokeFunction(const char *fname) - { - js2val retval; - uint8 *savePC = NULL; - - CompilationData *oldData = startCompilationUnit(NULL, bCon->mSource, bCon->mSourceLocation); - try { - LexicalReference rVal(&world.identifiers[widenCString(fname)], false); - rVal.emitReadForInvokeBytecode(bCon, 0); - bCon->emitOp(eCall, 0, -(0 + 2) + 1); // pop argCount args, the base & function, and push a result - bCon->addShort(0); - bCon->emitOp(eReturn, 0); - savePC = engine->pc; - engine->pc = NULL; - retval = engine->interpret(RunPhase, bCon); - } - catch (Exception &x) { - engine->pc = savePC; - restoreCompilationUnit(oldData); - throw x; - } - engine->pc = savePC; - restoreCompilationUnit(oldData); - return retval; - } - /* * Throw an exception of the specified kind, indicating the position 'pos' and * attaching the given message. If 'arg' is specified, replace {0} in the message @@ -4783,7 +4449,7 @@ deleteClassProperty: /************************************************************************************ * - * Frame + * MethodClosure * ************************************************************************************/ diff --git a/mozilla/js2/src/js2metadata.h b/mozilla/js2/src/js2metadata.h index fb799a82d3b..498cc90e56f 100644 --- a/mozilla/js2/src/js2metadata.h +++ b/mozilla/js2/src/js2metadata.h @@ -1095,6 +1095,7 @@ public: js2val invokeFunction(const char *fname); bool invokeFunctionOnObject(js2val thisValue, const String *fnName, js2val &result); + js2val invokeFunction(JS2Object *fnObj, js2val thisValue, js2val *argv, uint32 argc); bool readProperty(js2val *container, Multiname *multiname, LookupKind *lookupKind, Phase phase, js2val *rval); bool readProperty(Frame *pf, Multiname *multiname, LookupKind *lookupKind, Phase phase, js2val *rval); diff --git a/mozilla/js2/src/js2op_arithmetic.cpp b/mozilla/js2/src/js2op_arithmetic.cpp index 4256055adc7..bcd23418c6a 100644 --- a/mozilla/js2/src/js2op_arithmetic.cpp +++ b/mozilla/js2/src/js2op_arithmetic.cpp @@ -37,7 +37,7 @@ case eMinus: { - a = pop(); + a = pop(); a = meta->toGeneralNumber(a); if (JS2VAL_IS_LONG(a)) { int64 v = *JS2VAL_TO_LONG(a); @@ -61,14 +61,14 @@ case ePlus: { - a = pop(); + a = pop(); push(meta->toGeneralNumber(a)); } break; case eComplement: { - a = pop(); + a = pop(); a = meta->toGeneralNumber(a); if (JS2VAL_IS_LONG(a)) { int64 i = *JS2VAL_TO_LONG(a); @@ -89,8 +89,8 @@ break; case eLeftShift: { - b = pop(); - a = pop(); + b = pop(); + a = pop(); a = meta->toGeneralNumber(a); int32 count = meta->toInteger(b); if (JS2VAL_IS_LONG(a)) { @@ -110,8 +110,8 @@ break; case eRightShift: { - b = pop(); - a = pop(); + b = pop(); + a = pop(); a = meta->toGeneralNumber(a); int32 count = meta->toInteger(b); if (JS2VAL_IS_LONG(a)) { @@ -131,8 +131,8 @@ break; case eLogicalRightShift: { - b = pop(); - a = pop(); + b = pop(); + a = pop(); a = meta->toGeneralNumber(a); int32 count = meta->toInteger(b); if (JS2VAL_IS_LONG(a)) { @@ -152,8 +152,8 @@ break; case eBitwiseAnd: { - b = pop(); - a = pop(); + b = pop(); + a = pop(); a = meta->toGeneralNumber(a); b = meta->toGeneralNumber(b); if (JS2VAL_IS_LONG(a)) { @@ -210,8 +210,8 @@ break; case eBitwiseXor: { - b = pop(); - a = pop(); + b = pop(); + a = pop(); a = meta->toGeneralNumber(a); b = meta->toGeneralNumber(b); if (JS2VAL_IS_LONG(a)) { @@ -268,8 +268,8 @@ break; case eBitwiseOr: { - b = pop(); - a = pop(); + b = pop(); + a = pop(); a = meta->toGeneralNumber(a); b = meta->toGeneralNumber(b); if (JS2VAL_IS_LONG(a)) { @@ -327,18 +327,20 @@ case eAdd: { - b = pop(); - a = pop(); - a = meta->toPrimitive(a, NumberHint); - b = meta->toPrimitive(b, NumberHint); - if (JS2VAL_IS_STRING(a) || JS2VAL_IS_STRING(b)) { - const String *astr = meta->toString(a); - const String *bstr = meta->toString(b); - String c = *astr; - c += *bstr; - push(STRING_TO_JS2VAL(allocStringPtr(&c))); - } - else { + b = pop(); + a = pop(); + a = meta->toPrimitive(a, NumberHint); + b = meta->toPrimitive(b, NumberHint); + if (JS2VAL_IS_STRING(a) || JS2VAL_IS_STRING(b)) { + const String *astr = meta->toString(a); + const String *bstr = meta->toString(b); + + String c = *astr; + + c += *bstr; + push(STRING_TO_JS2VAL(allocStringPtr(&c))); + } + else { a = meta->toGeneralNumber(a); b = meta->toGeneralNumber(b); if (JS2VAL_IS_LONG(a)) { @@ -470,8 +472,8 @@ case eMultiply: { - b = pop(); - a = pop(); + b = pop(); + a = pop(); a = meta->toGeneralNumber(a); b = meta->toGeneralNumber(b); if (JS2VAL_IS_LONG(a)) { @@ -536,8 +538,8 @@ case eDivide: { - b = pop(); - a = pop(); + b = pop(); + a = pop(); a = meta->toGeneralNumber(a); b = meta->toGeneralNumber(b); if (JS2VAL_IS_LONG(a)) { @@ -602,8 +604,8 @@ case eModulo: { - b = pop(); - a = pop(); + b = pop(); + a = pop(); a = meta->toGeneralNumber(a); b = meta->toGeneralNumber(b); if (JS2VAL_IS_LONG(a)) { @@ -675,23 +677,23 @@ case eLogicalXor: { - b = pop(); - a = pop(); + b = pop(); + a = pop(); push(BOOLEAN_TO_JS2VAL(meta->toBoolean(a) ^ meta->toBoolean(b))); } break; case eLogicalNot: { - a = pop(); + a = pop(); push(BOOLEAN_TO_JS2VAL(!meta->toBoolean(a))); } break; case eLess: { - b = pop(); - a = pop(); + b = pop(); + a = pop(); a = meta->toPrimitive(a, NumberHint); b = meta->toPrimitive(b, NumberHint); bool rval; @@ -705,8 +707,8 @@ case eLessEqual: { - b = pop(); - a = pop(); + b = pop(); + a = pop(); a = meta->toPrimitive(a, NumberHint); b = meta->toPrimitive(b, NumberHint); bool rval; @@ -720,8 +722,8 @@ case eGreater: { - b = pop(); - a = pop(); + b = pop(); + a = pop(); a = meta->toPrimitive(a, NumberHint); b = meta->toPrimitive(b, NumberHint); bool rval; @@ -735,8 +737,8 @@ case eGreaterEqual: { - b = pop(); - a = pop(); + b = pop(); + a = pop(); a = meta->toPrimitive(a, NumberHint); b = meta->toPrimitive(b, NumberHint); bool rval; @@ -752,8 +754,8 @@ case eEqual: { bool rval; - b = pop(); - a = pop(); + b = pop(); + a = pop(); if (JS2VAL_IS_NULL(a) || JS2VAL_IS_UNDEFINED(a)) rval = (JS2VAL_IS_NULL(b) || JS2VAL_IS_UNDEFINED(b)); else @@ -764,8 +766,11 @@ b = meta->toPrimitive(b, NumberHint); if (JS2VAL_IS_NULL(b) || JS2VAL_IS_UNDEFINED(b)) rval = false; - else - rval = (meta->toFloat64(a) == meta->toFloat64(b)); + else { + float64 x = meta->toFloat64(a); + float64 y = meta->toFloat64(b); + rval = (!JSDOUBLE_IS_NaN(x) && !JSDOUBLE_IS_NaN(y) && (x == y)); + } } } else @@ -773,8 +778,11 @@ b = meta->toPrimitive(b, NumberHint); if (JS2VAL_IS_NULL(b) || JS2VAL_IS_UNDEFINED(b)) rval = false; - else - rval = (meta->toFloat64(a) == meta->toFloat64(b)); + else { + float64 x = meta->toFloat64(a); + float64 y = meta->toFloat64(b); + rval = (!JSDOUBLE_IS_NaN(x) && !JSDOUBLE_IS_NaN(y) && (x == y)); + } } else if (JS2VAL_IS_STRING(a)) { @@ -782,8 +790,11 @@ if (JS2VAL_IS_NULL(b) || JS2VAL_IS_UNDEFINED(b)) rval = false; else - if (JS2VAL_IS_BOOLEAN(b) || JS2VAL_IS_NUMBER(b)) - rval = (meta->toFloat64(a) == meta->toFloat64(b)); + if (JS2VAL_IS_BOOLEAN(b) || JS2VAL_IS_NUMBER(b)) { + float64 x = meta->toFloat64(a); + float64 y = meta->toFloat64(b); + rval = (!JSDOUBLE_IS_NaN(x) && !JSDOUBLE_IS_NaN(y) && (x == y)); + } else rval = (*JS2VAL_TO_STRING(a) == *JS2VAL_TO_STRING(b)); } @@ -798,16 +809,22 @@ else if (JS2VAL_IS_BOOLEAN(a)) rval = (JS2VAL_TO_BOOLEAN(a) == JS2VAL_TO_BOOLEAN(b)); - else - rval = (meta->toFloat64(a) == meta->toFloat64(b)); + else { + float64 x = meta->toFloat64(a); + float64 y = meta->toFloat64(b); + rval = (!JSDOUBLE_IS_NaN(x) && !JSDOUBLE_IS_NaN(y) && (x == y)); + } } else if (JS2VAL_IS_NUMBER(b)) { a = meta->toPrimitive(a, NumberHint); if (JS2VAL_IS_NULL(a) || JS2VAL_IS_UNDEFINED(a)) rval = false; - else - rval = (meta->toFloat64(a) == meta->toFloat64(b)); + else { + float64 x = meta->toFloat64(a); + float64 y = meta->toFloat64(b); + rval = (!JSDOUBLE_IS_NaN(x) && !JSDOUBLE_IS_NaN(y) && (x == y)); + } } else if (JS2VAL_IS_STRING(b)) { @@ -815,8 +832,11 @@ if (JS2VAL_IS_NULL(a) || JS2VAL_IS_UNDEFINED(a)) rval = false; else - if (JS2VAL_IS_BOOLEAN(a) || JS2VAL_IS_NUMBER(a)) - rval = (meta->toFloat64(a) == meta->toFloat64(b)); + if (JS2VAL_IS_BOOLEAN(a) || JS2VAL_IS_NUMBER(a)) { + float64 x = meta->toFloat64(a); + float64 y = meta->toFloat64(b); + rval = (!JSDOUBLE_IS_NaN(x) && !JSDOUBLE_IS_NaN(y) && (x == y)); + } else rval = (*JS2VAL_TO_STRING(a) == *JS2VAL_TO_STRING(b)); } @@ -834,9 +854,8 @@ case eNotIdentical: { bool rval; - b = pop(); - a = pop(); - + b = pop(); + a = pop(); if (meta->objectType(a) != meta->objectType(b)) rval = false; else { diff --git a/mozilla/js2/src/js2op_invocation.cpp b/mozilla/js2/src/js2op_invocation.cpp index 31001e52587..99260367058 100644 --- a/mozilla/js2/src/js2op_invocation.cpp +++ b/mozilla/js2/src/js2op_invocation.cpp @@ -113,6 +113,7 @@ pc += sizeof(uint16); a = top(argCount + 2); // 'this' b = top(argCount + 1); // target function + uint32 length = 0; if (JS2VAL_IS_PRIMITIVE(b)) meta->reportError(Exception::badValueError, "Can't call on primitive value", errorPos()); JS2Object *fObj = JS2VAL_TO_OBJECT(b); @@ -125,6 +126,7 @@ if ((fObj->kind == PrototypeInstanceKind) && ((checked_cast(fObj))->type == meta->functionClass)) { fWrap = (checked_cast(fObj))->fWrap; + length = getLength(meta, fObj); } if (fWrap) { if (fWrap->compileFrame->prototype) { @@ -135,6 +137,10 @@ } } if (fWrap->code) { // native code + while (length > argCount) { + push(JS2VAL_UNDEFINED); + argCount++; + } a = fWrap->code(meta, a, base(argCount), argCount); pop(argCount + 2); push(a); @@ -167,6 +173,7 @@ pFrame->instantiate(meta->env); pFrame->thisObject = mc->thisObject; // assignArguments(runtimeFrame, fWrap->compileFrame->signature); + pFrame->assignArguments(meta, base(argCount), argCount); jsr(phase, fWrap->bCon, base(argCount + 2) - execStack, JS2VAL_VOID); // seems out of order, but we need to catch the current top frame meta->env->addFrame(meta->objectType(mc->thisObject)); meta->env->addFrame(pFrame);