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.

This commit is contained in:
Brian Smith 2023-07-13 02:40:25 -05:00 • committed by roytam1
commit 13ac0d6483
15 changed files with 338 additions and 9 deletions

View file

@ -1100,6 +1100,7 @@ enum class ESClass {
SetIterator,
Arguments,
Error,
BigInt,
/** None of the above. */
Other

184
js/src/builtin/BigInt.cpp Normal file
View file

@ -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
};

51
js/src/builtin/BigInt.h Normal file
View file

@ -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

View file

@ -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.

View file

@ -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;

View file

@ -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);
}
/*

View file

@ -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;

View file

@ -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)) \

View file

@ -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;

View file

@ -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',

View file

@ -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);

View file

@ -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

View file

@ -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]") \

View file

@ -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"

View file

@ -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);