Skip to content

Commit 45ef6d0

Browse files
authored
js: Adding manual release methods (#7428)
* js: Adding manual release methods * Add unregister token * Add pointer assertion * Add missing line
1 parent 5cee19f commit 45ef6d0

File tree

2 files changed

+75
-21
lines changed

2 files changed

+75
-21
lines changed

src/api/js/src/high-level/high-level.ts

+54-20
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,10 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
178178
ctxs.forEach(other => assert('ctx' in other ? ctx === other.ctx : ctx === other, 'Context mismatch'));
179179
}
180180

181+
function _assertPtr<T extends Number>(ptr: T | null): asserts ptr is T {
182+
if (ptr == null) throw new TypeError('Expected non-null pointer');
183+
}
184+
181185
// call this after every nontrivial call to the underlying API
182186
function throwIfError() {
183187
if (Z3.get_error_code(contextPtr) !== Z3_error_code.Z3_OK) {
@@ -1203,7 +1207,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
12031207
const myAst = this.ast;
12041208

12051209
Z3.inc_ref(contextPtr, myAst);
1206-
cleanup.register(this, () => Z3.dec_ref(contextPtr, myAst));
1210+
cleanup.register(this, () => Z3.dec_ref(contextPtr, myAst), this);
12071211
}
12081212

12091213
get ast(): Z3_ast {
@@ -1240,8 +1244,12 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
12401244
class SolverImpl implements Solver<Name> {
12411245
declare readonly __typename: Solver['__typename'];
12421246

1243-
readonly ptr: Z3_solver;
12441247
readonly ctx: Context<Name>;
1248+
private _ptr: Z3_solver | null;
1249+
get ptr(): Z3_solver {
1250+
_assertPtr(this._ptr);
1251+
return this._ptr;
1252+
}
12451253

12461254
constructor(ptr: Z3_solver | string = Z3.mk_solver(contextPtr)) {
12471255
this.ctx = ctx;
@@ -1251,9 +1259,9 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
12511259
} else {
12521260
myPtr = ptr;
12531261
}
1254-
this.ptr = myPtr;
1262+
this._ptr = myPtr;
12551263
Z3.solver_inc_ref(contextPtr, myPtr);
1256-
cleanup.register(this, () => Z3.solver_dec_ref(contextPtr, myPtr));
1264+
cleanup.register(this, () => Z3.solver_dec_ref(contextPtr, myPtr), this);
12571265
}
12581266

12591267
set(key: string, value: any): void {
@@ -1327,21 +1335,32 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
13271335
Z3.solver_from_string(contextPtr, this.ptr, s);
13281336
throwIfError();
13291337
}
1338+
1339+
release() {
1340+
Z3.solver_dec_ref(contextPtr, this.ptr);
1341+
// Mark the ptr as null to prevent double free
1342+
this._ptr = null;
1343+
cleanup.unregister(this);
1344+
}
13301345
}
13311346

13321347
class OptimizeImpl implements Optimize<Name> {
13331348
declare readonly __typename: Optimize['__typename'];
13341349

1335-
readonly ptr: Z3_optimize;
13361350
readonly ctx: Context<Name>;
1351+
private _ptr: Z3_optimize | null;
1352+
get ptr(): Z3_optimize {
1353+
_assertPtr(this._ptr);
1354+
return this._ptr;
1355+
}
13371356

13381357
constructor(ptr: Z3_optimize = Z3.mk_optimize(contextPtr)) {
13391358
this.ctx = ctx;
13401359
let myPtr: Z3_optimize;
13411360
myPtr = ptr;
1342-
this.ptr = myPtr;
1361+
this._ptr = myPtr;
13431362
Z3.optimize_inc_ref(contextPtr, myPtr);
1344-
cleanup.register(this, () => Z3.optimize_dec_ref(contextPtr, myPtr));
1363+
cleanup.register(this, () => Z3.optimize_dec_ref(contextPtr, myPtr), this);
13451364
}
13461365

13471366
set(key: string, value: any): void {
@@ -1363,11 +1382,11 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
13631382
});
13641383
}
13651384

1366-
addSoft(expr: Bool<Name>, weight: number | bigint | string | CoercibleRational, id: number | string = "") {
1385+
addSoft(expr: Bool<Name>, weight: number | bigint | string | CoercibleRational, id: number | string = '') {
13671386
if (isCoercibleRational(weight)) {
13681387
weight = `${weight.numerator}/${weight.denominator}`;
13691388
}
1370-
check(Z3.optimize_assert_soft(contextPtr, this.ptr, expr.ast, weight.toString(), _toSymbol(id)))
1389+
check(Z3.optimize_assert_soft(contextPtr, this.ptr, expr.ast, weight.toString(), _toSymbol(id)));
13711390
}
13721391

13731392
addAndTrack(expr: Bool<Name>, constant: Bool<Name> | string) {
@@ -1395,9 +1414,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
13951414
_assertContext(expr);
13961415
return expr.ast;
13971416
});
1398-
const result = await asyncMutex.runExclusive(() =>
1399-
check(Z3.optimize_check(contextPtr, this.ptr, assumptions)),
1400-
);
1417+
const result = await asyncMutex.runExclusive(() => check(Z3.optimize_check(contextPtr, this.ptr, assumptions)));
14011418
switch (result) {
14021419
case Z3_lbool.Z3_L_FALSE:
14031420
return 'unsat';
@@ -1422,17 +1439,28 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
14221439
Z3.optimize_from_string(contextPtr, this.ptr, s);
14231440
throwIfError();
14241441
}
1425-
}
14261442

1443+
release() {
1444+
Z3.optimize_dec_ref(contextPtr, this.ptr);
1445+
this._ptr = null;
1446+
cleanup.unregister(this);
1447+
}
1448+
}
14271449

14281450
class ModelImpl implements Model<Name> {
14291451
declare readonly __typename: Model['__typename'];
14301452
readonly ctx: Context<Name>;
1453+
private _ptr: Z3_model | null;
1454+
get ptr(): Z3_model {
1455+
_assertPtr(this._ptr);
1456+
return this._ptr;
1457+
}
14311458

1432-
constructor(readonly ptr: Z3_model = Z3.mk_model(contextPtr)) {
1459+
constructor(ptr: Z3_model = Z3.mk_model(contextPtr)) {
14331460
this.ctx = ctx;
1461+
this._ptr = ptr;
14341462
Z3.model_inc_ref(contextPtr, ptr);
1435-
cleanup.register(this, () => Z3.model_dec_ref(contextPtr, ptr));
1463+
cleanup.register(this, () => Z3.model_dec_ref(contextPtr, ptr), this);
14361464
}
14371465

14381466
length() {
@@ -1594,6 +1622,12 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
15941622
_assertContext(sort);
15951623
return new AstVectorImpl(check(Z3.model_get_sort_universe(contextPtr, this.ptr, sort.ptr)));
15961624
}
1625+
1626+
release() {
1627+
Z3.model_dec_ref(contextPtr, this.ptr);
1628+
this._ptr = null;
1629+
cleanup.unregister(this);
1630+
}
15971631
}
15981632

15991633
class FuncEntryImpl implements FuncEntry<Name> {
@@ -1604,7 +1638,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
16041638
constructor(readonly ptr: Z3_func_entry) {
16051639
this.ctx = ctx;
16061640
Z3.func_entry_inc_ref(contextPtr, ptr);
1607-
cleanup.register(this, () => Z3.func_entry_dec_ref(contextPtr, ptr));
1641+
cleanup.register(this, () => Z3.func_entry_dec_ref(contextPtr, ptr), this);
16081642
}
16091643

16101644
numArgs() {
@@ -1627,7 +1661,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
16271661
constructor(readonly ptr: Z3_func_interp) {
16281662
this.ctx = ctx;
16291663
Z3.func_interp_inc_ref(contextPtr, ptr);
1630-
cleanup.register(this, () => Z3.func_interp_dec_ref(contextPtr, ptr));
1664+
cleanup.register(this, () => Z3.func_interp_dec_ref(contextPtr, ptr), this);
16311665
}
16321666

16331667
elseValue(): Expr<Name> {
@@ -1922,7 +1956,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
19221956
this.ptr = myPtr;
19231957

19241958
Z3.tactic_inc_ref(contextPtr, myPtr);
1925-
cleanup.register(this, () => Z3.tactic_dec_ref(contextPtr, myPtr));
1959+
cleanup.register(this, () => Z3.tactic_dec_ref(contextPtr, myPtr), this);
19261960
}
19271961
}
19281962

@@ -2586,7 +2620,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
25862620
constructor(readonly ptr: Z3_ast_vector = Z3.mk_ast_vector(contextPtr)) {
25872621
this.ctx = ctx;
25882622
Z3.ast_vector_inc_ref(contextPtr, ptr);
2589-
cleanup.register(this, () => Z3.ast_vector_dec_ref(contextPtr, ptr));
2623+
cleanup.register(this, () => Z3.ast_vector_dec_ref(contextPtr, ptr), this);
25902624
}
25912625

25922626
length(): number {
@@ -2684,7 +2718,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
26842718
constructor(readonly ptr: Z3_ast_map = Z3.mk_ast_map(contextPtr)) {
26852719
this.ctx = ctx;
26862720
Z3.ast_map_inc_ref(contextPtr, ptr);
2687-
cleanup.register(this, () => Z3.ast_map_dec_ref(contextPtr, ptr));
2721+
cleanup.register(this, () => Z3.ast_map_dec_ref(contextPtr, ptr), this);
26882722
}
26892723

26902724
[Symbol.iterator](): Iterator<[Key, Value]> {

src/api/js/src/high-level/types.ts

+21-1
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,13 @@ export interface Solver<Name extends string = 'main'> {
663663
check(...exprs: (Bool<Name> | AstVector<Name, Bool<Name>>)[]): Promise<CheckSatResult>;
664664

665665
model(): Model<Name>;
666+
667+
/**
668+
* Manually decrease the reference count of the solver
669+
* This is automatically done when the solver is garbage collected,
670+
* but calling this eagerly can help release memory sooner.
671+
*/
672+
release(): void;
666673
}
667674

668675
export interface Optimize<Name extends string = 'main'> {
@@ -695,8 +702,14 @@ export interface Optimize<Name extends string = 'main'> {
695702
check(...exprs: (Bool<Name> | AstVector<Name, Bool<Name>>)[]): Promise<CheckSatResult>;
696703

697704
model(): Model<Name>;
698-
}
699705

706+
/**
707+
* Manually decrease the reference count of the optimize
708+
* This is automatically done when the optimize is garbage collected,
709+
* but calling this eagerly can help release memory sooner.
710+
*/
711+
release(): void;
712+
}
700713

701714
/** @hidden */
702715
export interface ModelCtor<Name extends string> {
@@ -746,6 +759,13 @@ export interface Model<Name extends string = 'main'> extends Iterable<FuncDecl<N
746759
decl: FuncDecl<Name, DomainSort, RangeSort>,
747760
defaultValue: CoercibleToMap<SortToExprMap<RangeSort, Name>, Name>,
748761
): FuncInterp<Name>;
762+
763+
/**
764+
* Manually decrease the reference count of the model
765+
* This is automatically done when the model is garbage collected,
766+
* but calling this eagerly can help release memory sooner.
767+
*/
768+
release(): void;
749769
}
750770

751771
/**

0 commit comments

Comments
 (0)