import { ObjectValue, Value, NumberValue, type Arguments, type FunctionCallContext, } from '../value.mts'; import { Q, X, type ValueCompletion, type ValueEvaluator, } from '../completion.mts'; import type { Mutable } from '../utils/language.mts'; import { bootstrapPrototype } from './bootstrap.mts'; import type { NumberObject } from './Number.mts'; import { Assert, ToIntegerOrInfinity, ToString, F, R, Realm, Throw, } from '#self'; function thisNumberValue(value: Value) { if (value instanceof NumberValue) { return value; } if (value instanceof ObjectValue && 'NumberData' in value) { const n = value.NumberData; Assert(n instanceof NumberValue); return n; } return Throw.TypeError('$1 is not a $2 object', value, 'Number'); } /** https://tc39.es/ecma262/#sec-number.prototype.toexponential */ function* NumberProto_toExponential([fractionDigits = Value.undefined]: Arguments, { thisValue }: FunctionCallContext): ValueEvaluator { const x = Q(thisNumberValue(thisValue)); const f = Q(yield* ToIntegerOrInfinity(fractionDigits)); Assert(fractionDigits !== Value.undefined || f === 0); if (!x.isFinite()) { return NumberValue.toString(x, 10); } if (f < 0 || f > 100) { return Throw.RangeError('Invalid format range for $1', 'toExponential'); } return Value(R(x).toExponential(fractionDigits === Value.undefined ? undefined : f)); } /** https://tc39.es/ecma262/#sec-number.prototype.tofixed */ function* NumberProto_toFixed([fractionDigits = Value.undefined]: Arguments, { thisValue }: FunctionCallContext): ValueEvaluator { const x = Q(thisNumberValue(thisValue)); const f = Q(yield* ToIntegerOrInfinity(fractionDigits)); Assert(fractionDigits !== Value.undefined || f === 0); if (f < 0 || f > 100) { return Throw.RangeError('Invalid format range for $1', 'toFixed'); } if (!x.isFinite()) { return X(NumberValue.toString(x, 10)); } return Value(R(x).toFixed(f)); } /** https://tc39.es/ecma262/#sec-number.prototype.tolocalestring */ function NumberProto_toLocaleString(_args: Arguments, context: FunctionCallContext): ValueEvaluator { return NumberProto_toString([], context); } /** https://tc39.es/ecma262/#sec-number.prototype.toprecision */ function* NumberProto_toPrecision([precision = Value.undefined]: Arguments, { thisValue }: FunctionCallContext): ValueEvaluator { const x = Q(thisNumberValue(thisValue)); if (precision === Value.undefined) { return X(ToString(x)); } const p = Q(yield* ToIntegerOrInfinity(precision)); if (!x.isFinite()) { return X(NumberValue.toString(x, 10)); } if (p < 1 || p > 100) { return Throw.RangeError('Invalid format range for $1', 'toPrecision'); } return Value(R(x).toPrecision(p)); } /** https://tc39.es/ecma262/#sec-number.prototype.tostring */ function* NumberProto_toString([radix = Value.undefined]: Arguments, { thisValue }: FunctionCallContext): ValueEvaluator { const x = Q(thisNumberValue(thisValue)); let radixNumber; if (radix === Value.undefined) { radixNumber = 10; } else { radixNumber = Q(yield* ToIntegerOrInfinity(radix)); } if (radixNumber < 2 || radixNumber > 36) { return Throw.RangeError('Invalid format range for $1', 'toString'); } if (radixNumber === 10) { return X(ToString(x)); } // FIXME(devsnek): Return the String representation of this Number // value using the radix specified by radixNumber. Letters a-z are // used for digits with values 10 through 35. The precise algorithm // is implementation-dependent, however the algorithm should be a // generalization of that specified in 7.1.12.1. return Value(R(x).toString(radixNumber)); } /** https://tc39.es/ecma262/#sec-number.prototype.valueof */ function NumberProto_valueOf(_args: Arguments, { thisValue }: FunctionCallContext): ValueCompletion { return Q(thisNumberValue(thisValue)); } export function bootstrapNumberPrototype(realmRec: Realm) { const proto = bootstrapPrototype(realmRec, [ ['toExponential', NumberProto_toExponential, 1], ['toFixed', NumberProto_toFixed, 1], ['toLocaleString', NumberProto_toLocaleString, 0], ['toPrecision', NumberProto_toPrecision, 1], ['toString', NumberProto_toString, 1], ['valueOf', NumberProto_valueOf, 0], ], realmRec.Intrinsics['%Object.prototype%']); (proto as Mutable).NumberData = F(+0); realmRec.Intrinsics['%Number.prototype%'] = proto; }