boundary-batch-nullpointer-fix
Simon Holthausen 3 weeks ago
parent e14561c829
commit 0f4d0b101b

@ -28,7 +28,7 @@ import { queue_micro_task } from '../task.js';
import * as e from '../../errors.js';
import * as w from '../../warnings.js';
import { DEV } from 'esm-env';
import { Batch, effect_pending_updates } from '../../reactivity/batch.js';
import { Batch, current_batch, effect_pending_updates } from '../../reactivity/batch.js';
import { internal_set, source } from '../../reactivity/sources.js';
import { tag } from '../../dev/tracing.js';
import { createSubscriber } from '../../../../reactivity/create-subscriber.js';
@ -69,7 +69,7 @@ export class Boundary {
*
* @type {Batch | null}
*/
batch = null;
#batch = null;
/** @type {TemplateNode} */
#anchor;
@ -200,6 +200,13 @@ export class Boundary {
return !!this.#props.pending;
}
get_batch() {
if (current_batch) {
this.#batch = current_batch;
}
return /** @type {Batch} */ (this.#batch);
}
/**
* @param {() => Effect | null} fn
*/
@ -243,7 +250,7 @@ export class Boundary {
if (this.#pending_count === 0) {
this.pending = false;
this.batch = null;
this.#batch = null;
if (this.#pending_effect) {
pause_effect(this.#pending_effect, () => {

@ -184,6 +184,14 @@ export class Batch {
/** @type {Map<Source, { v: unknown, wv: number }> | null} */
var current_values = null;
/**
* A batch is superseeded if all of its sources are also in the current batch.
* If the current batch commits, we don't need the old batch anymore.
* This also prevents memory leaks since the old batch will never be committed.
* @type {Batch[]}
*/
var superseeded_batches = [];
// if there are multiple batches, we are 'time travelling' —
// we need to undo the changes belonging to any batch
// other than the current one
@ -196,15 +204,25 @@ export class Batch {
source.v = current;
}
let is_prior_batch = true;
for (const batch of batches) {
if (batch === this) continue;
if (batch === this) {
is_prior_batch = false;
continue;
}
let superseeded = is_prior_batch;
for (const [source, previous] of batch.#previous) {
if (!current_values.has(source)) {
superseeded = false;
current_values.set(source, { v: source.v, wv: source.wv });
source.v = previous;
}
}
if (superseeded) superseeded_batches.push(batch);
}
}
@ -215,6 +233,10 @@ export class Batch {
// if we didn't start any new async work, and no async work
// is outstanding from a previous flush, commit
if (this.#async_effects.length === 0 && this.#pending === 0) {
for (const batch of superseeded_batches) {
batch.remove();
}
this.#commit();
var render_effects = this.#render_effects;
@ -376,6 +398,11 @@ export class Batch {
this.#neutered = true;
}
remove() {
this.neuter();
batches.delete(this);
}
flush() {
if (queued_root_effects.length > 0) {
flush_effects();
@ -664,7 +691,7 @@ export function schedule_effect(signal) {
export function suspend() {
var boundary = get_pending_boundary();
var batch = (boundary.batch ??= /** @type {Batch} */ (current_batch));
var batch = boundary.get_batch();
var pending = boundary.pending;
boundary.update_pending_count(1);

@ -1,5 +1,4 @@
/** @import { Derived, Effect, Source } from '#client' */
/** @import { Batch } from './batch.js'; */
import { DEV } from 'esm-env';
import {
ERROR_VALUE,
@ -33,7 +32,7 @@ import { tracing_mode_flag } from '../../flags/index.js';
import { Boundary } from '../dom/blocks/boundary.js';
import { component_context } from '../context.js';
import { UNINITIALIZED } from '../../../constants.js';
import { batch_deriveds, current_batch } from './batch.js';
import { batch_deriveds } from './batch.js';
import { unset_context } from './async.js';
/** @type {Effect | null} */
@ -131,7 +130,7 @@ export function async_derived(fn, location) {
prev = promise;
var batch = (boundary.batch ??= /** @type {Batch} */ (current_batch));
var batch = boundary.get_batch();
var pending = boundary.pending;
if (should_suspend) {

Loading…
Cancel
Save