BigInt.cpp (7519B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "builtin/BigInt.h" 8 9 #if JS_HAS_INTL_API 10 # include "builtin/intl/GlobalIntlData.h" 11 # include "builtin/intl/NumberFormat.h" 12 #endif 13 #include "jit/InlinableNatives.h" 14 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* 15 #include "js/PropertySpec.h" 16 #include "vm/BigIntType.h" 17 18 #include "vm/GeckoProfiler-inl.h" 19 #include "vm/JSObject-inl.h" 20 21 using namespace js; 22 23 static MOZ_ALWAYS_INLINE bool IsBigInt(HandleValue v) { 24 return v.isBigInt() || (v.isObject() && v.toObject().is<BigIntObject>()); 25 } 26 27 // BigInt proposal section 5.1.3 28 static bool BigIntConstructor(JSContext* cx, unsigned argc, Value* vp) { 29 AutoJSConstructorProfilerEntry pseudoFrame(cx, "BigInt"); 30 CallArgs args = CallArgsFromVp(argc, vp); 31 32 // Step 1. 33 if (args.isConstructing()) { 34 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 35 JSMSG_NOT_CONSTRUCTOR, "BigInt"); 36 return false; 37 } 38 39 // Step 2. 40 RootedValue v(cx, args.get(0)); 41 if (!ToPrimitive(cx, JSTYPE_NUMBER, &v)) { 42 return false; 43 } 44 45 // Steps 3-4. 46 BigInt* bi; 47 if (!v.isNumber()) { 48 bi = ToBigInt(cx, v); 49 } else if (v.isInt32()) { 50 bi = BigInt::createFromInt64(cx, int64_t(v.toInt32())); 51 } else { 52 bi = NumberToBigInt(cx, v.toDouble()); 53 } 54 if (!bi) { 55 return false; 56 } 57 58 args.rval().setBigInt(bi); 59 return true; 60 } 61 62 JSObject* BigIntObject::create(JSContext* cx, HandleBigInt bigInt) { 63 BigIntObject* bn = NewBuiltinClassInstance<BigIntObject>(cx); 64 if (!bn) { 65 return nullptr; 66 } 67 bn->initFixedSlot(PRIMITIVE_VALUE_SLOT, BigIntValue(bigInt)); 68 return bn; 69 } 70 71 BigInt* BigIntObject::unbox() const { 72 return getFixedSlot(PRIMITIVE_VALUE_SLOT).toBigInt(); 73 } 74 75 static BigInt* ThisBigIntValue(const CallArgs& args) { 76 HandleValue thisv = args.thisv(); 77 MOZ_ASSERT(IsBigInt(thisv)); 78 79 return thisv.isBigInt() ? thisv.toBigInt() 80 : thisv.toObject().as<BigIntObject>().unbox(); 81 } 82 83 /** 84 * BigInt.prototype.valueOf ( ) 85 * 86 * ES2025 draft rev e42d11da7753bd933b1e7a5f3cb657ab0a8f6251 87 */ 88 bool BigIntObject::valueOf_impl(JSContext* cx, const CallArgs& args) { 89 // Step 1. 90 args.rval().setBigInt(ThisBigIntValue(args)); 91 return true; 92 } 93 94 /** 95 * BigInt.prototype.valueOf ( ) 96 * 97 * ES2025 draft rev e42d11da7753bd933b1e7a5f3cb657ab0a8f6251 98 */ 99 bool BigIntObject::valueOf(JSContext* cx, unsigned argc, Value* vp) { 100 CallArgs args = CallArgsFromVp(argc, vp); 101 return CallNonGenericMethod<IsBigInt, valueOf_impl>(cx, args); 102 } 103 104 /** 105 * BigInt.prototype.toString ( [ radix ] ) 106 * 107 * ES2025 draft rev e42d11da7753bd933b1e7a5f3cb657ab0a8f6251 108 */ 109 bool BigIntObject::toString_impl(JSContext* cx, const CallArgs& args) { 110 // Step 1. 111 RootedBigInt bi(cx, ThisBigIntValue(args)); 112 113 // Step 2. 114 uint8_t radix = 10; 115 116 // Steps 3-4. 117 if (args.hasDefined(0)) { 118 double d; 119 if (!ToInteger(cx, args[0], &d)) { 120 return false; 121 } 122 if (d < 2 || d > 36) { 123 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_RADIX); 124 return false; 125 } 126 radix = d; 127 } 128 129 // Step 5. 130 JSLinearString* str = BigInt::toString<CanGC>(cx, bi, radix); 131 if (!str) { 132 return false; 133 } 134 args.rval().setString(str); 135 return true; 136 } 137 138 bool BigIntObject::toString(JSContext* cx, unsigned argc, Value* vp) { 139 AutoJSMethodProfilerEntry pseudoFrame(cx, "BigInt.prototype", "toString"); 140 CallArgs args = CallArgsFromVp(argc, vp); 141 return CallNonGenericMethod<IsBigInt, toString_impl>(cx, args); 142 } 143 144 /** 145 * BigInt.prototype.toLocaleString ( [ reserved1 [ , reserved2 ] ] ) 146 * 147 * ES2025 draft rev e42d11da7753bd933b1e7a5f3cb657ab0a8f6251 148 * 149 * BigInt.prototype.toLocaleString ( [ locales [ , options ] ] ) 150 * 151 * ES2025 Intl draft rev 6827e6e40b45fb313472595be31352451a2d85fa 152 */ 153 bool BigIntObject::toLocaleString_impl(JSContext* cx, const CallArgs& args) { 154 // Step 1. 155 RootedBigInt bi(cx, ThisBigIntValue(args)); 156 157 #if JS_HAS_INTL_API 158 HandleValue locales = args.get(0); 159 HandleValue options = args.get(1); 160 161 // Step 2. 162 Rooted<NumberFormatObject*> numberFormat( 163 cx, intl::GetOrCreateNumberFormat(cx, locales, options)); 164 if (!numberFormat) { 165 return false; 166 } 167 168 // Step 3. 169 JSString* str = intl::FormatBigInt(cx, numberFormat, bi); 170 if (!str) { 171 return false; 172 } 173 args.rval().setString(str); 174 return true; 175 #else 176 // This method is implementation-defined, and it is permissible, but not 177 // encouraged, for it to return the same thing as toString. 178 JSString* str = BigInt::toString<CanGC>(cx, bi, 10); 179 if (!str) { 180 return false; 181 } 182 args.rval().setString(str); 183 return true; 184 #endif 185 } 186 187 bool BigIntObject::toLocaleString(JSContext* cx, unsigned argc, Value* vp) { 188 AutoJSMethodProfilerEntry pseudoFrame(cx, "BigInt.prototype", 189 "toLocaleString"); 190 CallArgs args = CallArgsFromVp(argc, vp); 191 return CallNonGenericMethod<IsBigInt, toLocaleString_impl>(cx, args); 192 } 193 194 // BigInt proposal section 5.2.1. BigInt.asUintN ( bits, bigint ) 195 bool BigIntObject::asUintN(JSContext* cx, unsigned argc, Value* vp) { 196 CallArgs args = CallArgsFromVp(argc, vp); 197 198 // Step 1. 199 uint64_t bits; 200 if (!ToIndex(cx, args.get(0), &bits)) { 201 return false; 202 } 203 204 // Step 2. 205 RootedBigInt bi(cx, ToBigInt(cx, args.get(1))); 206 if (!bi) { 207 return false; 208 } 209 210 // Step 3. 211 BigInt* res = BigInt::asUintN(cx, bi, bits); 212 if (!res) { 213 return false; 214 } 215 216 args.rval().setBigInt(res); 217 return true; 218 } 219 220 // BigInt proposal section 5.2.2. BigInt.asIntN ( bits, bigint ) 221 bool BigIntObject::asIntN(JSContext* cx, unsigned argc, Value* vp) { 222 CallArgs args = CallArgsFromVp(argc, vp); 223 224 // Step 1. 225 uint64_t bits; 226 if (!ToIndex(cx, args.get(0), &bits)) { 227 return false; 228 } 229 230 // Step 2. 231 RootedBigInt bi(cx, ToBigInt(cx, args.get(1))); 232 if (!bi) { 233 return false; 234 } 235 236 // Step 3. 237 BigInt* res = BigInt::asIntN(cx, bi, bits); 238 if (!res) { 239 return false; 240 } 241 242 args.rval().setBigInt(res); 243 return true; 244 } 245 246 const ClassSpec BigIntObject::classSpec_ = { 247 GenericCreateConstructor<BigIntConstructor, 1, gc::AllocKind::FUNCTION, 248 &jit::JitInfo_BigInt>, 249 GenericCreatePrototype<BigIntObject>, 250 BigIntObject::staticMethods, 251 nullptr, 252 BigIntObject::methods, 253 BigIntObject::properties, 254 }; 255 256 const JSClass BigIntObject::class_ = { 257 "BigInt", 258 JSCLASS_HAS_CACHED_PROTO(JSProto_BigInt) | 259 JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS), 260 JS_NULL_CLASS_OPS, 261 &BigIntObject::classSpec_, 262 }; 263 264 const JSClass BigIntObject::protoClass_ = { 265 "BigInt.prototype", 266 JSCLASS_HAS_CACHED_PROTO(JSProto_BigInt), 267 JS_NULL_CLASS_OPS, 268 &BigIntObject::classSpec_, 269 }; 270 271 const JSPropertySpec BigIntObject::properties[] = { 272 // BigInt proposal section 5.3.5 273 JS_STRING_SYM_PS(toStringTag, "BigInt", JSPROP_READONLY), 274 JS_PS_END, 275 }; 276 277 const JSFunctionSpec BigIntObject::methods[] = { 278 JS_FN("valueOf", valueOf, 0, 0), 279 JS_FN("toString", toString, 0, 0), 280 JS_FN("toLocaleString", toLocaleString, 0, 0), 281 JS_FS_END, 282 }; 283 284 const JSFunctionSpec BigIntObject::staticMethods[] = { 285 JS_INLINABLE_FN("asUintN", asUintN, 2, 0, BigIntAsUintN), 286 JS_INLINABLE_FN("asIntN", asIntN, 2, 0, BigIntAsIntN), 287 JS_FS_END, 288 };