From ccebbc06413f5e33aebd54223149526b4bacd719 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Fri, 18 May 2012 14:10:02 +0200 Subject: [PATCH] Implemented the ecma Date object. --- qmljs_objects.cpp | 2 +- qmljs_objects.h | 24 +++++- qmljs_runtime.cpp | 79 +++++++++++++++++++ qmljs_runtime.h | 26 +++++++ qv4ecmaobjects.cpp | 221 ++++++++++++++++++++++++++++++++++++++++++++++++----- qv4ecmaobjects_p.h | 3 +- 6 files changed, 331 insertions(+), 24 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 0fdf99b..cf5560e 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -323,7 +323,7 @@ Object *ExecutionEngine::newBooleanPrototype(Context *ctx, FunctionObject *proto Object *ExecutionEngine::newDateObject(const Value &value) { - return new Object(); // ### FIXME + return new DateObject(value); } FunctionObject *ExecutionEngine::newDateCtor(Context *ctx) diff --git a/qmljs_objects.h b/qmljs_objects.h index b2b2ea3..cbbea40 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -21,8 +21,10 @@ struct BooleanObject; struct NumberObject; struct StringObject; struct ArrayObject; +struct DateObject; struct FunctionObject; struct ErrorObject; +struct ArgumentsObject; struct Context; struct ExecutionEngine; @@ -194,7 +196,14 @@ struct Object { virtual ~Object(); + virtual BooleanObject *asBooleanObject() { return 0; } + virtual NumberObject *asNumberObject() { return 0; } + virtual StringObject *asStringObject() { return 0; } + virtual DateObject *asDateObject() { return 0; } + virtual ArrayObject *asArrayObject() { return 0; } virtual FunctionObject *asFunctionObject() { return 0; } + virtual ErrorObject *asErrorObject() { return 0; } + virtual ArgumentsObject *asArgumentsObject() { return 0; } bool get(String *name, Value *result); @@ -217,22 +226,33 @@ struct Object { struct BooleanObject: Object { Value value; BooleanObject(const Value &value): value(value) {} + virtual BooleanObject *asBooleanObject() { return this; } virtual void defaultValue(Context *, Value *result, int /*typehint*/) { *result = value; } }; struct NumberObject: Object { Value value; NumberObject(const Value &value): value(value) {} + virtual NumberObject *asNumberObject() { return this; } virtual void defaultValue(Context *, Value *result, int /*typehint*/) { *result = value; } }; struct StringObject: Object { Value value; StringObject(const Value &value): value(value) {} + virtual StringObject *asStringObject() { return this; } + virtual void defaultValue(Context *, Value *result, int /*typehint*/) { *result = value; } +}; + +struct DateObject: Object { + Value value; + DateObject(const Value &value): value(value) {} + virtual DateObject *asDateObject() { return this; } virtual void defaultValue(Context *, Value *result, int /*typehint*/) { *result = value; } }; struct ArrayObject: Object { + virtual ArrayObject *asArrayObject() { return this; } }; struct FunctionObject: Object { @@ -250,8 +270,8 @@ struct FunctionObject: Object { , varList(0) , varCount(0) , needsActivation(true) {} - virtual FunctionObject *asFunctionObject() { return this; } + virtual FunctionObject *asFunctionObject() { return this; } virtual bool hasInstance(const Value &value) const; virtual void call(Context *ctx); virtual void construct(Context *ctx); @@ -278,11 +298,13 @@ struct ScriptFunction: FunctionObject { struct ErrorObject: Object { Value message; ErrorObject(const Value &message): message(message) {} + virtual ErrorObject *asErrorObject() { return this; } }; struct ArgumentsObject: Object { Context *context; ArgumentsObject(Context *context): context(context) {} + virtual ArgumentsObject *asArgumentsObject() { return this; } virtual Value *getProperty(String *name, PropertyAttributes *attributes); }; diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 337f2f7..6cb2a93 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -69,6 +69,85 @@ String *Value::toString(Context *ctx) const return v.stringValue; } +bool Value::isFunctionObject() const +{ + return type == OBJECT_TYPE ? objectValue->asFunctionObject() != 0 : false; +} + +bool Value::isBooleanObject() const +{ + return type == OBJECT_TYPE ? objectValue->asBooleanObject() != 0 : false; +} + +bool Value::isNumberObject() const +{ + return type == OBJECT_TYPE ? objectValue->asNumberObject() != 0 : false; +} + +bool Value::isStringObject() const +{ + return type == OBJECT_TYPE ? objectValue->asStringObject() != 0 : false; +} + +bool Value::isDateObject() const +{ + return type == OBJECT_TYPE ? objectValue->asDateObject() != 0 : false; +} + +bool Value::isArrayObject() const +{ + return type == OBJECT_TYPE ? objectValue->asArrayObject() != 0 : false; +} + +bool Value::isErrorObject() const +{ + return type == OBJECT_TYPE ? objectValue->asErrorObject() != 0 : false; +} + +bool Value::isArgumentsObject() const +{ + return type == OBJECT_TYPE ? objectValue->asArgumentsObject() != 0 : false; +} + +FunctionObject *Value::asFunctionObject() const +{ + return type == OBJECT_TYPE ? objectValue->asFunctionObject() : 0; +} + +BooleanObject *Value::asBooleanObject() const +{ + return type == OBJECT_TYPE ? objectValue->asBooleanObject() : 0; +} + +NumberObject *Value::asNumberObject() const +{ + return type == OBJECT_TYPE ? objectValue->asNumberObject() : 0; +} + +StringObject *Value::asStringObject() const +{ + return type == OBJECT_TYPE ? objectValue->asStringObject() : 0; +} + +DateObject *Value::asDateObject() const +{ + return type == OBJECT_TYPE ? objectValue->asDateObject() : 0; +} + +ArrayObject *Value::asArrayObject() const +{ + return type == OBJECT_TYPE ? objectValue->asArrayObject() : 0; +} + +ErrorObject *Value::asErrorObject() const +{ + return type == OBJECT_TYPE ? objectValue->asErrorObject() : 0; +} + +ArgumentsObject *Value::asArgumentsObject() const +{ + return type == OBJECT_TYPE ? objectValue->asArgumentsObject() : 0; +} extern "C" { diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 223a219..a02ceec 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -40,6 +40,14 @@ struct Value; struct Object; struct String; struct Context; +struct FunctionObject; +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct DateObject; +struct ArrayObject; +struct ErrorObject; +struct ArgumentsObject; extern "C" { @@ -228,6 +236,24 @@ struct Value { inline bool isBoolean() const { return is(BOOLEAN_TYPE); } inline bool isNumber() const { return is(NUMBER_TYPE); } inline bool isObject() const { return is(OBJECT_TYPE); } + + bool isFunctionObject() const; + bool isBooleanObject() const; + bool isNumberObject() const; + bool isStringObject() const; + bool isDateObject() const; + bool isArrayObject() const; + bool isErrorObject() const; + bool isArgumentsObject() const; + + FunctionObject *asFunctionObject() const; + BooleanObject *asBooleanObject() const; + NumberObject *asNumberObject() const; + StringObject *asStringObject() const; + DateObject *asDateObject() const; + ArrayObject *asArrayObject() const; + ErrorObject *asErrorObject() const; + ArgumentsObject *asArgumentsObject() const; }; extern "C" { diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index 4630502..0131cbf 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -936,7 +936,7 @@ void NumberPrototype::method_toLocaleString(Context *ctx) assert(self.isObject()); // if (self.classInfo() != classInfo) { // return throwThisObjectTypeError( - // context, QLatin1String("Number.prototype.toLocaleString")); + // ctx, QLatin1String("Number.prototype.toLocaleString")); // } Value internalValue; self.objectValue->defaultValue(ctx, &internalValue, STRING_HINT); @@ -950,7 +950,7 @@ void NumberPrototype::method_valueOf(Context *ctx) assert(self.isObject()); // if (self.classInfo() != classInfo) { // return throwThisObjectTypeError( - // context, QLatin1String("Number.prototype.toLocaleString")); + // ctx, QLatin1String("Number.prototype.toLocaleString")); // } Value internalValue; self.objectValue->defaultValue(ctx, &internalValue, NUMBER_HINT); @@ -1068,7 +1068,7 @@ void BooleanPrototype::method_toString(Context *ctx) Value self = ctx->thisObject; // if (self.classInfo() != classInfo) { // return throwThisObjectTypeError( -// context, QLatin1String("Boolean.prototype.toString")); +// ctx, QLatin1String("Boolean.prototype.toString")); // } assert(self.isObject()); Value internalValue; @@ -1083,7 +1083,7 @@ void BooleanPrototype::method_valueOf(Context *ctx) Value self = ctx->thisObject; // if (self.classInfo() != classInfo) { // return throwThisObjectTypeError( -// context, QLatin1String("Boolean.prototype.valueOf")); +// ctx, QLatin1String("Boolean.prototype.valueOf")); // } assert(self.isObject()); Value internalValue; @@ -1099,7 +1099,7 @@ Value DateCtor::create(ExecutionEngine *engine) { Context *ctx = engine->rootContext; FunctionObject *ctor = ctx->engine->newDateCtor(ctx); - ctor->setProperty(ctx, QLatin1String("prototype"), Value::fromObject(ctx->engine->newNumberPrototype(ctx, ctor))); + ctor->setProperty(ctx, QLatin1String("prototype"), Value::fromObject(ctx->engine->newDatePrototype(ctx, ctor))); return Value::fromObject(ctor); } @@ -1110,18 +1110,17 @@ DateCtor::DateCtor(Context *scope) void DateCtor::construct(Context *ctx) { - // called as constructor - double t; + double t = 0; if (ctx->argumentCount == 0) t = currentTime(); else if (ctx->argumentCount == 1) { Value arg = ctx->argument(0); -// if (arg.isDate()) -// arg = arg.internalValue(); -// else -// __qmljs_to_primitive(ctx, &arg, &arg, PREFERREDTYPE_HINT); + if (DateObject *d = arg.asDateObject()) + arg = d->value; + else + __qmljs_to_primitive(ctx, &arg, &arg, PREFERREDTYPE_HINT); if (arg.isString()) t = ParseString(arg.toString(ctx)->toQString()); @@ -1129,7 +1128,7 @@ void DateCtor::construct(Context *ctx) t = TimeClip(arg.toNumber(ctx)); } - else { // context->argumentCount() > 1 + else { // ctx->argumentCount() > 1 double year = ctx->argument(0).toNumber(ctx); double month = ctx->argument(1).toNumber(ctx); double day = ctx->argumentCount >= 3 ? ctx->argument(2).toNumber(ctx) : 1; @@ -1143,11 +1142,8 @@ void DateCtor::construct(Context *ctx) t = TimeClip(UTC(t)); } -// Value &obj = ctx->m_thisObject; -// obj.setClassInfo(classInfo()); -// obj.setInternalValue(Value(t)); -// obj.setPrototype(publicPrototype); -// ctx->setReturnValue(obj); + Object *d = ctx->engine->newDateObject(Value::fromNumber(t)); + ctx->thisObject = Value::fromObject(d); } void DateCtor::call(Context *ctx) @@ -1157,6 +1153,7 @@ void DateCtor::call(Context *ctx) } DatePrototype::DatePrototype(Context *ctx, FunctionObject *ctor) + : DateObject(Value::fromNumber(qSNaN())) { LocalTZA = getLocalTZA(); @@ -1210,11 +1207,19 @@ DatePrototype::DatePrototype(Context *ctx, FunctionObject *ctor) setProperty(ctx, QLatin1String("toGMTString"), method_toUTCString, 0); } +DateObject *DatePrototype::getThisDateObject(Context *ctx) +{ + assert(ctx->thisObject.isObject()); + DateObject *date = ctx->thisObject.objectValue->asDateObject(); + return date; +} + double DatePrototype::getThisDate(Context *ctx) { assert(ctx->thisObject.isObject()); - Value internalValue; - ctx->thisObject.objectValue->defaultValue(ctx, &internalValue, NUMBER_HINT); + DateObject *date = ctx->thisObject.objectValue->asDateObject(); + assert(date); + Value internalValue = date->value; assert(internalValue.isNumber()); return internalValue.numberValue; } @@ -1449,70 +1454,244 @@ void DatePrototype::method_getTimezoneOffset(Context *ctx) void DatePrototype::method_setTime(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + self->value.numberValue = TimeClip(ctx->argument(0).toNumber(ctx)); + ctx->result = self->value; + } else { + assert(!"type error"); + } } void DatePrototype::method_setMilliseconds(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = LocalTime(self->value.numberValue); + double ms = ctx->argument(0).toNumber(ctx); + self->value.numberValue = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))); + ctx->result = self->value; + } else { + assert(!"type error"); + } } void DatePrototype::method_setUTCMilliseconds(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = self->value.numberValue; + double ms = ctx->argument(0).toNumber(ctx); + self->value.numberValue = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))); + ctx->result = self->value; + } else { + assert(!"type error"); + } } void DatePrototype::method_setSeconds(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = LocalTime(self->value.numberValue); + double sec = ctx->argument(0).toNumber(ctx); + double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); + self->value.numberValue = t; + ctx->result = self->value; + } else { + assert(!"type error"); + } } void DatePrototype::method_setUTCSeconds(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = self->value.numberValue; + double sec = ctx->argument(0).toNumber(ctx); + double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); + self->value.numberValue = t; + ctx->result = self->value; + } else { + assert(!"type error"); + } } void DatePrototype::method_setMinutes(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = LocalTime(self->value.numberValue); + double min = ctx->argument(0).toNumber(ctx); + double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); + double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); + self->value.numberValue = t; + ctx->result = self->value; + } else { + assert(!"type error"); + } } void DatePrototype::method_setUTCMinutes(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = self->value.numberValue; + double min = ctx->argument(0).toNumber(ctx); + double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(ctx); + double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); + self->value.numberValue = t; + ctx->result = self->value; + } else { + assert(!"type error"); + } } void DatePrototype::method_setHours(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = LocalTime(self->value.numberValue); + double hour = ctx->argument(0).toNumber(ctx); + double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); + double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx); + double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); + self->value.numberValue = t; + ctx->result = self->value; + } else { + assert(!"type error"); + } } void DatePrototype::method_setUTCHours(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = self->value.numberValue; + double hour = ctx->argument(0).toNumber(ctx); + double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(ctx); + double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(ctx); + double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber(ctx); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); + self->value.numberValue = t; + ctx->result = self->value; + } else { + assert(!"type error"); + } } void DatePrototype::method_setDate(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = LocalTime(self->value.numberValue); + double date = ctx->argument(0).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); + self->value.numberValue = t; + ctx->result = self->value; + } else { + assert(!"type error"); + } } void DatePrototype::method_setUTCDate(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = self->value.numberValue; + double date = ctx->argument(0).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); + self->value.numberValue = t; + ctx->result = self->value; + } else { + assert(!"type error"); + } } void DatePrototype::method_setMonth(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = LocalTime(self->value.numberValue); + double month = ctx->argument(0).toNumber(ctx); + double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); + self->value.numberValue = t; + ctx->result = self->value; + } else { + assert(!"type error"); + } } void DatePrototype::method_setUTCMonth(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = self->value.numberValue; + double month = ctx->argument(0).toNumber(ctx); + double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); + self->value.numberValue = t; + ctx->result = self->value; + } else { + assert(!"type error"); + } } void DatePrototype::method_setYear(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = self->value.numberValue; + if (qIsNaN(t)) + t = 0; + else + t = LocalTime(t); + double year = ctx->argument(0).toNumber(ctx); + double r; + if (qIsNaN(year)) { + r = qSNaN(); + } else { + if ((Value::toInteger(year) >= 0) && (Value::toInteger(year) <= 99)) + year += 1900; + r = MakeDay(year, MonthFromTime(t), DateFromTime(t)); + r = UTC(MakeDate(r, TimeWithinDay(t))); + r = TimeClip(r); + } + self->value.numberValue = r; + ctx->result = self->value; + } else { + assert(!"type error"); + } } -void DatePrototype::method_setFullYear(Context *ctx) +void DatePrototype::method_setUTCFullYear(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = self->value.numberValue; + double year = ctx->argument(0).toNumber(ctx); + double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); + double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); + self->value.numberValue = t; + ctx->result = self->value; + } else { + assert(!"type error"); + } } -void DatePrototype::method_setUTCFullYear(Context *ctx) +void DatePrototype::method_setFullYear(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = LocalTime(self->value.numberValue); + double year = ctx->argument(0).toNumber(ctx); + double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(ctx); + double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(ctx); + t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); + self->value.numberValue = t; + ctx->result = self->value; + } else { + assert(!"type error"); + } } void DatePrototype::method_toUTCString(Context *ctx) { + if (DateObject *self = getThisDateObject(ctx)) { + double t = self->value.numberValue; + ctx->result = Value::fromString(ctx, ToUTCString(t)); + } } // diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index 68c1522..d4cbf61 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -112,11 +112,12 @@ struct DateCtor: FunctionObject virtual void call(Context *ctx); }; -struct DatePrototype: Object +struct DatePrototype: DateObject { DatePrototype(Context *ctx, FunctionObject *ctor); protected: + static DateObject *getThisDateObject(Context *ctx); static double getThisDate(Context *ctx); static void method_MakeTime(Context *ctx); -- 2.7.4