qtdeclarative/src/qml/jsruntime/qv4numberobject.cpp

269 lines
9.7 KiB
C++

/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the QtQml module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** As a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qv4numberobject_p.h"
#include "qv4runtime_p.h"
#include "qv4string_p.h"
#include <QtCore/qnumeric.h>
#include <QtCore/qmath.h>
#include <QtCore/QDebug>
#include <cassert>
#include <double-conversion.h>
using namespace QV4;
DEFINE_OBJECT_VTABLE(NumberCtor);
DEFINE_OBJECT_VTABLE(NumberObject);
Heap::NumberCtor::NumberCtor(QV4::ExecutionContext *scope)
: Heap::FunctionObject(scope, QStringLiteral("Number"))
{
}
ReturnedValue NumberCtor::construct(const Managed *m, CallData *callData)
{
Scope scope(m->cast<NumberCtor>()->engine());
double dbl = callData->argc ? callData->args[0].toNumber() : 0.;
return Encode(scope.engine->newNumberObject(dbl));
}
ReturnedValue NumberCtor::call(const Managed *, CallData *callData)
{
double dbl = callData->argc ? callData->args[0].toNumber() : 0.;
return Encode(dbl);
}
void NumberPrototype::init(ExecutionEngine *engine, Object *ctor)
{
Scope scope(engine);
ScopedObject o(scope);
ctor->defineReadonlyProperty(engine->id_prototype, (o = this));
ctor->defineReadonlyProperty(engine->id_length, Primitive::fromInt32(1));
ctor->defineReadonlyProperty(QStringLiteral("NaN"), Primitive::fromDouble(qSNaN()));
ctor->defineReadonlyProperty(QStringLiteral("NEGATIVE_INFINITY"), Primitive::fromDouble(-qInf()));
ctor->defineReadonlyProperty(QStringLiteral("POSITIVE_INFINITY"), Primitive::fromDouble(qInf()));
ctor->defineReadonlyProperty(QStringLiteral("MAX_VALUE"), Primitive::fromDouble(1.7976931348623158e+308));
#ifdef __INTEL_COMPILER
# pragma warning( push )
# pragma warning(disable: 239)
#endif
ctor->defineReadonlyProperty(QStringLiteral("MIN_VALUE"), Primitive::fromDouble(5e-324));
#ifdef __INTEL_COMPILER
# pragma warning( pop )
#endif
defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
defineDefaultProperty(engine->id_toString, method_toString);
defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString);
defineDefaultProperty(engine->id_valueOf, method_valueOf);
defineDefaultProperty(QStringLiteral("toFixed"), method_toFixed, 1);
defineDefaultProperty(QStringLiteral("toExponential"), method_toExponential);
defineDefaultProperty(QStringLiteral("toPrecision"), method_toPrecision);
}
inline ReturnedValue thisNumberValue(ExecutionContext *ctx)
{
if (ctx->thisObject().isNumber())
return ctx->thisObject().asReturnedValue();
NumberObject *n = ctx->thisObject().as<NumberObject>();
if (!n)
return ctx->engine()->throwTypeError();
return Encode(n->value());
}
inline double thisNumber(ExecutionContext *ctx)
{
if (ctx->thisObject().isNumber())
return ctx->thisObject().asDouble();
NumberObject *n = ctx->thisObject().as<NumberObject>();
if (!n)
return ctx->engine()->throwTypeError();
return n->value();
}
ReturnedValue NumberPrototype::method_toString(CallContext *ctx)
{
Scope scope(ctx);
double num = thisNumber(ctx);
if (scope.engine->hasException)
return Encode::undefined();
if (ctx->argc() && !ctx->args()[0].isUndefined()) {
int radix = ctx->args()[0].toInt32();
if (radix < 2 || radix > 36)
return ctx->engine()->throwError(QString::fromLatin1("Number.prototype.toString: %0 is not a valid radix")
.arg(radix));
if (std::isnan(num)) {
return scope.engine->newString(QStringLiteral("NaN"))->asReturnedValue();
} else if (qIsInf(num)) {
return scope.engine->newString(QLatin1String(num < 0 ? "-Infinity" : "Infinity"))->asReturnedValue();
}
if (radix != 10) {
QString str;
bool negative = false;
if (num < 0) {
negative = true;
num = -num;
}
double frac = num - std::floor(num);
num = Primitive::toInteger(num);
do {
char c = (char)std::fmod(num, radix);
c = (c < 10) ? (c + '0') : (c - 10 + 'a');
str.prepend(QLatin1Char(c));
num = std::floor(num / radix);
} while (num != 0);
if (frac != 0) {
str.append(QLatin1Char('.'));
do {
frac = frac * radix;
char c = (char)std::floor(frac);
c = (c < 10) ? (c + '0') : (c - 10 + 'a');
str.append(QLatin1Char(c));
frac = frac - std::floor(frac);
} while (frac != 0);
}
if (negative)
str.prepend(QLatin1Char('-'));
return scope.engine->newString(str)->asReturnedValue();
}
}
return Primitive::fromDouble(num).toString(scope.engine)->asReturnedValue();
}
ReturnedValue NumberPrototype::method_toLocaleString(CallContext *ctx)
{
Scope scope(ctx);
ScopedValue v(scope, thisNumberValue(ctx));
ScopedString str(scope, v->toString(scope.engine));
if (scope.engine->hasException)
return Encode::undefined();
return str.asReturnedValue();
}
ReturnedValue NumberPrototype::method_valueOf(CallContext *ctx)
{
return thisNumberValue(ctx);
}
ReturnedValue NumberPrototype::method_toFixed(CallContext *ctx)
{
Scope scope(ctx);
double v = thisNumber(ctx);
if (scope.engine->hasException)
return Encode::undefined();
double fdigits = 0;
if (ctx->argc() > 0)
fdigits = ctx->args()[0].toInteger();
if (std::isnan(fdigits))
fdigits = 0;
if (fdigits < 0 || fdigits > 20)
return ctx->engine()->throwRangeError(ctx->thisObject());
QString str;
if (std::isnan(v))
str = QString::fromLatin1("NaN");
else if (qIsInf(v))
str = QString::fromLatin1(v < 0 ? "-Infinity" : "Infinity");
else if (v < 1.e21) {
char buf[100];
double_conversion::StringBuilder builder(buf, sizeof(buf));
double_conversion::DoubleToStringConverter::EcmaScriptConverter().ToFixed(v, fdigits, &builder);
str = QString::fromLatin1(builder.Finalize());
// At some point, the 3rd party double-conversion code should be moved to qtcore.
// When that's done, we can use:
// str = QString::number(v, 'f', int (fdigits));
} else
return RuntimeHelpers::stringFromNumber(ctx->engine(), v)->asReturnedValue();
return scope.engine->newString(str)->asReturnedValue();
}
ReturnedValue NumberPrototype::method_toExponential(CallContext *ctx)
{
Scope scope(ctx);
double d = thisNumber(ctx);
if (scope.engine->hasException)
return Encode::undefined();
int fdigits = -1;
if (ctx->argc() && !ctx->args()[0].isUndefined()) {
fdigits = ctx->args()[0].toInt32();
if (fdigits < 0 || fdigits > 20) {
ScopedString error(scope, scope.engine->newString(QStringLiteral("Number.prototype.toExponential: fractionDigits out of range")));
return ctx->engine()->throwRangeError(error);
}
}
char str[100];
double_conversion::StringBuilder builder(str, sizeof(str));
double_conversion::DoubleToStringConverter::EcmaScriptConverter().ToExponential(d, fdigits, &builder);
QString result = QString::fromLatin1(builder.Finalize());
return scope.engine->newString(result)->asReturnedValue();
}
ReturnedValue NumberPrototype::method_toPrecision(CallContext *ctx)
{
Scope scope(ctx);
ScopedValue v(scope, thisNumberValue(ctx));
if (scope.engine->hasException)
return Encode::undefined();
if (!ctx->argc() || ctx->args()[0].isUndefined())
return RuntimeHelpers::toString(scope.engine, v);
double precision = ctx->args()[0].toInt32();
if (precision < 1 || precision > 21) {
ScopedString error(scope, scope.engine->newString(QStringLiteral("Number.prototype.toPrecision: precision out of range")));
return ctx->engine()->throwRangeError(error);
}
char str[100];
double_conversion::StringBuilder builder(str, sizeof(str));
double_conversion::DoubleToStringConverter::EcmaScriptConverter().ToPrecision(v->asDouble(), precision, &builder);
QString result = QString::fromLatin1(builder.Finalize());
return scope.engine->newString(result)->asReturnedValue();
}