summaryrefslogtreecommitdiff
path: root/js
diff options
context:
space:
mode:
authorBrian Smith <brian@dbsoft.org>2023-07-13 02:40:25 -0500
committerBrian Smith <brian@dbsoft.org>2023-07-13 02:40:25 -0500
commitb2ae4d388529c1a63959cc169f3ec1f7fdce9558 (patch)
tree83f80a93cb61d769c05e41e3ec59045fa2659f41 /js
parenteb7a856c766a508cde0a9ad90a233b272c7a9e54 (diff)
downloaduxp-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.h1
-rw-r--r--js/src/builtin/BigInt.cpp184
-rw-r--r--js/src/builtin/BigInt.h51
-rw-r--r--js/src/builtin/Object.cpp5
-rw-r--r--js/src/jsfriendapi.cpp4
-rw-r--r--js/src/jsobj.cpp4
-rw-r--r--js/src/json.cpp5
-rw-r--r--js/src/jsprototypes.h1
-rw-r--r--js/src/jsstr.cpp2
-rw-r--r--js/src/moz.build1
-rw-r--r--js/src/vm/BigIntType.cpp74
-rw-r--r--js/src/vm/BigIntType.h11
-rw-r--r--js/src/vm/CommonPropertyNames.h1
-rw-r--r--js/src/vm/GlobalObject.cpp1
-rw-r--r--js/src/vm/StringBuffer.cpp2
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);