Initial implementation of the Math Object.
authorRoberto Raggi <roberto.raggi@nokia.com>
Mon, 14 May 2012 15:38:53 +0000 (17:38 +0200)
committerRoberto Raggi <roberto.raggi@nokia.com>
Mon, 14 May 2012 15:38:53 +0000 (17:38 +0200)
qmljs_objects.cpp
qmljs_objects.h
qv4ecmaobjects.cpp
qv4ecmaobjects_p.h

index 704eead..aed6c37 100644 (file)
@@ -17,8 +17,9 @@ void Object::setProperty(Context *ctx, const QString &name, const Value &value)
     put(ctx->engine->identifier(name), value);
 }
 
-void Object::setProperty(Context *ctx, const QString &name, void (*code)(Context *))
+void Object::setProperty(Context *ctx, const QString &name, void (*code)(Context *), int count)
 {
+    Q_UNUSED(count);
     setProperty(ctx, name, Value::object(ctx, new NativeFunction(ctx, code)));
 }
 
@@ -131,6 +132,7 @@ ScriptFunction::ScriptFunction(Context *scope, IR::Function *function)
     : FunctionObject(scope)
     , function(function)
 {
+    needsActivation = function->needsActivation();
     formalParameterCount = function->formals.size();
     if (formalParameterCount) {
         formalParameterList = new String*[formalParameterCount];
@@ -198,6 +200,7 @@ ExecutionEngine::ExecutionEngine()
     glo->put(VM::String::get(rootContext, QLatin1String("Object")), objectCtor);
     glo->put(VM::String::get(rootContext, QLatin1String("String")), stringCtor);
     glo->put(VM::String::get(rootContext, QLatin1String("Number")), numberCtor);
+    glo->put(VM::String::get(rootContext, QLatin1String("Math")), Value::object(rootContext, new MathObject(rootContext)));
 }
 
 String *ExecutionEngine::identifier(const QString &s)
index 7d1a328..6c64491 100644 (file)
@@ -216,7 +216,7 @@ struct Object {
     // helpers
     //
     void setProperty(Context *ctx, const QString &name, const Value &value);
-    void setProperty(Context *ctx, const QString &name, void (*code)(Context *));
+    void setProperty(Context *ctx, const QString &name, void (*code)(Context *), int count = 0);
 };
 
 struct BooleanObject: Object {
index 9b678dc..1e00940 100644 (file)
@@ -6,6 +6,8 @@
 
 using namespace QQmlJS::VM;
 
+static const double qt_PI = 2.0 * ::asin(1.0);
+
 //
 // Object
 //
@@ -236,7 +238,7 @@ void StringPrototype::method_slice(Context *ctx)
 
     int start = int (ctx->argument(0).toInteger(ctx));
     int end = ctx->argument(1).isUndefined()
-              ? length : int (ctx->argument(1).toInteger(ctx));
+            ? length : int (ctx->argument(1).toInteger(ctx));
 
     if (start < 0)
         start = qMax(length + start, 0);
@@ -406,17 +408,17 @@ void NumberPrototype::method_toString(Context *ctx)
 {
     Value self = ctx->thisObject;
     assert(self.isObject());
-//    if (self.classInfo() != classInfo) {
-//        return throwThisObjectTypeError(
-//            ctx, QLatin1String("Number.prototype.toString"));
-//    }
+    //    if (self.classInfo() != classInfo) {
+    //        return throwThisObjectTypeError(
+    //            ctx, QLatin1String("Number.prototype.toString"));
+    //    }
 
     Value arg = ctx->argument(0);
     if (!arg.isUndefined()) {
         int radix = arg.toInt32(ctx);
-//        if (radix < 2 || radix > 36)
-//            return ctx->throwError(QString::fromLatin1("Number.prototype.toString: %0 is not a valid radix")
-//                                       .arg(radix));
+        //        if (radix < 2 || radix > 36)
+        //            return ctx->throwError(QString::fromLatin1("Number.prototype.toString: %0 is not a valid radix")
+        //                                       .arg(radix));
         if (radix != 10) {
             Value internalValue;
             self.objectValue->defaultValue(ctx, &internalValue, NUMBER_HINT);
@@ -471,10 +473,10 @@ void NumberPrototype::method_toLocaleString(Context *ctx)
 {
     Value self = ctx->thisObject;
     assert(self.isObject());
-//    if (self.classInfo() != classInfo) {
-//        return throwThisObjectTypeError(
-//            context, QLatin1String("Number.prototype.toLocaleString"));
-//    }
+    //    if (self.classInfo() != classInfo) {
+    //        return throwThisObjectTypeError(
+    //            context, QLatin1String("Number.prototype.toLocaleString"));
+    //    }
     Value internalValue;
     self.objectValue->defaultValue(ctx, &internalValue, STRING_HINT);
     String *str = internalValue.toString(ctx);
@@ -485,10 +487,10 @@ void NumberPrototype::method_valueOf(Context *ctx)
 {
     Value self = ctx->thisObject;
     assert(self.isObject());
-//    if (self.classInfo() != classInfo) {
-//        return throwThisObjectTypeError(
-//            context, QLatin1String("Number.prototype.toLocaleString"));
-//    }
+    //    if (self.classInfo() != classInfo) {
+    //        return throwThisObjectTypeError(
+    //            context, QLatin1String("Number.prototype.toLocaleString"));
+    //    }
     Value internalValue;
     self.objectValue->defaultValue(ctx, &internalValue, NUMBER_HINT);
     ctx->result = internalValue;
@@ -498,10 +500,10 @@ void NumberPrototype::method_toFixed(Context *ctx)
 {
     Value self = ctx->thisObject;
     assert(self.isObject());
-//    if (self.classInfo() != classInfo) {
-//        return throwThisObjectTypeError(
-//            ctx, QLatin1String("Number.prototype.toFixed"));
-//    }
+    //    if (self.classInfo() != classInfo) {
+    //        return throwThisObjectTypeError(
+    //            ctx, QLatin1String("Number.prototype.toFixed"));
+    //    }
     double fdigits = 0;
 
     if (ctx->argumentCount > 0)
@@ -528,10 +530,10 @@ void NumberPrototype::method_toExponential(Context *ctx)
 {
     Value self = ctx->thisObject;
     assert(self.isObject());
-//    if (self.classInfo() != classInfo) {
-//        return throwThisObjectTypeError(
-//            ctx, QLatin1String("Number.prototype.toFixed"));
-//    }
+    //    if (self.classInfo() != classInfo) {
+    //        return throwThisObjectTypeError(
+    //            ctx, QLatin1String("Number.prototype.toFixed"));
+    //    }
     double fdigits = 0;
 
     if (ctx->argumentCount > 0)
@@ -549,10 +551,10 @@ void NumberPrototype::method_toPrecision(Context *ctx)
 {
     Value self = ctx->thisObject;
     assert(self.isObject());
-//    if (self.classInfo() != classInfo) {
-//        return throwThisObjectTypeError(
-//            ctx, QLatin1String("Number.prototype.toFixed"));
-//    }
+    //    if (self.classInfo() != classInfo) {
+    //        return throwThisObjectTypeError(
+    //            ctx, QLatin1String("Number.prototype.toFixed"));
+    //    }
     double fdigits = 0;
 
     if (ctx->argumentCount > 0)
@@ -564,3 +566,255 @@ void NumberPrototype::method_toPrecision(Context *ctx)
     double v = internalValue.toNumber(ctx);
     ctx->result = Value::string(ctx, QString::number(v, 'g', int (fdigits)));
 }
+
+//
+// Math object
+//
+MathObject::MathObject(Context *ctx)
+{
+    setProperty(ctx, QLatin1String("E"), Value::number(ctx, ::exp(1.0)));
+    setProperty(ctx, QLatin1String("LN2"), Value::number(ctx, ::log(2.0)));
+    setProperty(ctx, QLatin1String("LN10"), Value::number(ctx, ::log(10.0)));
+    setProperty(ctx, QLatin1String("LOG2E"), Value::number(ctx, 1.0/::log(2.0)));
+    setProperty(ctx, QLatin1String("LOG10E"), Value::number(ctx, 1.0/::log(10.0)));
+    setProperty(ctx, QLatin1String("PI"), Value::number(ctx, qt_PI));
+    setProperty(ctx, QLatin1String("SQRT1_2"), Value::number(ctx, ::sqrt(0.5)));
+    setProperty(ctx, QLatin1String("SQRT2"), Value::number(ctx, ::sqrt(2.0)));
+
+    setProperty(ctx, QLatin1String("abs"), method_abs, 1);
+    setProperty(ctx, QLatin1String("acos"), method_acos, 1);
+    setProperty(ctx, QLatin1String("asin"), method_asin, 0);
+    setProperty(ctx, QLatin1String("atan"), method_atan, 1);
+    setProperty(ctx, QLatin1String("atan2"), method_atan2, 2);
+    setProperty(ctx, QLatin1String("ceil"), method_ceil, 1);
+    setProperty(ctx, QLatin1String("cos"), method_cos, 1);
+    setProperty(ctx, QLatin1String("exp"), method_exp, 1);
+    setProperty(ctx, QLatin1String("floor"), method_floor, 1);
+    setProperty(ctx, QLatin1String("log"), method_log, 1);
+    setProperty(ctx, QLatin1String("max"), method_max, 2);
+    setProperty(ctx, QLatin1String("min"), method_min, 2);
+    setProperty(ctx, QLatin1String("pow"), method_pow, 2);
+    setProperty(ctx, QLatin1String("random"), method_random, 0);
+    setProperty(ctx, QLatin1String("round"), method_round, 1);
+    setProperty(ctx, QLatin1String("sin"), method_sin, 1);
+    setProperty(ctx, QLatin1String("sqrt"), method_sqrt, 1);
+    setProperty(ctx, QLatin1String("tan"), method_tan, 1);
+}
+
+/* copies the sign from y to x and returns the result */
+static double copySign(double x, double y)
+{
+    uchar *xch = (uchar *)&x;
+    uchar *ych = (uchar *)&y;
+    if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
+        xch[0] = (xch[0] & 0x7f) | (ych[0] & 0x80);
+    else
+        xch[7] = (xch[7] & 0x7f) | (ych[7] & 0x80);
+    return x;
+}
+
+void MathObject::method_abs(Context *ctx)
+{
+    double v = ctx->argument(0).toNumber(ctx);
+    if (v == 0) // 0 | -0
+        ctx->result = Value::number(ctx, 0);
+    else
+        ctx->result = Value::number(ctx, v < 0 ? -v : v);
+}
+
+void MathObject::method_acos(Context *ctx)
+{
+    double v = ctx->argument(0).toNumber(ctx);
+    if (v > 1)
+        ctx->result = Value::number(ctx, qSNaN());
+    else
+        ctx->result = Value::number(ctx, ::acos(v));
+}
+
+void MathObject::method_asin(Context *ctx)
+{
+    double v = ctx->argument(0).toNumber(ctx);
+    if (v > 1)
+        ctx->result = Value::number(ctx, qSNaN());
+    else
+        ctx->result = Value::number(ctx, ::asin(v));
+}
+
+void MathObject::method_atan(Context *ctx)
+{
+    double v = ctx->argument(0).toNumber(ctx);
+    if (v == 0.0)
+        ctx->result = Value::number(ctx, v);
+    else
+        ctx->result = Value::number(ctx, ::atan(v));
+}
+
+void MathObject::method_atan2(Context *ctx)
+{
+    double v1 = ctx->argument(0).toNumber(ctx);
+    double v2 = ctx->argument(1).toNumber(ctx);
+    if ((v1 < 0) && qIsFinite(v1) && qIsInf(v2) && (copySign(1.0, v2) == 1.0)) {
+        ctx->result = Value::number(ctx, copySign(0, -1.0));
+        return;
+    }
+    if ((v1 == 0.0) && (v2 == 0.0)) {
+        if ((copySign(1.0, v1) == 1.0) && (copySign(1.0, v2) == -1.0)) {
+            ctx->result = Value::number(ctx, qt_PI);
+            return;
+        } else if ((copySign(1.0, v1) == -1.0) && (copySign(1.0, v2) == -1.0)) {
+            ctx->result = Value::number(ctx, -qt_PI);
+            return;
+        }
+    }
+    ctx->result = Value::number(ctx, ::atan2(v1, v2));
+}
+
+void MathObject::method_ceil(Context *ctx)
+{
+    double v = ctx->argument(0).toNumber(ctx);
+    if (v < 0.0 && v > -1.0)
+        ctx->result = Value::number(ctx, copySign(0, -1.0));
+    else
+        ctx->result = Value::number(ctx, ::ceil(v));
+}
+
+void MathObject::method_cos(Context *ctx)
+{
+    double v = ctx->argument(0).toNumber(ctx);
+    ctx->result = Value::number(ctx, ::cos(v));
+}
+
+void MathObject::method_exp(Context *ctx)
+{
+    double v = ctx->argument(0).toNumber(ctx);
+    if (qIsInf(v)) {
+        if (copySign(1.0, v) == -1.0)
+            ctx->result = Value::number(ctx, 0);
+        else
+            ctx->result = Value::number(ctx, qInf());
+    } else {
+        ctx->result = Value::number(ctx, ::exp(v));
+    }
+}
+
+void MathObject::method_floor(Context *ctx)
+{
+    double v = ctx->argument(0).toNumber(ctx);
+    ctx->result = Value::number(ctx, ::floor(v));
+}
+
+void MathObject::method_log(Context *ctx)
+{
+    double v = ctx->argument(0).toNumber(ctx);
+    if (v < 0)
+        ctx->result = Value::number(ctx, qSNaN());
+    else
+        ctx->result = Value::number(ctx, ::log(v));
+}
+
+void MathObject::method_max(Context *ctx)
+{
+    double mx = -qInf();
+    for (unsigned i = 0; i < ctx->argumentCount; ++i) {
+        double x = ctx->argument(i).toNumber(ctx);
+        if (x > mx || qIsNaN(x))
+            mx = x;
+    }
+    ctx->result = Value::number(ctx, mx);
+}
+
+void MathObject::method_min(Context *ctx)
+{
+    double mx = qInf();
+    for (unsigned i = 0; i < ctx->argumentCount; ++i) {
+        double x = ctx->argument(i).toNumber(ctx);
+        if ((x == 0 && mx == x && copySign(1.0, x) == -1.0)
+                || (x < mx) || qIsNaN(x)) {
+            mx = x;
+        }
+    }
+    ctx->result = Value::number(ctx, mx);
+}
+
+void MathObject::method_pow(Context *ctx)
+{
+    double x = ctx->argument(0).toNumber(ctx);
+    double y = ctx->argument(1).toNumber(ctx);
+
+    if (qIsNaN(y)) {
+        ctx->result = Value::number(ctx, qSNaN());
+        return;
+    }
+
+    if (y == 0) {
+        ctx->result = Value::number(ctx, 1);
+    } else if (((x == 1) || (x == -1)) && qIsInf(y)) {
+        ctx->result = Value::number(ctx, qSNaN());
+    } else if (((x == 0) && copySign(1.0, x) == 1.0) && (y < 0)) {
+        ctx->result = Value::number(ctx, qInf());
+    } else if ((x == 0) && copySign(1.0, x) == -1.0) {
+        if (y < 0) {
+            if (::fmod(-y, 2.0) == 1.0)
+                ctx->result = Value::number(ctx, -qInf());
+            else
+                ctx->result = Value::number(ctx, qInf());
+        } else if (y > 0) {
+            if (::fmod(y, 2.0) == 1.0)
+                ctx->result = Value::number(ctx, copySign(0, -1.0));
+            else
+                ctx->result = Value::number(ctx, 0);
+        }
+    }
+
+#ifdef Q_OS_AIX
+    else if (qIsInf(x) && copySign(1.0, x) == -1.0) {
+        if (y > 0) {
+            if (::fmod(y, 2.0) == 1.0)
+                ctx->result = Value::number(ctx, -qInf());
+            else
+                ctx->result = Value::number(ctx, qInf());
+        } else if (y < 0) {
+            if (::fmod(-y, 2.0) == 1.0)
+                ctx->result = Value::number(ctx, copySign(0, -1.0));
+            else
+                ctx->result = Value::number(ctx, 0);
+        }
+    }
+#endif
+    else {
+        ctx->result = Value::number(ctx, ::pow(x, y));
+    }
+}
+
+void MathObject::method_random(Context *ctx)
+{
+    ctx->result = Value::number(ctx, qrand() / (double) RAND_MAX);
+}
+
+void MathObject::method_round(Context *ctx)
+{
+    double v = ctx->argument(0).toNumber(ctx);
+    v = copySign(::floor(v + 0.5), v);
+    ctx->result = Value::number(ctx, v);
+}
+
+void MathObject::method_sin(Context *ctx)
+{
+    double v = ctx->argument(0).toNumber(ctx);
+    ctx->result = Value::number(ctx, ::sin(v));
+}
+
+void MathObject::method_sqrt(Context *ctx)
+{
+    double v = ctx->argument(0).toNumber(ctx);
+    ctx->result = Value::number(ctx, ::sqrt(v));
+}
+
+void MathObject::method_tan(Context *ctx)
+{
+    double v = ctx->argument(0).toNumber(ctx);
+    if (v == 0.0)
+        ctx->result = Value::number(ctx, v);
+    else
+        ctx->result = Value::number(ctx, ::tan(v));
+}
index 4d8cb1d..386c2f1 100644 (file)
@@ -83,6 +83,31 @@ protected:
     static void method_toPrecision(Context *ctx);
 };
 
+struct MathObject: Object
+{
+    MathObject(Context *ctx);
+
+protected:
+    static void method_abs(Context *ctx);
+    static void method_acos(Context *ctx);
+    static void method_asin(Context *ctx);
+    static void method_atan(Context *ctx);
+    static void method_atan2(Context *ctx);
+    static void method_ceil(Context *ctx);
+    static void method_cos(Context *ctx);
+    static void method_exp(Context *ctx);
+    static void method_floor(Context *ctx);
+    static void method_log(Context *ctx);
+    static void method_max(Context *ctx);
+    static void method_min(Context *ctx);
+    static void method_pow(Context *ctx);
+    static void method_random(Context *ctx);
+    static void method_round(Context *ctx);
+    static void method_sin(Context *ctx);
+    static void method_sqrt(Context *ctx);
+    static void method_tan(Context *ctx);
+};
+
 } // end of namespace VM
 } // end of namespace QQmlJS