Added Array.sort. Fixed toNumber(String) for trailing whitespace. Fixed

Date.SetXXX for date values. Fixed scopechain growth bug. Fixed empty
array literals. Fixed empty type casts for built-ins.
This commit is contained in:
rogerl%netscape.com 2001-10-19 00:25:28 +00:00
Родитель fcfa1d2581
Коммит 9f287653da
10 изменённых файлов: 319 добавлений и 53 удалений

Просмотреть файл

@ -533,17 +533,6 @@ void ByteCodeGen::genCodeForFunction(FunctionDefinition &f, size_t pos, JSFuncti
{
mScopeChain->addScope(fnc->mParameterBarrel);
mScopeChain->addScope(&fnc->mActivation);
// OPT - no need to push the parameter and function
// scopes if the function doesn't contain any 'eval'
// calls, all other references to the variables mapped
// inside these scopes will have been turned into
// localVar references.
/*
addByte(PushScopeOp);
addPointer(fnc->mParameterBarrel);
addByte(PushScopeOp);
addPointer(&fnc->mActivation);
*/
#ifdef DEBUG
if (f.name) {
@ -632,13 +621,8 @@ void ByteCodeGen::genCodeForFunction(FunctionDefinition &f, size_t pos, JSFuncti
if (f.body)
hasReturn = genCodeForStatement(f.body, NULL, NotALabel);
/*
// OPT - see above
addByte(PopScopeOp);
addByte(PopScopeOp);
*/
if (isConstructor) {
ASSERT(!hasReturn); // is this useful? Won't the semantics have done it?
ASSERT(!hasReturn); // XXX is this useful? Won't the semantics have done it?
addOp(LoadThisOp);
ASSERT(mStackTop == 1);
addOpSetDepth(ReturnOp, 0);

Просмотреть файл

@ -202,8 +202,9 @@ JSValue Context::interpret(JS2Runtime::ByteCodeModule *bcm, int offset, ScopeCha
mScopeChain = new ScopeChain(this, mWorld);
mScopeChain->addScope(getGlobalObject());
}
if (mThis.isObject())
mScopeChain->addScope(mThis.object);
// if (mThis.isObject())
// mScopeChain->addScope(mThis.object);
// mScopeChain->addScope(mActivationStack.top());
mCurModule = bcm;
@ -225,8 +226,8 @@ JSValue Context::interpret(JS2Runtime::ByteCodeModule *bcm, int offset, ScopeCha
// the following (delete's) are a bit iffy - depends on whether
// a closure capturing the contents has come along...
if (mThis.isObject())
mScopeChain->popScope();
// if (mThis.isObject())
// mScopeChain->popScope();
delete[] mStack;
delete[] mLocals;
if (scopeChain == NULL)
@ -249,8 +250,8 @@ JSValue Context::interpret(JS2Runtime::ByteCodeModule *bcm, int offset, ScopeCha
// the following (delete's) are a bit iffy - depends on whether
// a closure capturing the contents has come along...
if (mThis.isObject())
mScopeChain->popScope();
// if (mThis.isObject())
// mScopeChain->popScope();
delete[] mStack;
delete[] mLocals;
if (scopeChain == NULL)
@ -401,6 +402,7 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
try {
if (mDebugFlag) {
FunctionName *fnName;
uint32 x = mScopeChain->mScopeStack.size();
if (mCurModule->mFunction && (fnName = mCurModule->mFunction->getFunctionName())) {
StringFormatter s;
PrettyPrinter pp(s);
@ -409,10 +411,10 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
std::string str(fnStr.length(), char());
std::transform(fnStr.begin(), fnStr.end(), str.begin(), narrow);
uint32 len = strlen(str.c_str());
printFormat(stdOut, "%.30s+%.4d%*c%d ", str.c_str(), (pc - mCurModule->mCodeBase), (len > 30) ? 0 : (len - 30), ' ', stackSize());
printFormat(stdOut, "%.30s+%.4d%*c%d %d ", str.c_str(), (pc - mCurModule->mCodeBase), (len > 30) ? 0 : (len - 30), ' ', stackSize(), x);
}
else
printFormat(stdOut, "+%.4d%*c%d ", (pc - mCurModule->mCodeBase), 30, ' ', stackSize());
printFormat(stdOut, "+%.4d%*c%d %d ", (pc - mCurModule->mCodeBase), 30, ' ', stackSize(), x);
printInstruction(stdOut, toUInt32(pc - mCurModule->mCodeBase), *mCurModule);
}
switch ((ByteCodeOp)(*pc++)) {
@ -639,8 +641,8 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
mArgumentBase, oldThis,
pc, mCurModule));
mScopeChain = target->getScopeChain();
if (mThis.isObject())
mScopeChain->addScope(mThis.object);
// if (mThis.isObject())
// mScopeChain->addScope(mThis.object);
if (!target->isChecked()) {
JSArrayInstance *args = (JSArrayInstance *)Array_Type->newInstance(this);
@ -694,6 +696,9 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
delete[] mLocals;
delete[] mStack;
mScopeChain->popScope();
mScopeChain->popScope();
mCurModule = prev->mModule;
pc = prev->mPC;
endPC = mCurModule->mCodeBase + mCurModule->mLength;
@ -721,6 +726,9 @@ JSValue Context::interpret(uint8 *pc, uint8 *endPC)
delete[] mLocals;
delete[] mStack;
mScopeChain->popScope();
mScopeChain->popScope();
mCurModule = prev->mModule;
pc = prev->mPC;
endPC = mCurModule->mCodeBase + mCurModule->mLength;
@ -2225,8 +2233,19 @@ float64 stringToNumber(const String *string)
if (sBegin)
if ((sBegin[0] == '0') && ((sBegin[1] & ~0x20) == 'X'))
return stringToInteger(sBegin, string->end(), numEnd, 16);
else {
float64 result = stringToDouble(sBegin, string->end(), numEnd);
if (numEnd != string->end()) {
const char16 *sEnd = string->end();
while (numEnd != sEnd) {
if (!isSpace(*numEnd++))
return nan;
}
return result;
}
else
return stringToDouble(sBegin, string->end(), numEnd);
return result;
}
else
return 0.0;
}

Просмотреть файл

@ -175,10 +175,12 @@ bool JSObject::hasProperty(const String &name, NamespaceList *names, Access acc,
bool JSObject::deleteProperty(const String &name, NamespaceList *names)
{
PropertyIterator i = findNamespacedProperty(name, names);
if (i != mProperties.end()) {
if ((PROPERTY_ATTR(i) & Property::DontDelete) == 0) {
mProperties.erase(i);
return true;
}
}
return false;
}
@ -1962,6 +1964,14 @@ static JSValue Number_Constructor(Context *cx, const JSValue& thisValue, JSValue
return v;
}
static JSValue Number_TypeCast(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 argc)
{
if (argc == 0)
return kPositiveZero;
else
return argv[0].toNumber(cx);
}
static JSValue Number_toString(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/)
{
if (!thisValue.isObject() || (thisValue.getType() != Number_Type))
@ -2387,14 +2397,17 @@ void Context::initBuiltins()
Function_Type->mTypeCast = new JSFunction(this, Function_Constructor, Object_Type);
Number_Type->mTypeCast = new JSFunction(this, Number_TypeCast, Number_Type);
Array_Type->defineUnaryOperator(Index, new JSFunction(this, Array_GetElement, Object_Type));
Array_Type->defineUnaryOperator(IndexEqual, new JSFunction(this, Array_SetElement, Object_Type));
Array_Type->mTypeCast = new JSFunction(this, Array_Constructor, Array_Type);
Date_Type->mTypeCast = new JSFunction(this, Date_TypeCast, String_Type);
Date_Type->defineStaticMethod(this, widenCString("parse"), NULL, new JSFunction(this, Date_parse, Number_Type));
Date_Type->defineStaticMethod(this, widenCString("UTC"), NULL, new JSFunction(this, Date_UTC, Number_Type));
String_Type->mTypeCast = new JSFunction(this, String_Constructor, String_Type);
String_Type->mTypeCast = new JSFunction(this, String_TypeCast, String_Type);
}

Просмотреть файл

@ -1515,7 +1515,7 @@ XXX ...couldn't get this to work...
void resizeStack(uint32 n)
{
ASSERT(n < mStackMax);
ASSERT(n <= mStackMax);
mStackTop = n;
}
@ -1619,6 +1619,13 @@ XXX ...couldn't get this to work...
};
/*
(a local instance of) This class is used when a function in the
interpreter execution codepath may need to re-invoke the interpreter
(by calling an internal method that MAY have an override). The stack
replacement simply inserts a stack big enough for whatever action is
about to occur.
*/
class ContextStackReplacement {
public:
enum { ReplacementStackSize = 4 };

Просмотреть файл

@ -62,7 +62,17 @@ JSValue Array_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv,
JSArrayInstance *arrInst = checked_cast<JSArrayInstance *>(thisObj);
if (argc > 0) {
if (argc == 1) {
arrInst->mLength = (uint32)(argv[0].toNumber(cx).f64);
if (argv[0].isNumber()) {
uint32 i = (uint32)(argv[0].f64);
if (i == argv[0].f64)
arrInst->mLength = i;
else
cx->reportError(Exception::rangeError, "Array length too large");
}
else {
arrInst->mLength = 1;
arrInst->defineVariable(cx, widenCString("0"), (NamespaceList *)(NULL), Property::Enumerable, Object_Type, argv[0]);
}
}
else {
arrInst->mLength = argc;
@ -93,6 +103,7 @@ static JSValue Array_toString(Context *cx, const JSValue& thisValue, JSValue * /
const String *id = numberToString(i);
arrInst->getProperty(cx, *id, NULL);
JSValue result = cx->popValue();
if (!result.isUndefined() && !result.isNull())
s->append(*result.toString(cx).string);
if (i < (arrInst->mLength - 1))
s->append(widenCString(","));
@ -385,9 +396,154 @@ static JSValue Array_slice(Context *cx, const JSValue& thisValue, JSValue *argv,
return JSValue(A);
}
static JSValue Array_sort(Context * /*cx*/, const JSValue& /*thisValue*/, JSValue * /*argv*/, uint32 /*argc*/)
typedef struct CompareArgs {
Context *context;
JSFunction *target;
} CompareArgs;
typedef struct QSortArgs {
JSValue *vec;
JSValue *pivot;
CompareArgs *arg;
} QSortArgs;
static int sort_compare(JSValue *a, JSValue *b, CompareArgs *arg);
static void
js_qsort_r(QSortArgs *qa, int lo, int hi)
{
return kUndefinedValue;
JSValue *pivot, *vec, *a, *b;
int i, j, lohi, hilo;
CompareArgs *arg;
pivot = qa->pivot;
vec = qa->vec;
arg = qa->arg;
while (lo < hi) {
i = lo;
j = hi;
a = vec + i;
*pivot = *a;
while (i < j) {
b = vec + j;
if (sort_compare(b, pivot, arg) >= 0) {
j--;
continue;
}
*a = *b;
while (sort_compare(a, pivot, arg) <= 0) {
i++;
a = vec + i;
if (i == j)
goto store_pivot;
}
*b = *a;
}
if (i > lo) {
store_pivot:
*a = *pivot;
}
if (i - lo < hi - i) {
lohi = i - 1;
if (lo < lohi)
js_qsort_r(qa, lo, lohi);
lo = i + 1;
} else {
hilo = i + 1;
if (hilo < hi)
js_qsort_r(qa, hilo, hi);
hi = i - 1;
}
}
}
static void js_qsort(JSValue *vec, size_t nel, CompareArgs *arg)
{
JSValue *pivot;
QSortArgs qa;
pivot = new JSValue();
qa.vec = vec;
qa.pivot = pivot;
qa.arg = arg;
js_qsort_r(&qa, 0, (int)(nel - 1));
delete(pivot);
}
static int sort_compare(JSValue *a, JSValue *b, CompareArgs *arg)
{
JSValue av = *(const JSValue *)a;
JSValue bv = *(const JSValue *)b;
CompareArgs *ca = (CompareArgs *) arg;
Context *cx = ca->context;
if (ca->target == NULL) {
int32 result;
if (av.isUndefined() || bv.isUndefined()) {
/* Put undefined properties at the end. */
result = (av.isUndefined()) ? 1 : -1;
}
else {
const String *astr = av.toString(cx).string;
const String *bstr = bv.toString(cx).string;
result = astr->compare(*bstr);
}
return result;
}
else {
JSValue argv[2];
argv[0] = av;
argv[1] = bv;
JSValue result = cx->invokeFunction(ca->target, kNullValue, argv, 2);
return (int32)(result.toInt32(cx).f64);
}
}
static JSValue Array_sort(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
ASSERT(thisValue.isObject());
ContextStackReplacement csr(cx);
CompareArgs ca;
ca.context = cx;
if (argc > 0) {
if (!argv[0].isFunction())
cx->reportError(Exception::typeError, "sort needs a compare function");
ca.target = argv[0].function;
}
else
ca.target = NULL;
JSObject *thisObj = thisValue.object;
thisObj->getProperty(cx, cx->Length_StringAtom, CURRENT_ATTR);
JSValue result = cx->popValue();
uint32 length = (uint32)(result.toUInt32(cx).f64);
if (length > 0) {
uint32 i;
JSValue *vec = new JSValue[length];
for (i = 0; i < length; i++) {
const String *id = numberToString(i);
thisObj->getProperty(cx, *id, CURRENT_ATTR);
vec[i] = cx->popValue();
delete id;
}
js_qsort(vec, length, &ca);
for (i = 0; i < length; i++) {
const String *id = numberToString(i);
thisObj->setProperty(cx, *id, CURRENT_ATTR, vec[i]);
delete id;
}
delete[] vec;
}
return thisValue;
}
static JSValue Array_splice(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)

Просмотреть файл

@ -426,6 +426,74 @@ static JSValue Date_makeTime(Context *cx, const JSValue& thisValue, JSValue *arg
return JSValue(*date);
}
static JSValue Date_makeDate(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc, uint32 maxargs, bool local)
{
uint32 i;
float64 lorutime; /* local or UTC version of *date */
float64 args[3], *argp, *stop;
float64 year, month, day;
float64 result;
float64 *date = Date_getProlog(cx, thisValue);
result = *date;
/* see complaint about ECMA in date_MakeTime */
if (argc == 0)
argc = 1; /* should be safe, because length of all settors is 1 */
else if (argc > maxargs)
argc = maxargs; /* clamp argc */
for (i = 0; i < argc; i++) {
argv[i] = argv[i].toNumber(cx);
if (JSDOUBLE_IS_NaN(argv[i])) {
*date = nan;
return kNaNValue;
}
args[i] = argv[i].toInteger(cx).f64;
}
/* return NaN if date is NaN and we're not setting the year,
* If we are, use 0 as the time. */
if (!(JSDOUBLE_IS_FINITE(result))) {
if (argc < 3)
return kNaNValue;
else
lorutime = +0.;
} else {
if (local)
lorutime = LocalTime(result);
else
lorutime = result;
}
argp = args;
stop = argp + argc;
if (maxargs >= 3 && argp < stop)
year = *argp++;
else
year = YearFromTime(lorutime);
if (maxargs >= 2 && argp < stop)
month = *argp++;
else
month = MonthFromTime(lorutime);
if (maxargs >= 1 && argp < stop)
day = *argp++;
else
day = DateFromTime(lorutime);
day = MakeDay(year, month, day); /* day within year */
result = MakeDate(day, TimeWithinDay(lorutime));
if (local)
result = UTC(result);
*date = TIMECLIP(result);
return JSValue(*date);
}
/* find UTC time from given date... no 1900 correction! */
static float64 date_msecFromDate(float64 year, float64 mon, float64 mday, float64 hour, float64 min, float64 sec, float64 msec)
{
@ -1258,32 +1326,32 @@ static JSValue Date_setYear(Context *cx, const JSValue& thisValue, JSValue *argv
static JSValue Date_setFullYear(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
return Date_makeTime(cx, thisValue, argv, argc, 3, true);
return Date_makeDate(cx, thisValue, argv, argc, 3, true);
}
static JSValue Date_setUTCFullYear(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
return Date_makeTime(cx, thisValue, argv, argc, 3, false);
return Date_makeDate(cx, thisValue, argv, argc, 3, false);
}
static JSValue Date_setMonth(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
return Date_makeTime(cx, thisValue, argv, argc, 2, true);
return Date_makeDate(cx, thisValue, argv, argc, 2, true);
}
static JSValue Date_setUTCMonth(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
return Date_makeTime(cx, thisValue, argv, argc, 2, false);
return Date_makeDate(cx, thisValue, argv, argc, 2, false);
}
static JSValue Date_setDate(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
return Date_makeTime(cx, thisValue, argv, argc, 1, true);
return Date_makeDate(cx, thisValue, argv, argc, 1, true);
}
static JSValue Date_setUTCDate(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
return Date_makeTime(cx, thisValue, argv, argc, 1, false);
return Date_makeDate(cx, thisValue, argv, argc, 1, false);
}
static JSValue Date_setHours(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)

Просмотреть файл

@ -67,9 +67,18 @@ JSValue String_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv,
return thatValue;
}
JSValue String_TypeCast(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 argc)
{
if (argc == 0)
return JSValue(&cx->Empty_StringAtom);
else
return argv[0].toString(cx);
}
JSValue String_fromCharCode(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 argc)
{
String *resultStr = new String(); // can't use cx->mEmptyString because we're modifying this below
String *resultStr = new String(); // can't use cx->Empty_StringAtom; because we're modifying this below
resultStr->reserve(argc);
for (uint32 i = 0; i < argc; i++)
*resultStr += (char16)(argv[i].toUInt16(cx).f64);

Просмотреть файл

@ -36,6 +36,7 @@ namespace JS2Runtime {
extern JSValue String_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc);
extern JSValue String_TypeCast(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc);
extern JSValue String_fromCharCode(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc);
Context::PrototypeFunctions *getStringProtos();

Просмотреть файл

@ -1683,8 +1683,6 @@ double JS::stringToDouble(const char16 *str, const char16 *strEnd, const char16
double value = strToDouble(cstr.get(), estr);
ptrdiff_t i = estr - cstr.get();
numEnd = i ? str1 + i : str;
if ((value == 0.0) && (i == 0))
return nan;
return value;
}

Просмотреть файл

@ -221,6 +221,10 @@ static bool processArgs(Context *cx, int argc, char **argv, int *result)
for (int i = 0; i < argc; i++) {
if (argv[i][0] == '-') {
switch (argv[i][1]) {
default:
stdOut << "unrecognized command line switch\n";
i = argc;
break;
case 'f':
{
try {
@ -235,7 +239,12 @@ static bool processArgs(Context *cx, int argc, char **argv, int *result)
break;
}
}
else {
if ((argv[i][0] == '/') && (argv[i][1] == '/')) {
// skip rest of command line
break;
}
}
}
return doInteractive;
}
@ -246,13 +255,15 @@ static bool processArgs(Context *cx, int argc, char **argv, int *result)
int main(int argc, char **argv)
{
#if defined(XP_MAC) && !defined(XP_MAC_MPW)
initConsole("\pJavaScript Shell", "Welcome to the js2 shell.\n", argc, argv);
#endif
using namespace JavaScript;
using namespace Shell;
#if defined(XP_MAC) && !defined(XP_MAC_MPW)
initConsole("\pJavaScript Shell", "Welcome to DikDik.\n", argc, argv);
#else
stdOut << "Welcome to DikDik.\n";
#endif
try {
JSObject *globalObject;
Context cx(&globalObject, world, a, Pragma::js2);