1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 04:17:35 +00:00

LibJS: Add Number.prototype.toString

This commit is contained in:
Matthew Olsson 2020-07-14 15:26:15 -07:00 committed by Andreas Kling
parent ec3737510d
commit 02305d01ea
6 changed files with 189 additions and 4 deletions

View file

@ -45,6 +45,7 @@
M(InstanceOfOperatorBadPrototype, "'prototype' property of %s is not an object") \
M(InvalidAssignToConst, "Invalid assignment to const variable") \
M(InvalidLeftHandAssignment, "Invalid left-hand side in assignment") \
M(InvalidRadix, "Radix must be an integer no less than 2, and no greater than 36") \
M(IsNotA, "%s is not a %s") \
M(IsNotAEvaluatedFrom, "%s is not a %s (evaluated from '%s')") \
M(IterableNextBadReturn, "iterator.next() returned a non-object value") \
@ -62,6 +63,7 @@
M(NotASymbol, "%s is not a symbol") \
M(NotIterable, "%s is not iterable") \
M(NonExtensibleDefine, "Cannot define property %s on non-extensible object") \
M(NumberIncompatibleThis, "Number.prototype.%s method called with incompatible this target") \
M(ObjectDefinePropertyReturnedFalse, "Object's [[DefineProperty]] method returned false") \
M(ObjectSetPrototypeOfReturnedFalse, "Object's [[SetPrototypeOf]] method returned false") \
M(ObjectSetPrototypeOfTwoArgs, "Object.setPrototypeOf requires at least two arguments") \

View file

@ -42,6 +42,8 @@ public:
virtual bool is_number_object() const override { return true; }
virtual Value value_of() const override { return Value(m_value); }
double number() const { return m_value; }
private:
double m_value { 0 };
};

View file

@ -25,18 +25,117 @@
*/
#include <LibJS/Interpreter.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/NumberObject.h>
#include <LibJS/Runtime/NumberPrototype.h>
namespace JS {
static const u8 max_precision_for_radix[37] = {
0, 0, 52, 32, 26, 22, 20, 18, 17, 16,
15, 15, 14, 14, 13, 13, 13, 12, 12, 12,
12, 11, 11, 11, 11, 11, 11, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10,
};
static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
NumberPrototype::NumberPrototype(GlobalObject& global_object)
: NumberObject(0, *global_object.object_prototype())
{
}
void NumberPrototype::initialize(Interpreter& interpreter, GlobalObject& object)
{
Object::initialize(interpreter, object);
define_native_function("toString", to_string, 1, Attribute::Configurable | Attribute::Writable);
}
NumberPrototype::~NumberPrototype()
{
}
JS_DEFINE_NATIVE_FUNCTION(NumberPrototype::to_string)
{
Value number_value;
auto this_value = interpreter.this_value(global_object);
if (this_value.is_number()) {
number_value = this_value;
} else if (this_value.is_object() && this_value.as_object().is_number_object()) {
number_value = static_cast<NumberObject&>(this_value.as_object()).value_of();
} else {
return interpreter.throw_exception<TypeError>(ErrorType::NumberIncompatibleThis, "toString");
}
int radix;
auto argument = interpreter.argument(0);
if (argument.is_undefined()) {
radix = 10;
} else {
radix = argument.to_i32(interpreter);
}
if (interpreter.exception() || radix < 2 || radix > 36)
return interpreter.throw_exception<RangeError>(ErrorType::InvalidRadix);
if (number_value.is_positive_infinity())
return js_string(interpreter, "Infinity");
if (number_value.is_negative_infinity())
return js_string(interpreter, "-Infinity");
if (number_value.is_nan())
return js_string(interpreter, "NaN");
if (number_value.is_positive_zero() || number_value.is_negative_zero())
return js_string(interpreter, "0");
double number = number_value.as_double();
bool negative = number < 0;
if (negative)
number *= -1;
int int_part = floor(number);
double decimal_part = number - int_part;
Vector<char> backwards_characters;
if (int_part == 0) {
backwards_characters.append('0');
} else {
while (int_part > 0) {
backwards_characters.append(digits[int_part % radix]);
int_part /= radix;
}
}
Vector<char> characters;
if (negative)
characters.append('-');
// Reverse characters;
for (ssize_t i = backwards_characters.size() - 1; i >= 0; --i) {
characters.append(backwards_characters[i]);
}
// decimal part
if (decimal_part != 0.0) {
characters.append('.');
int precision = max_precision_for_radix[radix];
for (int i = 0; i < precision; ++i) {
decimal_part *= radix;
int integral = floor(decimal_part);
characters.append(digits[integral]);
decimal_part -= integral;
}
while (characters.last() == '0')
characters.take_last();
}
return js_string(interpreter, String(characters.data(), characters.size()));
}
}

View file

@ -35,7 +35,10 @@ class NumberPrototype final : public NumberObject {
public:
explicit NumberPrototype(GlobalObject&);
virtual void initialize(Interpreter&, GlobalObject&) override;
virtual ~NumberPrototype() override;
JS_DECLARE_NATIVE_FUNCTION(to_string);
};
}