Skip to content

Commit

Permalink
BigInt (-DCONFIG_BIGNUM) support (#104)
Browse files Browse the repository at this point in the history
* enable CONFIG_BIGNUM

* recompile for bignum

* bigint basics

* vm.dump for bigint

* fix bigint call

* update changelog

* dump
  • Loading branch information
justjake authored Feb 19, 2023
1 parent 8b4675a commit f562def
Show file tree
Hide file tree
Showing 12 changed files with 99 additions and 21 deletions.
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# Changelog

## v0.22.0 (unreleased)

- [#104](https://github.com/justjake/quickjs-emscripten/pull/104) BigInt support.

- [#100](https://github.com/justjake/quickjs-emscripten/pull/100) **Breaking change** upgrade Emscripten version and switch to `async import(...)` for loading variants.

We also drop support for older browsers and Node versions:

- Node >= 16 is required
- Safari >= 14.1 is required
- Typescript >= 4.7 is recommended, but not required.

## v0.21.2

- [#94](https://github.com/justjake/quickjs-emscripten/pull/94) (thanks to @swimmadude66) allows QuickJS to create many more functions before overflowing.

## v0.21.1

- [#66](https://github.com/justjake/quickjs-emscripten/pull/66) (thanks to @BickelLukas) fixes `ReferenceError` when running in browser due to `global.process`
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ BUILD_TS=ts/generated
# QuickJS
QUICKJS_OBJS=quickjs.o libregexp.o libunicode.o cutils.o quickjs-libc.o libbf.o
QUICKJS_CONFIG_VERSION=$(shell cat $(QUICKJS_ROOT)/VERSION)
QUICKJS_DEFINES:=-D_GNU_SOURCE -DCONFIG_VERSION=\"$(QUICKJS_CONFIG_VERSION)\" -DCONFIG_STACK_CHECK
QUICKJS_DEFINES:=-D_GNU_SOURCE -DCONFIG_VERSION=\"$(QUICKJS_CONFIG_VERSION)\" -DCONFIG_STACK_CHECK -DCONFIG_BIGNUM
VARIANT_QUICKJS_OBJS=$(patsubst %.o, $(BUILD_QUICKJS)/%.$(VARIANT).o, $(QUICKJS_OBJS))

# quickjs-emscripten
Expand Down
2 changes: 1 addition & 1 deletion c/interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ OwnedHeapChar *QTS_Typeof(JSContext *ctx, JSValueConst *value) {

if (JS_IsNumber(*value)) {
result = "number";
} else if (tag == JS_TAG_BIG_INT) {
} else if (JS_IsBigInt(ctx, *value)) {
result = "bigint";
} else if (JS_IsBigFloat(*value)) {
result = "bigfloat";
Expand Down
30 changes: 30 additions & 0 deletions ts/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ export class QuickJSContext implements LowLevelJavascriptVm<QuickJSHandle>, Disp
protected _true: QuickJSHandle | undefined = undefined
/** @private */
protected _global: QuickJSHandle | undefined = undefined
/** @private */
protected _BigInt: QuickJSHandle | undefined = undefined

/**
* Use {@link QuickJS.createVm} to create a QuickJSContext instance.
Expand Down Expand Up @@ -305,6 +307,23 @@ export class QuickJSContext implements LowLevelJavascriptVm<QuickJSHandle>, Disp
return this.memory.heapValueHandle(ptr)
}

/**
* Create a QuickJS [bigint](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) value.
*/
newBigInt(num: bigint): QuickJSHandle {
if (!this._BigInt) {
const bigIntHandle = this.getProp(this.global, "BigInt")
this.memory.manage(bigIntHandle)
this._BigInt = new StaticLifetime(bigIntHandle.value as JSValueConstPointer, this.runtime)
}

const bigIntHandle = this._BigInt
const asString = String(num)
return this.newString(asString).consume((handle) =>
this.unwrapResult(this.callFunction(bigIntHandle, this.undefined, handle))
)
}

/**
* `{}`.
* Create a new QuickJS [object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer).
Expand Down Expand Up @@ -474,6 +493,15 @@ export class QuickJSContext implements LowLevelJavascriptVm<QuickJSHandle>, Disp
return this.memory.consumeJSCharPointer(this.ffi.QTS_GetString(this.ctx.value, handle.value))
}

/**
* Converts `handle` to a Javascript bigint.
*/
getBigInt(handle: QuickJSHandle): bigint {
this.runtime.assertOwned(handle)
const asString = this.getString(handle)
return BigInt(asString)
}

/**
* `Promise.resolve(value)`.
* Convert a handle containing a Promise-like value inside the VM into an
Expand Down Expand Up @@ -736,6 +764,8 @@ export class QuickJSContext implements LowLevelJavascriptVm<QuickJSHandle>, Disp
return this.getString(handle)
} else if (type === "number") {
return this.getNumber(handle)
} else if (type === "bigint") {
return this.getBigInt(handle)
} else if (type === "undefined") {
return undefined
}
Expand Down
22 changes: 16 additions & 6 deletions ts/generated/emscripten-module.WASM_DEBUG_ASYNCIFY.js
Original file line number Diff line number Diff line change
Expand Up @@ -2092,6 +2092,16 @@ var dynCall_iiii = Module["dynCall_iiii"] = createExportWrapper("dynCall_iiii");
/** @type {function(...*):?} */
var dynCall_ii = Module["dynCall_ii"] = createExportWrapper("dynCall_ii");
/** @type {function(...*):?} */
var dynCall_jiij = Module["dynCall_jiij"] = createExportWrapper("dynCall_jiij");
/** @type {function(...*):?} */
var dynCall_iiiijj = Module["dynCall_iiiijj"] = createExportWrapper("dynCall_iiiijj");
/** @type {function(...*):?} */
var dynCall_iiiij = Module["dynCall_iiiij"] = createExportWrapper("dynCall_iiiij");
/** @type {function(...*):?} */
var dynCall_jiiiii = Module["dynCall_jiiiii"] = createExportWrapper("dynCall_jiiiii");
/** @type {function(...*):?} */
var dynCall_jij = Module["dynCall_jij"] = createExportWrapper("dynCall_jij");
/** @type {function(...*):?} */
var dynCall_jijjiii = Module["dynCall_jijjiii"] = createExportWrapper("dynCall_jijjiii");
/** @type {function(...*):?} */
var dynCall_jiii = Module["dynCall_jiii"] = createExportWrapper("dynCall_jiii");
Expand All @@ -2110,6 +2120,8 @@ var dynCall_viji = Module["dynCall_viji"] = createExportWrapper("dynCall_viji");
/** @type {function(...*):?} */
var dynCall_vij = Module["dynCall_vij"] = createExportWrapper("dynCall_vij");
/** @type {function(...*):?} */
var dynCall_iiijj = Module["dynCall_iiijj"] = createExportWrapper("dynCall_iiijj");
/** @type {function(...*):?} */
var dynCall_viii = Module["dynCall_viii"] = createExportWrapper("dynCall_viii");
/** @type {function(...*):?} */
var dynCall_iijijjji = Module["dynCall_iijijjji"] = createExportWrapper("dynCall_iijijjji");
Expand All @@ -2118,16 +2130,12 @@ var dynCall_iiiji = Module["dynCall_iiiji"] = createExportWrapper("dynCall_iiiji
/** @type {function(...*):?} */
var dynCall_iiji = Module["dynCall_iiji"] = createExportWrapper("dynCall_iiji");
/** @type {function(...*):?} */
var dynCall_iiiij = Module["dynCall_iiiij"] = createExportWrapper("dynCall_iiiij");
/** @type {function(...*):?} */
var dynCall_jijij = Module["dynCall_jijij"] = createExportWrapper("dynCall_jijij");
/** @type {function(...*):?} */
var dynCall_iijijji = Module["dynCall_iijijji"] = createExportWrapper("dynCall_iijijji");
/** @type {function(...*):?} */
var dynCall_jiiii = Module["dynCall_jiiii"] = createExportWrapper("dynCall_jiiii");
/** @type {function(...*):?} */
var dynCall_jij = Module["dynCall_jij"] = createExportWrapper("dynCall_jij");
/** @type {function(...*):?} */
var dynCall_jiji = Module["dynCall_jiji"] = createExportWrapper("dynCall_jiji");
/** @type {function(...*):?} */
var dynCall_dd = Module["dynCall_dd"] = createExportWrapper("dynCall_dd");
Expand All @@ -2136,6 +2144,8 @@ var dynCall_ddd = Module["dynCall_ddd"] = createExportWrapper("dynCall_ddd");
/** @type {function(...*):?} */
var dynCall_jii = Module["dynCall_jii"] = createExportWrapper("dynCall_jii");
/** @type {function(...*):?} */
var dynCall_iiiiii = Module["dynCall_iiiiii"] = createExportWrapper("dynCall_iiiiii");
/** @type {function(...*):?} */
var dynCall_iidiiii = Module["dynCall_iidiiii"] = createExportWrapper("dynCall_iidiiii");
/** @type {function(...*):?} */
var _asyncify_start_unwind = createExportWrapper("asyncify_start_unwind");
Expand All @@ -2145,8 +2155,8 @@ var _asyncify_stop_unwind = createExportWrapper("asyncify_stop_unwind");
var _asyncify_start_rewind = createExportWrapper("asyncify_start_rewind");
/** @type {function(...*):?} */
var _asyncify_stop_rewind = createExportWrapper("asyncify_stop_rewind");
var ___start_em_js = Module['___start_em_js'] = 82784;
var ___stop_em_js = Module['___stop_em_js'] = 83779;
var ___start_em_js = Module['___start_em_js'] = 86032;
var ___stop_em_js = Module['___stop_em_js'] = 87027;

// include: postamble.js
// === Auto-generated postamble setup entry stuff ===
Expand Down
Binary file modified ts/generated/emscripten-module.WASM_DEBUG_ASYNCIFY.wasm
Binary file not shown.

Large diffs are not rendered by default.

24 changes: 16 additions & 8 deletions ts/generated/emscripten-module.WASM_DEBUG_SYNC.js
Original file line number Diff line number Diff line change
Expand Up @@ -1537,8 +1537,8 @@ function dbg(text) {
// === Body ===

var ASM_CONSTS = {
5358123: () => { return withBuiltinMalloc(function () { return allocateUTF8(Module['LSAN_OPTIONS'] || 0); }); },
5358220: () => { var setting = Module['printWithColors']; if (setting != null) { return setting; } else { return ENVIRONMENT_IS_NODE && process.stderr.isTTY; } }
5361259: () => { return withBuiltinMalloc(function () { return allocateUTF8(Module['LSAN_OPTIONS'] || 0); }); },
5361356: () => { var setting = Module['printWithColors']; if (setting != null) { return setting; } else { return ENVIRONMENT_IS_NODE && process.stderr.isTTY; } }
};
function qts_host_call_function(ctx,this_ptr,argc,argv,magic_func_id) { const asyncify = undefined; return Module['callbacks']['callFunction'](asyncify, ctx, this_ptr, argc, argv, magic_func_id); }
function qts_host_interrupt_handler(rt) { const asyncify = undefined; return Module['callbacks']['shouldInterrupt'](asyncify, rt); }
Expand Down Expand Up @@ -4980,10 +4980,22 @@ var dynCall_jijiii = Module["dynCall_jijiii"] = createExportWrapper("dynCall_jij
/** @type {function(...*):?} */
var dynCall_jijjiii = Module["dynCall_jijjiii"] = createExportWrapper("dynCall_jijjiii");
/** @type {function(...*):?} */
var dynCall_jij = Module["dynCall_jij"] = createExportWrapper("dynCall_jij");
/** @type {function(...*):?} */
var dynCall_jiiiii = Module["dynCall_jiiiii"] = createExportWrapper("dynCall_jiiiii");
/** @type {function(...*):?} */
var dynCall_iiiij = Module["dynCall_iiiij"] = createExportWrapper("dynCall_iiiij");
/** @type {function(...*):?} */
var dynCall_iiiijj = Module["dynCall_iiiijj"] = createExportWrapper("dynCall_iiiijj");
/** @type {function(...*):?} */
var dynCall_jiij = Module["dynCall_jiij"] = createExportWrapper("dynCall_jiij");
/** @type {function(...*):?} */
var dynCall_jijii = Module["dynCall_jijii"] = createExportWrapper("dynCall_jijii");
/** @type {function(...*):?} */
var dynCall_jijiiiii = Module["dynCall_jijiiiii"] = createExportWrapper("dynCall_jijiiiii");
/** @type {function(...*):?} */
var dynCall_iiijj = Module["dynCall_iiijj"] = createExportWrapper("dynCall_iiijj");
/** @type {function(...*):?} */
var dynCall_jijj = Module["dynCall_jijj"] = createExportWrapper("dynCall_jijj");
/** @type {function(...*):?} */
var dynCall_jiii = Module["dynCall_jiii"] = createExportWrapper("dynCall_jiii");
Expand All @@ -5002,19 +5014,15 @@ var dynCall_iiiji = Module["dynCall_iiiji"] = createExportWrapper("dynCall_iiiji
/** @type {function(...*):?} */
var dynCall_iiji = Module["dynCall_iiji"] = createExportWrapper("dynCall_iiji");
/** @type {function(...*):?} */
var dynCall_iiiij = Module["dynCall_iiiij"] = createExportWrapper("dynCall_iiiij");
/** @type {function(...*):?} */
var dynCall_jijij = Module["dynCall_jijij"] = createExportWrapper("dynCall_jijij");
/** @type {function(...*):?} */
var dynCall_iijijji = Module["dynCall_iijijji"] = createExportWrapper("dynCall_iijijji");
/** @type {function(...*):?} */
var dynCall_jiiii = Module["dynCall_jiiii"] = createExportWrapper("dynCall_jiiii");
/** @type {function(...*):?} */
var dynCall_jij = Module["dynCall_jij"] = createExportWrapper("dynCall_jij");
/** @type {function(...*):?} */
var dynCall_jiji = Module["dynCall_jiji"] = createExportWrapper("dynCall_jiji");
var ___start_em_js = Module['___start_em_js'] = 5357221;
var ___stop_em_js = Module['___stop_em_js'] = 5358123;
var ___start_em_js = Module['___start_em_js'] = 5360357;
var ___stop_em_js = Module['___stop_em_js'] = 5361259;

// include: postamble.js
// === Auto-generated postamble setup entry stuff ===
Expand Down
Binary file modified ts/generated/emscripten-module.WASM_DEBUG_SYNC.wasm
Binary file not shown.
4 changes: 2 additions & 2 deletions ts/generated/emscripten-module.WASM_RELEASE_ASYNCIFY.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions ts/generated/emscripten-module.WASM_RELEASE_SYNC.js

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions ts/quickjs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,20 @@ function contextTests(getContext: () => Promise<QuickJSContext>, isDebug = false
numHandle.dispose()
})

it("can round-trip a bigint", () => {
const int = 2n ** 64n
const numHandle = vm.newBigInt(int)
assert.equal(vm.getBigInt(numHandle), int)
numHandle.dispose()
})

it("can dump a bigint", () => {
const int = 2n ** 64n
const numHandle = vm.newBigInt(int)
assert.equal(vm.dump(numHandle), int)
numHandle.dispose()
})

it("can round-trip a string", () => {
const jsString = "an example 🤔 string with unicode 🎉"
const stringHandle = vm.newString(jsString)
Expand Down

0 comments on commit f562def

Please sign in to comment.