From ad7ee40fe0efd87a5abf28fe2be883d526dcc382 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Sat, 21 Feb 2026 13:12:34 -0600 Subject: [PATCH] proper gc rooting --- dsp.c | 3 +-- flac.c | 15 ++++++++++----- mp3.c | 16 +++++++++++----- soundwave.cm | 16 ++++++++-------- wav.c | 15 ++++++++++----- 5 files changed, 40 insertions(+), 25 deletions(-) diff --git a/dsp.c b/dsp.c index 8097978..d2ada44 100644 --- a/dsp.c +++ b/dsp.c @@ -17,9 +17,8 @@ JSC_CCALL(dsp_mix_blobs, if (!JS_IsArray(vols_arr)) return JS_ThrowTypeError(js, "volumes must be an array"); int len = 0; - JSValue len_val = JS_GetPropertyStr(js, blobs_arr, "length"); + JSValue len_val = JS_CellLength(js, blobs_arr); JS_ToInt32(js, &len, len_val); - JS_FreeValue(js, len_val); if (len == 0) { // Return empty stoned blob diff --git a/flac.c b/flac.c index 6116417..c94ef05 100644 --- a/flac.c +++ b/flac.c @@ -28,10 +28,12 @@ static JSValue flac_make_info(JSContext *js, drflac *flac) JS_SetPropertyStr(js, obj.val, "channels", JS_NewInt32(js, flac->channels)); JS_SetPropertyStr(js, obj.val, "sample_rate", JS_NewInt32(js, flac->sampleRate)); JS_SetPropertyStr(js, obj.val, "bits_per_sample", JS_NewInt32(js, flac->bitsPerSample)); - JS_SetPropertyStr(js, obj.val, "total_pcm_frames", JS_NewFloat64(js, (double)flac->totalPCMFrameCount)); + JSValue pcm_count = JS_NewFloat64(js, (double)flac->totalPCMFrameCount); + JS_SetPropertyStr(js, obj.val, "total_pcm_frames", pcm_count); JS_SetPropertyStr(js, obj.val, "decoded_bytes_per_frame", JS_NewInt32(js, (int)((size_t)flac->channels * sizeof(drflac_int32)))); - JS_SetPropertyStr(js, obj.val, "format", JS_NewString(js, "s32")); + JSValue fmt = JS_NewString(js, "s32"); + JS_SetPropertyStr(js, obj.val, "format", fmt); JS_RETURN(obj.val); } @@ -94,11 +96,14 @@ JSC_CCALL(flac_decode, JS_FRAME(js); JS_ROOT(result, flac_make_info(js, flac)); - // Update format info - JS_SetPropertyStr(js, result.val, "format", JS_NewString(js, "f32")); + // Update format info — evaluate allocating calls in separate statements + // (C argument evaluation order is unspecified; GC can invalidate .val) + JSValue fmt = JS_NewString(js, "f32"); + JS_SetPropertyStr(js, result.val, "format", fmt); JS_SetPropertyStr(js, result.val, "decoded_bytes_per_frame", JS_NewInt32(js, (int)bytes_per_frame)); - JS_SetPropertyStr(js, result.val, "pcm", js_new_blob_stoned_copy(js, pcm, bytes_read)); + JSValue pcm_blob = js_new_blob_stoned_copy(js, pcm, bytes_read); + JS_SetPropertyStr(js, result.val, "pcm", pcm_blob); free(pcm); drflac_close(flac); JS_RETURN(result.val); diff --git a/mp3.c b/mp3.c index dea55e7..963cfda 100644 --- a/mp3.c +++ b/mp3.c @@ -14,10 +14,12 @@ static JSValue mp3_make_info(JSContext *js, drmp3_uint32 channels, drmp3_uint32 JS_SetPropertyStr(js, obj.val, "bits_per_sample", JS_NewInt32(js, 16)); double total_frames = frames == DRMP3_UINT64_MAX ? -1.0 : (double)frames; - JS_SetPropertyStr(js, obj.val, "total_pcm_frames", JS_NewFloat64(js, total_frames)); + JSValue pcm_count = JS_NewFloat64(js, total_frames); + JS_SetPropertyStr(js, obj.val, "total_pcm_frames", pcm_count); JS_SetPropertyStr(js, obj.val, "decoded_bytes_per_frame", JS_NewInt32(js, (int)((size_t)channels * sizeof(drmp3_int16)))); - JS_SetPropertyStr(js, obj.val, "format", JS_NewString(js, "s16")); + JSValue fmt = JS_NewString(js, "s16"); + JS_SetPropertyStr(js, obj.val, "format", fmt); JS_RETURN(obj.val); } @@ -85,11 +87,15 @@ JSC_CCALL(mp3_decode, JS_FRAME(js); JS_ROOT(result, mp3_make_info(js, config.channels, config.sampleRate, frames)); - // Update format info - JS_SetPropertyStr(js, result.val, "format", JS_NewString(js, "f32")); + // Update format info — evaluate allocating calls before reading .val + // (C argument evaluation order is unspecified; GC during allocation + // can move the rooted object, invalidating an already-read .val) + JSValue fmt = JS_NewString(js, "f32"); + JS_SetPropertyStr(js, result.val, "format", fmt); JS_SetPropertyStr(js, result.val, "decoded_bytes_per_frame", JS_NewInt32(js, (int)bytes_per_frame)); - JS_SetPropertyStr(js, result.val, "pcm", js_new_blob_stoned_copy(js, pcm, total_bytes)); + JSValue pcm_blob = js_new_blob_stoned_copy(js, pcm, total_bytes); + JS_SetPropertyStr(js, result.val, "pcm", pcm_blob); drmp3_free(pcm, NULL); JS_RETURN(result.val); ) diff --git a/soundwave.cm b/soundwave.cm index 312eb60..7613162 100644 --- a/soundwave.cm +++ b/soundwave.cm @@ -139,16 +139,16 @@ soundwave.create = function(opts) { // Pull a chunk of audio from a voice, handling looping function pull_voice_chunk(voice, frames) { if (voice.stopped) return null - + var source = voice.source var total_frames = source.frames var pos = voice.pos var bytes_per_frame = player.channels * BYTES_PER_SAMPLE var bits_per_frame = bytes_per_frame * 8 - + var out = Blob() var frames_written = 0 - + var frames_available = null var frames_needed = null var frames_to_read = null @@ -172,13 +172,13 @@ soundwave.create = function(opts) { end_bit = (pos + frames_to_read) * bits_per_frame chunk = source.pcm.read_blob(start_bit, end_bit) out.write_blob(chunk) - + pos += frames_to_read frames_written += frames_to_read } - + voice.pos = pos - + // Pad with silence if needed var silence_frames = null var silence = null @@ -187,7 +187,7 @@ soundwave.create = function(opts) { silence = dsp.silence(silence_frames, player.channels) out.write_blob(silence) } - + stone(out) return out } @@ -259,7 +259,7 @@ soundwave.create = function(opts) { } else { mixed = dsp.mix_blobs(blobs, vols) } - + player.cleanup() return mixed } diff --git a/wav.c b/wav.c index fdd4f73..d8af4ef 100644 --- a/wav.c +++ b/wav.c @@ -29,7 +29,8 @@ static JSValue wav_make_info(JSContext *js, drwav *wav) JS_SetPropertyStr(js, obj.val, "sample_rate", JS_NewInt32(js, wav->sampleRate)); JS_SetPropertyStr(js, obj.val, "bits_per_sample", JS_NewInt32(js, wav->bitsPerSample)); JS_SetPropertyStr(js, obj.val, "format_tag", JS_NewInt32(js, wav->translatedFormatTag)); - JS_SetPropertyStr(js, obj.val, "total_pcm_frames", JS_NewFloat64(js, (double)wav->totalPCMFrameCount)); + JSValue pcm_count = JS_NewFloat64(js, (double)wav->totalPCMFrameCount); + JS_SetPropertyStr(js, obj.val, "total_pcm_frames", pcm_count); JS_SetPropertyStr(js, obj.val, "bytes_per_frame", JS_NewInt32(js, (int)drwav_get_bytes_per_pcm_frame(wav))); JS_RETURN(obj.val); } @@ -94,15 +95,19 @@ JSC_CCALL(wav_decode, JS_FRAME(js); JS_ROOT(result, wav_make_info(js, &wav)); - // Update format info to reflect f32 - JS_SetPropertyStr(js, result.val, "format", JS_NewString(js, "f32")); + // Update format info — evaluate allocating calls in separate statements + // (C argument evaluation order is unspecified; GC can invalidate .val) + JSValue fmt = JS_NewString(js, "f32"); + JS_SetPropertyStr(js, result.val, "format", fmt); JS_SetPropertyStr(js, result.val, "bytes_per_frame", JS_NewInt32(js, (int)bytes_per_frame)); if (pcm_bytes > 0) { - JS_SetPropertyStr(js, result.val, "pcm", js_new_blob_stoned_copy(js, pcm, bytes_read)); + JSValue pcm_blob = js_new_blob_stoned_copy(js, pcm, bytes_read); + JS_SetPropertyStr(js, result.val, "pcm", pcm_blob); free(pcm); } else { - JS_SetPropertyStr(js, result.val, "pcm", js_new_blob_stoned_copy(js, NULL, 0)); + JSValue pcm_blob = js_new_blob_stoned_copy(js, NULL, 0); + JS_SetPropertyStr(js, result.val, "pcm", pcm_blob); } drwav_uninit(&wav);