From 0da208381f98ffd22790716a144aa5f22a5fd07e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 16 Aug 2013 20:40:03 +0200 Subject: [PATCH] Never convert the this object when calling a builtin function 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 --- src/qml/jsruntime/qv4arrayobject.cpp | 21 ++++++++------ src/qml/jsruntime/qv4functionobject.cpp | 20 ++----------- src/qml/jsruntime/qv4numberobject.cpp | 51 +++++++++++++-------------------- 3 files changed, 34 insertions(+), 58 deletions(-) diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index 214ec93..ee69be4 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -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; diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 58a0bbb..68137f6 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -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) { diff --git a/src/qml/jsruntime/qv4numberobject.cpp b/src/qml/jsruntime/qv4numberobject.cpp index 266fa79..6d3b622 100644 --- a/src/qml/jsruntime/qv4numberobject.cpp +++ b/src/qml/jsruntime/qv4numberobject.cpp @@ -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); -- 2.7.4