diff options
author | Brian Smith <brian@dbsoft.org> | 2023-07-13 02:40:25 -0500 |
---|---|---|
committer | Brian Smith <brian@dbsoft.org> | 2023-07-13 02:40:25 -0500 |
commit | b2ae4d388529c1a63959cc169f3ec1f7fdce9558 (patch) | |
tree | 83f80a93cb61d769c05e41e3ec59045fa2659f41 /js | |
parent | eb7a856c766a508cde0a9ad90a233b272c7a9e54 (diff) | |
download | uxp-b2ae4d388529c1a63959cc169f3ec1f7fdce9558.tar.gz |
Issue #1240 - Part 2 - Define the BigIntObject class for BigInt wrapper objects.
Based on https://bugzilla.mozilla.org/show_bug.cgi?id=1366287 Part 3.
In our Part 3 we will fast forward to the V8 implementation skipping GMP.
Diffstat (limited to 'js')
-rw-r--r-- | js/public/Class.h | 1 | ||||
-rw-r--r-- | js/src/builtin/BigInt.cpp | 184 | ||||
-rw-r--r-- | js/src/builtin/BigInt.h | 51 | ||||
-rw-r--r-- | js/src/builtin/Object.cpp | 5 | ||||
-rw-r--r-- | js/src/jsfriendapi.cpp | 4 | ||||
-rw-r--r-- | js/src/jsobj.cpp | 4 | ||||
-rw-r--r-- | js/src/json.cpp | 5 | ||||
-rw-r--r-- | js/src/jsprototypes.h | 1 | ||||
-rw-r--r-- | js/src/jsstr.cpp | 2 | ||||
-rw-r--r-- | js/src/moz.build | 1 | ||||
-rw-r--r-- | js/src/vm/BigIntType.cpp | 74 | ||||
-rw-r--r-- | js/src/vm/BigIntType.h | 11 | ||||
-rw-r--r-- | js/src/vm/CommonPropertyNames.h | 1 | ||||
-rw-r--r-- | js/src/vm/GlobalObject.cpp | 1 | ||||
-rw-r--r-- | js/src/vm/StringBuffer.cpp | 2 |
15 files changed, 338 insertions, 9 deletions
diff --git a/js/public/Class.h b/js/public/Class.h index f1d7739718..8aeacdc541 100644 --- a/js/public/Class.h +++ b/js/public/Class.h @@ -1100,6 +1100,7 @@ enum class ESClass { SetIterator, Arguments, Error, + BigInt, /** None of the above. */ Other diff --git a/js/src/builtin/BigInt.cpp b/js/src/builtin/BigInt.cpp new file mode 100644 index 0000000000..2790a20ccc --- /dev/null +++ b/js/src/builtin/BigInt.cpp @@ -0,0 +1,184 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "builtin/BigInt.h" + +#include "jsapi.h" + +#include "builtin/TypedObject.h" +#include "gc/Tracer.h" +#include "js/TracingAPI.h" +#include "vm/ArrayBufferObject.h" +#include "vm/BigIntType.h" +#include "vm/SelfHosting.h" +#include "vm/TaggedProto.h" + +#include "vm/NativeObject-inl.h" + +using namespace js; + +static MOZ_ALWAYS_INLINE bool +IsBigInt(HandleValue v) +{ + return v.isBigInt() || (v.isObject() && v.toObject().is<BigIntObject>()); +} + +static JSObject* +CreateBigIntPrototype(JSContext* cx, JSProtoKey key) +{ + return GlobalObject::createBlankPrototype<PlainObject>(cx, cx->global()); +} + +// BigInt proposal section 5.1.3 +static bool +BigIntConstructor(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + // Step 1. + if (args.isConstructing()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_CONSTRUCTOR, "BigInt"); + return false; + } + + // Step 2. + RootedValue v(cx, args.get(0)); + if (!ToPrimitive(cx, JSTYPE_NUMBER, &v)) + return false; + + // Steps 3-4. + BigInt* bi = v.isNumber() + ? NumberToBigInt(cx, v.toNumber()) + : ToBigInt(cx, v); + if (!bi) + return false; + + args.rval().setBigInt(bi); + return true; +} + +JSObject* +BigIntObject::create(JSContext* cx, HandleBigInt bigInt) +{ + RootedObject obj(cx, NewBuiltinClassInstance(cx, &class_)); + if (!obj) + return nullptr; + BigIntObject& bn = obj->as<BigIntObject>(); + bn.setFixedSlot(PRIMITIVE_VALUE_SLOT, BigIntValue(bigInt)); + return &bn; +} + +BigInt* +BigIntObject::unbox() const +{ + return getFixedSlot(PRIMITIVE_VALUE_SLOT).toBigInt(); +} + +bool +js::intrinsic_ToBigInt(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + + BigInt* result = ToBigInt(cx, args[0]); + if (!result) + return false; + + args.rval().setBigInt(result); + return true; +} + +// BigInt proposal section 5.3.4 +bool +BigIntObject::valueOf_impl(JSContext* cx, const CallArgs& args) +{ + // Step 1. + HandleValue thisv = args.thisv(); + MOZ_ASSERT(IsBigInt(thisv)); + RootedBigInt bi(cx, thisv.isBigInt() + ? thisv.toBigInt() + : thisv.toObject().as<BigIntObject>().unbox()); + + args.rval().setBigInt(bi); + return true; +} + +bool +BigIntObject::valueOf(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return CallNonGenericMethod<IsBigInt, valueOf_impl>(cx, args); +} + +// BigInt proposal section 5.3.3 +bool +BigIntObject::toString_impl(JSContext* cx, const CallArgs& args) +{ + // Step 1. + HandleValue thisv = args.thisv(); + MOZ_ASSERT(IsBigInt(thisv)); + RootedBigInt bi(cx, thisv.isBigInt() + ? thisv.toBigInt() + : thisv.toObject().as<BigIntObject>().unbox()); + + // Steps 2-3. + uint8_t radix = 10; + + // Steps 4-5. + if (args.hasDefined(0)) { + double d; + if (!ToInteger(cx, args[0], &d)) + return false; + if (d < 2 || d > 36) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_RADIX); + return false; + } + radix = d; + } + + // Steps 6-7. + JSLinearString* str = BigInt::toString(cx, bi, radix); + if (!str) + return false; + args.rval().setString(str); + return true; +} + +bool +BigIntObject::toString(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return CallNonGenericMethod<IsBigInt, toString_impl>(cx, args); +} + +const ClassSpec BigIntObject::classSpec_ = { + GenericCreateConstructor<BigIntConstructor, 1, gc::AllocKind::FUNCTION>, + CreateBigIntPrototype, + nullptr, + nullptr, + BigIntObject::methods, + BigIntObject::properties +}; + +const Class BigIntObject::class_ = { + "BigInt", + JSCLASS_HAS_CACHED_PROTO(JSProto_BigInt) | + JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS), + JS_NULL_CLASS_OPS, + &BigIntObject::classSpec_ +}; + +const JSPropertySpec BigIntObject::properties[] = { + // BigInt proposal section 5.3.5 + JS_STRING_SYM_PS(toStringTag, "BigInt", JSPROP_READONLY), + JS_PS_END +}; + +const JSFunctionSpec BigIntObject::methods[] = { + JS_FN("valueOf", valueOf, 0, 0), + JS_FN("toString", toString, 0, 0), + JS_FS_END +}; diff --git a/js/src/builtin/BigInt.h b/js/src/builtin/BigInt.h new file mode 100644 index 0000000000..75bf99867e --- /dev/null +++ b/js/src/builtin/BigInt.h @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef builtin_BigInt_h +#define builtin_BigInt_h + +#include "js/Class.h" +#include "js/RootingAPI.h" +#include "vm/BigIntType.h" +#include "vm/NativeObject.h" + +namespace js { + +class GlobalObject; + +class BigIntObject : public NativeObject +{ + static const unsigned PRIMITIVE_VALUE_SLOT = 0; + static const unsigned RESERVED_SLOTS = 1; + + public: + static const ClassSpec classSpec_; + static const Class class_; + + static JSObject* create(JSContext* cx, JS::Handle<JS::BigInt*> bi); + + // Methods defined on BigInt.prototype. + static bool valueOf_impl(JSContext* cx, const CallArgs& args); + static bool valueOf(JSContext* cx, unsigned argc, JS::Value* vp); + static bool toString_impl(JSContext* cx, const CallArgs& args); + static bool toString(JSContext* cx, unsigned argc, JS::Value* vp); + + JS::BigInt* unbox() const; + + private: + static const JSPropertySpec properties[]; + static const JSFunctionSpec methods[]; +}; + +extern JSObject* +InitBigIntClass(JSContext* cx, Handle<GlobalObject*> global); + +extern bool +intrinsic_ToBigInt(JSContext* cx, unsigned argc, JS::Value* vp); + +} // namespace js + +#endif diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index 5221afb617..388af28f26 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -7,6 +7,8 @@ #include "mozilla/ArrayUtils.h" +#include "builtin/BigInt.h" + #include "jscntxt.h" #include "jsstr.h" @@ -471,6 +473,9 @@ js::obj_toString(JSContext* cx, unsigned argc, Value* vp) case ESClass::RegExp: builtinTag = cx->names().objectRegExp; break; + case ESClass::BigInt: + builtinTag = cx->names().objectBigInt; + break; default: if (obj->isCallable()) { // Non-standard: Prevent <object> from showing up as Function. diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 0cd57ab52b..f73cc5fd65 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -9,6 +9,8 @@ #include <stdint.h> +#include "builtin/BigInt.h" + #include "jscntxt.h" #include "jscompartment.h" #include "jsgc.h" @@ -299,6 +301,8 @@ js::GetBuiltinClass(JSContext* cx, HandleObject obj, ESClass* cls) *cls = ESClass::Arguments; else if (obj->is<ErrorObject>()) *cls = ESClass::Error; + else if (obj->is<BigIntObject>()) + *cls = ESClass::BigInt; else *cls = ESClass::Other; diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 75d45e76d5..d0f9430bc8 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -35,6 +35,7 @@ #include "jswin.h" #include "jswrapper.h" +#include "builtin/BigInt.h" #include "builtin/Eval.h" #include "builtin/Object.h" #include "builtin/SymbolObject.h" @@ -3110,8 +3111,7 @@ js::PrimitiveToObject(JSContext* cx, const Value& v) } MOZ_ASSERT(v.isBigInt()); RootedBigInt bigInt(cx, v.toBigInt()); - // Return nullptr because BigIntObject has not been defined yet. - return nullptr; + return BigIntObject::create(cx, bigInt); } /* diff --git a/js/src/json.cpp b/js/src/json.cpp index 084deb2b94..d426fc721a 100644 --- a/js/src/json.cpp +++ b/js/src/json.cpp @@ -9,6 +9,8 @@ #include "mozilla/Range.h" #include "mozilla/ScopeExit.h" +#include "builtin/BigInt.h" + #include "jsarray.h" #include "jsatom.h" #include "jscntxt.h" @@ -328,6 +330,8 @@ PreprocessValue(JSContext* cx, HandleObject holder, KeyType key, MutableHandleVa } else if (cls == ESClass::Boolean) { if (!Unbox(cx, obj, vp)) return false; + } else if (cls == ESClass::BigInt) { + vp.setBigInt(obj->as<BigIntObject>().unbox()); } } @@ -626,6 +630,7 @@ Str(JSContext* cx, const Value& v, StringifyContext* scx) return NumberValueToStringBuffer(cx, v, scx->sb); } + /* Step 10 in the BigInt proposal. */ if (v.isBigInt()) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BIGINT_NOT_SERIALIZABLE); return false; diff --git a/js/src/jsprototypes.h b/js/src/jsprototypes.h index 7de6b0245a..75168d37ef 100644 --- a/js/src/jsprototypes.h +++ b/js/src/jsprototypes.h @@ -91,6 +91,7 @@ real(Float32Array, InitViaClassSpec, TYPED_ARRAY_CLASP(Float32)) \ real(Float64Array, InitViaClassSpec, TYPED_ARRAY_CLASP(Float64)) \ real(Uint8ClampedArray, InitViaClassSpec, TYPED_ARRAY_CLASP(Uint8Clamped)) \ + real(BigInt, InitViaClassSpec, OCLASP(BigInt)) \ real(Proxy, InitProxyClass, js::ProxyClassPtr) \ real(WeakMap, InitWeakMapClass, OCLASP(WeakMap)) \ real(Map, InitMapClass, OCLASP(Map)) \ diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index bd1ca38972..3b5461b441 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -3737,7 +3737,7 @@ js::ToStringSlow(ExclusiveContext* cx, typename MaybeRooted<Value, allowGC>::Han } else if (v.isBigInt()) { if (!allowGC) return nullptr; - str = BigInt::toString(cx, v.toBigInt()); + str = BigInt::toString(cx, v.toBigInt(), 10); } else { MOZ_ASSERT(v.isUndefined()); str = cx->names().undefined; diff --git a/js/src/moz.build b/js/src/moz.build index 321fdb88a9..b75afc2628 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -384,6 +384,7 @@ main_deunified_sources = [ # instantiations may or may not be needed depending on what it gets bundled # with. SOURCES += [ + 'builtin/BigInt.cpp', 'builtin/RegExp.cpp', 'frontend/Parser.cpp', 'gc/StoreBuffer.cpp', diff --git a/js/src/vm/BigIntType.cpp b/js/src/vm/BigIntType.cpp index 394f8f8784..50f92bce49 100644 --- a/js/src/vm/BigIntType.cpp +++ b/js/src/vm/BigIntType.cpp @@ -11,6 +11,7 @@ #include "jsapi.h" #include "jscntxt.h" +#include "builtin/BigInt.h" #include "gc/Allocator.h" #include "gc/Tracer.h" #include "vm/SelfHosting.h" @@ -18,7 +19,7 @@ using namespace js; BigInt* -BigInt::create(js::ExclusiveContext* cx) +BigInt::create(ExclusiveContext* cx) { BigInt* x = Allocate<BigInt>(cx); if (!x) @@ -27,7 +28,51 @@ BigInt::create(js::ExclusiveContext* cx) } BigInt* -BigInt::copy(js::ExclusiveContext* cx, HandleBigInt x) +BigInt::create(ExclusiveContext* cx, double d) +{ + return nullptr; +} + +// BigInt proposal section 5.1.1 +static bool +IsInteger(double d) +{ + // Step 1 is an assertion checked by the caller. + // Step 2. + if (!mozilla::IsFinite(d)) + return false; + + // Step 3. + double i = JS::ToInteger(d); + + // Step 4. + if (i != d) + return false; + + // Step 5. + return true; +} + +// BigInt proposal section 5.1.2 +BigInt* +js::NumberToBigInt(ExclusiveContext* cx, double d) +{ + // Step 1 is an assertion checked by the caller. + // Step 2. + if (!IsInteger(d)) { + if(cx->isJSContext()) { + JS_ReportErrorNumberASCII(cx->asJSContext(), GetErrorMessage, nullptr, + JSMSG_NUMBER_TO_BIGINT); + } + return nullptr; + } + + // Step 3. + return BigInt::create(cx, d); +} + +BigInt* +BigInt::copy(ExclusiveContext* cx, HandleBigInt x) { BigInt* bi = create(cx); if (!bi) @@ -35,8 +80,29 @@ BigInt::copy(js::ExclusiveContext* cx, HandleBigInt x) return bi; } +// BigInt proposal section 7.3 +BigInt* +js::ToBigInt(ExclusiveContext* cx, HandleValue val) +{ + RootedValue v(cx, val); + + if(cx->isJSContext()) { + // Step 1. + if (!ToPrimitive(cx->asJSContext(), JSTYPE_NUMBER, &v)) + return nullptr; + + // Step 2. + // Boolean and string conversions are not yet supported. + if (v.isBigInt()) + return v.toBigInt(); + + JS_ReportErrorNumberASCII(cx->asJSContext(), GetErrorMessage, nullptr, JSMSG_NOT_BIGINT); + } + return nullptr; +} + JSLinearString* -BigInt::toString(ExclusiveContext* cx, BigInt* x) +BigInt::toString(ExclusiveContext* cx, BigInt* x, uint8_t radix) { return nullptr; } @@ -50,7 +116,7 @@ BigInt::finalize(js::FreeOp* fop) JSAtom* js::BigIntToAtom(ExclusiveContext* cx, BigInt* bi) { - JSString* str = BigInt::toString(cx, bi); + JSString* str = BigInt::toString(cx, bi, 10); if (!str) return nullptr; return AtomizeString(cx, str); diff --git a/js/src/vm/BigIntType.h b/js/src/vm/BigIntType.h index 3c5768e818..8d934271a3 100644 --- a/js/src/vm/BigIntType.h +++ b/js/src/vm/BigIntType.h @@ -27,6 +27,8 @@ class BigInt final : public js::gc::TenuredCell // Allocate and initialize a BigInt value static BigInt* create(js::ExclusiveContext* cx); + static BigInt* create(js::ExclusiveContext* cx, double d); + static const JS::TraceKind TraceKind = JS::TraceKind::BigInt; void traceChildren(JSTracer* trc); @@ -37,10 +39,12 @@ class BigInt final : public js::gc::TenuredCell size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; - static JSLinearString* toString(js::ExclusiveContext* cx, BigInt* x); bool toBoolean(); static BigInt* copy(js::ExclusiveContext* cx, Handle<BigInt*> x); + + static JSLinearString* toString(js::ExclusiveContext* cx, BigInt* x, uint8_t radix); + }; static_assert(sizeof(BigInt) >= js::gc::CellSize, @@ -53,6 +57,11 @@ namespace js { extern JSAtom* BigIntToAtom(ExclusiveContext* cx, JS::BigInt* bi); +extern JS::BigInt* +NumberToBigInt(ExclusiveContext* cx, double d); + +extern JS::BigInt* +ToBigInt(ExclusiveContext* cx, JS::Handle<JS::Value> v); } // namespace js #endif diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index 84582b0ac5..efbd8a5323 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -275,6 +275,7 @@ macro(objectArguments, objectArguments, "[object Arguments]") \ macro(objectArray, objectArray, "[object Array]") \ macro(objectBoolean, objectBoolean, "[object Boolean]") \ + macro(objectBigInt, objectBigInt, "[object BigInt]") \ macro(objectDate, objectDate, "[object Date]") \ macro(objectError, objectError, "[object Error]") \ macro(objectFunction, objectFunction, "[object Function]") \ diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index 542160ce56..b7d3344b3e 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -15,6 +15,7 @@ #include "jsweakmap.h" #include "builtin/AtomicsObject.h" +#include "builtin/BigInt.h" #include "builtin/Eval.h" #include "builtin/MapObject.h" #include "builtin/ModuleObject.h" diff --git a/js/src/vm/StringBuffer.cpp b/js/src/vm/StringBuffer.cpp index 38f1e74e71..58a3c3e164 100644 --- a/js/src/vm/StringBuffer.cpp +++ b/js/src/vm/StringBuffer.cpp @@ -171,7 +171,7 @@ js::ValueToStringBufferSlow(JSContext* cx, const Value& arg, StringBuffer& sb) return false; } if (v.isBigInt()) { - JSString* str = BigInt::toString(cx, v.toBigInt()); + JSLinearString* str = BigInt::toString(cx, v.toBigInt(), 10); if (!str) return false; return sb.append(str); |