Never convert the this object when calling a builtin function
authorLars Knoll <lars.knoll@digia.com>
Fri, 16 Aug 2013 18:40:03 +0000 (20:40 +0200)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Sat, 17 Aug 2013 07:26:40 +0000 (09:26 +0200)
When calling builtin methods, the this object should should be
passed unmodified to the method. This failed so far because some
of our buitin methods where implemented slightly wrong.

Change-Id: I725f4dc952b4af6101645cf702e01b5410406a92
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
src/qml/jsruntime/qv4arrayobject.cpp
src/qml/jsruntime/qv4functionobject.cpp
src/qml/jsruntime/qv4numberobject.cpp

index 214ec93..ee69be4 100644 (file)
@@ -147,12 +147,15 @@ Value ArrayPrototype::method_concat(SimpleCallContext *ctx)
 
     if (ArrayObject *instance = ctx->thisObject.asArrayObject()) {
         result->copyArrayData(instance);
+    } else if (ctx->thisObject.isString()) {
+        QString v = ctx->thisObject.stringValue()->toQString();
+        result->arraySet(0, Value::fromString(ctx, v));
     } else if (ctx->thisObject.asStringObject()) {
         QString v = ctx->thisObject.toString(ctx)->toQString();
         result->arraySet(0, Value::fromString(ctx, v));
     } else {
-        Object *instance = ctx->thisObject.asObject();
-        result->arraySet(0, ctx->thisObject);
+        Object *instance = ctx->thisObject.toObject(ctx);
+        result->arraySet(0, Value::fromObject(instance));
     }
 
     for (uint i = 0; i < ctx->argumentCount; ++i) {
@@ -646,7 +649,7 @@ Value ArrayPrototype::method_every(SimpleCallContext *ctx)
         Value args[3];
         args[0] = v;
         args[1] = Value::fromDouble(k);
-        args[2] = ctx->thisObject;
+        args[2] = Value::fromObject(instance);
         Value r = callback->call(thisArg, args, 3);
         ok = r.toBoolean();
     }
@@ -674,7 +677,7 @@ Value ArrayPrototype::method_some(SimpleCallContext *ctx)
         Value args[3];
         args[0] = v;
         args[1] = Value::fromDouble(k);
-        args[2] = ctx->thisObject;
+        args[2] = Value::fromObject(instance);
         Value r = callback->call(thisArg, args, 3);
         if (r.toBoolean())
             return Value::fromBoolean(true);
@@ -703,7 +706,7 @@ Value ArrayPrototype::method_forEach(SimpleCallContext *ctx)
         Value args[3];
         args[0] = v;
         args[1] = Value::fromDouble(k);
-        args[2] = ctx->thisObject;
+        args[2] = Value::fromObject(instance);
         callback->call(thisArg, args, 3);
     }
     return Value::undefinedValue();
@@ -734,7 +737,7 @@ Value ArrayPrototype::method_map(SimpleCallContext *ctx)
         Value args[3];
         args[0] = v;
         args[1] = Value::fromDouble(k);
-        args[2] = ctx->thisObject;
+        args[2] = Value::fromObject(instance);
         Value mapped = callback->call(thisArg, args, 3);
         a->arraySet(k, mapped);
     }
@@ -766,7 +769,7 @@ Value ArrayPrototype::method_filter(SimpleCallContext *ctx)
         Value args[3];
         args[0] = v;
         args[1] = Value::fromDouble(k);
-        args[2] = ctx->thisObject;
+        args[2] = Value::fromObject(instance);
         Value selected = callback->call(thisArg, args, 3);
         if (selected.toBoolean()) {
             a->arraySet(to, v);
@@ -810,7 +813,7 @@ Value ArrayPrototype::method_reduce(SimpleCallContext *ctx)
             args[0] = acc;
             args[1] = v;
             args[2] = Value::fromDouble(k);
-            args[3] = ctx->thisObject;
+            args[3] = Value::fromObject(instance);
             acc = callback->call(Value::undefinedValue(), args, 4);
         }
         ++k;
@@ -858,7 +861,7 @@ Value ArrayPrototype::method_reduceRight(SimpleCallContext *ctx)
             args[0] = acc;
             args[1] = v;
             args[2] = Value::fromDouble(k - 1);
-            args[3] = ctx->thisObject;
+            args[3] = Value::fromObject(instance);
             acc = callback->call(Value::undefinedValue(), args, 4);
         }
         --k;
index 58a0bbb..68137f6 100644 (file)
@@ -447,15 +447,7 @@ Value BuiltinFunctionOld::call(Managed *that, const Value &thisObject, Value *ar
     ctx.argumentCount = argc;
     v4->pushContext(&ctx);
 
-    if (!f->strictMode && !thisObject.isObject()) {
-        // Built-in functions allow for the this object to be null or undefined. This overrides
-        // the behaviour of changing thisObject to the global object if null/undefined and allows
-        // the built-in functions for example to throw a type error if null is passed.
-        if (!thisObject.isUndefined() && !thisObject.isNull())
-            ctx.thisObject = Value::fromObject(thisObject.toObject(context));
-    }
-
-    Value result = Value::undefinedValue();
+    Value result;
     try {
         result = f->code(&ctx);
     } catch (Exception &ex) {
@@ -481,15 +473,7 @@ Value IndexedBuiltinFunction::call(Managed *that, const Value &thisObject, Value
     ctx.argumentCount = argc;
     v4->pushContext(&ctx);
 
-    if (!f->strictMode && !thisObject.isObject()) {
-        // Built-in functions allow for the this object to be null or undefined. This overrides
-        // the behaviour of changing thisObject to the global object if null/undefined and allows
-        // the built-in functions for example to throw a type error if null is passed.
-        if (!thisObject.isUndefined() && !thisObject.isNull())
-            ctx.thisObject = Value::fromObject(thisObject.toObject(context));
-    }
-
-    Value result = Value::undefinedValue();
+    Value result;
     try {
         result = f->code(&ctx, f->index);
     } catch (Exception &ex) {
index 266fa79..6d3b622 100644 (file)
@@ -96,17 +96,19 @@ void NumberPrototype::init(ExecutionContext *ctx, const Value &ctor)
     defineDefaultProperty(ctx, QStringLiteral("toPrecision"), method_toPrecision);
 }
 
+inline Value thisNumberValue(ExecutionContext *ctx)
+{
+    if (ctx->thisObject.isNumber())
+        return ctx->thisObject;
+    NumberObject *n = ctx->thisObject.asNumberObject();
+    if (!n)
+        ctx->throwTypeError();
+    return n->value;
+}
+
 Value NumberPrototype::method_toString(SimpleCallContext *ctx)
 {
-    double num;
-    if (ctx->thisObject.isNumber()) {
-        num = ctx->thisObject.asDouble();
-    } else {
-        NumberObject *thisObject = ctx->thisObject.asNumberObject();
-        if (!thisObject)
-            ctx->throwTypeError();
-        num = thisObject->value.asDouble();
-    }
+    double num = thisNumberValue(ctx).asDouble();
 
     Value arg = ctx->argument(0);
     if (!arg.isUndefined()) {
@@ -160,28 +162,20 @@ Value NumberPrototype::method_toString(SimpleCallContext *ctx)
 
 Value NumberPrototype::method_toLocaleString(SimpleCallContext *ctx)
 {
-    NumberObject *thisObject = ctx->thisObject.asNumberObject();
-    if (!thisObject)
-        ctx->throwTypeError();
+    Value v = thisNumberValue(ctx);
 
-    String *str = thisObject->value.toString(ctx);
+    String *str = v.toString(ctx);
     return Value::fromString(str);
 }
 
 Value NumberPrototype::method_valueOf(SimpleCallContext *ctx)
 {
-    NumberObject *thisObject = ctx->thisObject.asNumberObject();
-    if (!thisObject)
-        ctx->throwTypeError();
-
-    return thisObject->value;
+    return thisNumberValue(ctx);
 }
 
 Value NumberPrototype::method_toFixed(SimpleCallContext *ctx)
 {
-    NumberObject *thisObject = ctx->thisObject.asNumberObject();
-    if (!thisObject)
-        ctx->throwTypeError();
+    double v = thisNumberValue(ctx).asDouble();
 
     double fdigits = 0;
 
@@ -194,7 +188,6 @@ Value NumberPrototype::method_toFixed(SimpleCallContext *ctx)
     if (fdigits < 0 || fdigits > 20)
         ctx->throwRangeError(ctx->thisObject);
 
-    double v = thisObject->value.asDouble();
     QString str;
     if (std::isnan(v))
         str = QString::fromLatin1("NaN");
@@ -209,9 +202,7 @@ Value NumberPrototype::method_toFixed(SimpleCallContext *ctx)
 
 Value NumberPrototype::method_toExponential(SimpleCallContext *ctx)
 {
-    NumberObject *thisObject = ctx->thisObject.asNumberObject();
-    if (!thisObject)
-        ctx->throwTypeError();
+    double d = thisNumberValue(ctx).asDouble();
 
     Value fraction = ctx->argument(0);
     int fdigits = -1;
@@ -226,7 +217,7 @@ Value NumberPrototype::method_toExponential(SimpleCallContext *ctx)
 
     char str[100];
     double_conversion::StringBuilder builder(str, sizeof(str));
-    double_conversion::DoubleToStringConverter::EcmaScriptConverter().ToExponential(thisObject->value.asDouble(), fdigits, &builder);
+    double_conversion::DoubleToStringConverter::EcmaScriptConverter().ToExponential(d, fdigits, &builder);
     QString result = QString::fromLatin1(builder.Finalize());
 
     return Value::fromString(ctx, result);
@@ -234,13 +225,11 @@ Value NumberPrototype::method_toExponential(SimpleCallContext *ctx)
 
 Value NumberPrototype::method_toPrecision(SimpleCallContext *ctx)
 {
-    NumberObject *thisObject = ctx->thisObject.asNumberObject();
-    if (!thisObject)
-        ctx->throwTypeError();
+    Value v = thisNumberValue(ctx);
 
     Value prec = ctx->argument(0);
     if (prec.isUndefined())
-        return __qmljs_to_string(thisObject->value, ctx);
+        return __qmljs_to_string(v, ctx);
 
     double precision = prec.toInt32();
     if (precision < 1 || precision > 21) {
@@ -250,7 +239,7 @@ Value NumberPrototype::method_toPrecision(SimpleCallContext *ctx)
 
     char str[100];
     double_conversion::StringBuilder builder(str, sizeof(str));
-    double_conversion::DoubleToStringConverter::EcmaScriptConverter().ToPrecision(thisObject->value.asDouble(), precision, &builder);
+    double_conversion::DoubleToStringConverter::EcmaScriptConverter().ToPrecision(v.asDouble(), precision, &builder);
     QString result = QString::fromLatin1(builder.Finalize());
 
     return Value::fromString(ctx, result);