Skip to content

Commit 6db3184

Browse files
authored
fix: use BigInt for pointers (#199)
1 parent 21de81d commit 6db3184

File tree

4 files changed

+67
-6
lines changed

4 files changed

+67
-6
lines changed

NativeScript/runtime/Helpers.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ inline bool IsNumber(const v8::Local<v8::Value>& value) {
157157
return !value.IsEmpty() && (value->IsNumber() || value->IsNumberObject());
158158
}
159159

160+
inline bool IsBigInt(const v8::Local<v8::Value>& value) {
161+
return !value.IsEmpty() && (value->IsBigInt() || value->IsBigIntObject());
162+
}
163+
160164
inline bool IsBool(const v8::Local<v8::Value>& value) {
161165
return !value.IsEmpty() && (value->IsBoolean() || value->IsBooleanObject());
162166
}

NativeScript/runtime/Pointer.cpp

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Local<Value> Pointer::NewInstance(Local<Context> context, void* handle) {
1919
Isolate* isolate = context->GetIsolate();
2020
intptr_t ptr = static_cast<intptr_t>(reinterpret_cast<size_t>(handle));
2121

22-
Local<Value> arg = Number::New(isolate, ptr);
22+
Local<Value> arg = BigInt::NewFromUnsigned(isolate, ptr);
2323
Local<Value> args[1] { arg };
2424
Local<Value> result;
2525
Local<v8::Function> ctorFunc = Pointer::GetPointerCtorFunc(context);
@@ -60,6 +60,7 @@ Local<v8::Function> Pointer::GetPointerCtorFunc(Local<Context> context) {
6060
Pointer::RegisterToHexStringMethod(context, prototype);
6161
Pointer::RegisterToDecimalStringMethod(context, prototype);
6262
Pointer::RegisterToNumberMethod(context, prototype);
63+
Pointer::RegisterToBigIntMethod(context, prototype);
6364

6465
cache->PointerCtorFunc = std::make_unique<Persistent<v8::Function>>(isolate, ctorFunc);
6566

@@ -73,12 +74,12 @@ void Pointer::PointerConstructorCallback(const FunctionCallbackInfo<Value>& info
7374
void* ptr = nullptr;
7475

7576
if (info.Length() == 1) {
76-
if (!tns::IsNumber(info[0])) {
77+
bool isBigInt = tns::IsBigInt(info[0]);
78+
bool isNumber = !isBigInt && tns::IsNumber(info[0]);
79+
if (!isBigInt && !isNumber) {
7780
throw NativeScriptException("Pointer constructor's first arg must be an integer.");
7881
}
7982

80-
Local<Number> arg = info[0].As<Number>();
81-
8283
#if __SIZEOF_POINTER__ == 8
8384
// JSC stores 64-bit integers as doubles in JSValue.
8485
// Caution: This means that pointers with more than 54 significant bits
@@ -87,12 +88,28 @@ void Pointer::PointerConstructorCallback(const FunctionCallbackInfo<Value>& info
8788
// so we're safe at the time being.
8889
// See https://en.wikipedia.org/wiki/X86-64#Virtual_address_space_details
8990
// and https://en.wikipedia.org/wiki/ARM_architecture#ARMv8-A
91+
92+
// The future is here, and turns out the OS is using more than 54 bits.
93+
// as a result,
9094
int64_t value;
91-
tns::Assert(arg->IntegerValue(context).To(&value), isolate);
95+
if (isBigInt) {
96+
value = info[0].As<BigInt>()->Int64Value();
97+
} else {
98+
// TODO: Maybe log this?
99+
//#ifdef DEBUG
100+
// syslog(LOG_WARNING, "Using JS Number to represent a pointer. Result might be wrong.");
101+
//#endif
102+
tns::Assert(info[0].As<Number>()->IntegerValue(context).To(&value), isolate);
103+
}
104+
92105
ptr = reinterpret_cast<void*>(value);
93106
#else
94107
int32_t value;
95-
tns::Assert(arg->Int32Value(context).To(&value), isolate);
108+
if (isBigInt) {
109+
value = (int32_t)info[0].As<BigInt>()->Int64Value();
110+
} else {
111+
tns::Assert(info[0].As<Number>()->Int32Value (context).To(&value), isolate);
112+
}
96113
ptr = reinterpret_cast<void*>(value);
97114
#endif
98115
}
@@ -253,4 +270,22 @@ void Pointer::RegisterToNumberMethod(Local<Context> context, Local<Object> proto
253270
tns::Assert(success, isolate);
254271
}
255272

273+
void Pointer::RegisterToBigIntMethod(Local<Context> context, Local<Object> prototype) {
274+
Isolate* isolate = context->GetIsolate();
275+
Local<FunctionTemplate> funcTemplate = FunctionTemplate::New(isolate, [](const FunctionCallbackInfo<Value>& info) {
276+
Isolate* isolate = info.GetIsolate();
277+
PointerWrapper* wrapper = static_cast<PointerWrapper*>(info.This()->GetInternalField(0).As<External>()->Value());
278+
const void* value = wrapper->Data();
279+
size_t number = reinterpret_cast<size_t>(value);
280+
Local<BigInt> result = BigInt::NewFromUnsigned(isolate, number);
281+
info.GetReturnValue().Set(result);
282+
});
283+
284+
Local<v8::Function> func;
285+
tns::Assert(funcTemplate->GetFunction(context).ToLocal(&func), isolate);
286+
287+
bool success = prototype->Set(context, tns::ToV8String(isolate, "toBigInt"), func).FromMaybe(false);
288+
tns::Assert(success, isolate);
289+
}
290+
256291
}

NativeScript/runtime/Pointer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class Pointer {
1919
static void RegisterToHexStringMethod(v8::Local<v8::Context> context, v8::Local<v8::Object> prototype);
2020
static void RegisterToDecimalStringMethod(v8::Local<v8::Context> context, v8::Local<v8::Object> prototype);
2121
static void RegisterToNumberMethod(v8::Local<v8::Context> context, v8::Local<v8::Object> prototype);
22+
static void RegisterToBigIntMethod(v8::Local<v8::Context> context, v8::Local<v8::Object> prototype);
2223
};
2324

2425
}

TestRunner/app/tests/Marshalling/PointerTests.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,27 @@ describe(module.id, function () {
1818
expect(pointer.toString()).toBe(`<Pointer: ${hexMinusOneForCurrentBitness}>`);
1919
});
2020

21+
it("Pointer from a BigInt", function () {
22+
const number = 0x12abcdef;
23+
var pointer = new interop.Pointer(BigInt(number));
24+
expect(pointer instanceof interop.Pointer).toBe(true);
25+
expect(pointer.toNumber()).toBe(number);
26+
expect(pointer.toString()).toBe(`<Pointer: 0x${number.toString(16)}>`);
27+
expect(pointer.toDecimalString()).toBe(number.toString());
28+
expect(pointer.toBigInt()).toBe(BigInt(number));
29+
});
30+
31+
it("Pointer from a really big BigInt", function () {
32+
const number = BigInt("0x1fffffffffffffff");
33+
var pointer = new interop.Pointer(number);
34+
expect(pointer instanceof interop.Pointer).toBe(true);
35+
// toNumber is no longer accurate
36+
expect(pointer.toNumber()).toBeGreaterThan(Number.MAX_SAFE_INTEGER);
37+
expect(pointer.toString()).toBe(`<Pointer: 0x${number.toString(16)}>`);
38+
expect(pointer.toDecimalString()).toBe(number.toString());
39+
expect(pointer.toBigInt()).toBe(BigInt(number));
40+
});
41+
2142
it("Pointer from a wrapped Number", function () {
2243
const number = 0x12abcdef;
2344
var pointer = new interop.Pointer(new Number(number));

0 commit comments

Comments
 (0)