Skip to content

Commit 3c41e23

Browse files
committed
Make sure the unit tests pass and the reused memory is not double freed
1 parent 6717602 commit 3c41e23

1 file changed

Lines changed: 81 additions & 97 deletions

File tree

bindings/java/src/main/java/com/cadoodlecad/manifold/ManifoldBindings.java

Lines changed: 81 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
import java.nio.file.Files;
77
import java.util.ArrayList;
88
import java.util.HashMap;
9+
import java.util.HashSet;
910
import java.util.Map;
10-
11+
import java.util.Set;
1112
import java.io.*;
1213
import java.lang.foreign.MemorySegment;
1314
import java.nio.*;
@@ -297,16 +298,19 @@ public ManifoldBindings(File cacheDirectory) throws Exception {
297298
// ManifoldPolygons* manifold_slice(void* mem, ManifoldManifold* m, double
298299
// height);
299300
load("manifold_slice", ValueLayout.ADDRESS, ValueLayout.ADDRESS, ValueLayout.ADDRESS, ValueLayout.JAVA_DOUBLE);
300-
// size_t manifold_polygons_num_contour(ManifoldPolygons* p);
301-
load("manifold_polygons_num_contour", ValueLayout.JAVA_LONG, ValueLayout.ADDRESS);
302301

303-
// size_t manifold_polygons_contour_length(ManifoldPolygons* p, int idx);
304-
load("manifold_polygons_contour_length", ValueLayout.JAVA_LONG, ValueLayout.ADDRESS, ValueLayout.JAVA_INT);
305302

306-
// ManifoldVec2 manifold_polygons_get_point(ManifoldPolygons* p, int contour, int idx);
307-
// ManifoldVec2 is {double x, double y} = 16 bytes
308-
load("manifold_polygons_get_point", MemoryLayout.structLayout(ValueLayout.JAVA_DOUBLE, ValueLayout.JAVA_DOUBLE),
309-
ValueLayout.ADDRESS, ValueLayout.JAVA_INT, ValueLayout.JAVA_INT);
303+
load("manifold_alloc_polygons", ValueLayout.ADDRESS);
304+
// size_t manifold_polygons_length(ManifoldPolygons* ps);
305+
load("manifold_polygons_length", ValueLayout.JAVA_LONG, ValueLayout.ADDRESS);
306+
307+
// size_t manifold_polygons_simple_length(ManifoldPolygons* ps, size_t idx);
308+
load("manifold_polygons_simple_length", ValueLayout.JAVA_LONG, ValueLayout.ADDRESS, ValueLayout.JAVA_LONG);
309+
// ManifoldVec2 manifold_polygons_get_point(ManifoldPolygons* ps, size_t simple_idx, size_t pt_idx);
310+
// ManifoldVec2 = {double x, double y} = 16 bytes, returned by value
311+
load("manifold_polygons_get_point",
312+
MemoryLayout.structLayout(ValueLayout.JAVA_DOUBLE, ValueLayout.JAVA_DOUBLE),
313+
ValueLayout.ADDRESS, ValueLayout.JAVA_LONG, ValueLayout.JAVA_LONG);
310314
// ManifoldPolygons* manifold_alloc_polygons();
311315
load("manifold_alloc_polygons", ValueLayout.ADDRESS);
312316

@@ -388,8 +392,11 @@ public ManifoldBindings(File cacheDirectory) throws Exception {
388392
load("manifold_is_empty", ValueLayout.JAVA_INT, ValueLayout.ADDRESS);
389393

390394
// size_t manifold_manifold_vec_size(); Should return 8
391-
load("manifold_manifold_vec_size", ValueLayout.JAVA_LONG, ValueLayout.ADDRESS);
392-
395+
//load("manifold_manifold_vec_size", ValueLayout.JAVA_LONG, ValueLayout.ADDRESS);
396+
// REPLACE WITH (no argument — it's a sizeof-style function):
397+
load("manifold_manifold_vec_size", ValueLayout.JAVA_LONG);
398+
// AND add the length function for actual runtime vec count:
399+
load("manifold_manifold_vec_length", ValueLayout.JAVA_LONG, ValueLayout.ADDRESS);
393400
// ManifoldManifold* manifold_manifold_vec_get(void* mem, ManifoldManifoldVec*
394401
// ms, size_t idx);
395402
load("manifold_manifold_vec_get", ValueLayout.ADDRESS, ValueLayout.ADDRESS, ValueLayout.JAVA_LONG);
@@ -545,42 +552,38 @@ public MemorySegment hullPoints(ArrayList<double[]> points) throws Throwable {
545552
}
546553
}
547554

548-
/**
549-
* Slices the manifold at the given Z height, returning the cross-section as a
550-
* list of polygon contours. Each contour is a list of (x, y) vertex pairs
551-
* representing one closed loop of the cross-section. Outer contours are
552-
* wound counter-clockwise; holes are wound clockwise.
553-
*
554-
* @param m the manifold to slice
555-
* @param height Z height at which to slice
556-
* @return list of contours, each contour is a double[][] of [x, y] pairs
557-
* @throws Throwable on native call failure
558-
*/
559555
public ArrayList<double[][]> slice(MemorySegment m, double height) throws Throwable {
560556
MemorySegment polygons = null;
561557
try (Arena arena = Arena.ofConfined()) {
558+
// manifold_slice(void* mem, ManifoldManifold* m, double height) → ManifoldPolygons*
562559
MemorySegment mem = (MemorySegment) functions.get("manifold_alloc_polygons").invoke();
563560
polygons = (MemorySegment) functions.get("manifold_slice").invoke(mem, m, height);
564561

565-
long numContours = (long) functions.get("manifold_polygons_num_contour").invoke(polygons);
566-
ArrayList<double[][]> result = new ArrayList<double[][]>((int) numContours);
562+
// size_t manifold_polygons_length(ManifoldPolygons* ps) → number of contours
563+
long numContours = (long) functions.get("manifold_polygons_length").invoke(polygons);
564+
ArrayList<double[][]> result = new ArrayList<>((int) numContours);
567565

568566
for (int c = 0; c < numContours; c++) {
569-
long len = (long) functions.get("manifold_polygons_contour_length").invoke(polygons, c);
567+
// size_t manifold_polygons_simple_length(ManifoldPolygons* ps, size_t idx)
568+
long len = (long) functions.get("manifold_polygons_simple_length").invoke(polygons, (long) c);
570569
double[][] contour = new double[(int) len][2];
570+
571571
for (int i = 0; i < len; i++) {
572+
// ManifoldVec2 manifold_polygons_get_point(ManifoldPolygons* ps, size_t simple_idx, size_t pt_idx)
572573
MemorySegment pt = (MemorySegment) functions.get("manifold_polygons_get_point").invoke(arena,
573-
polygons, c, i);
574+
polygons, (long) c, (long) i);
574575
contour[i][0] = pt.get(ValueLayout.JAVA_DOUBLE, 0); // x
575576
contour[i][1] = pt.get(ValueLayout.JAVA_DOUBLE, 8); // y
576577
}
577578
result.add(contour);
578579
}
579-
580580
return result;
581581
} finally {
582582
if (polygons != null) {
583-
functions.get("manifold_delete_polygons").invoke(polygons);
583+
try {
584+
functions.get("manifold_delete_polygons").invoke(polygons);
585+
} catch (Throwable ignored) {
586+
}
584587
}
585588
}
586589
}
@@ -1029,13 +1032,15 @@ public MemorySegment[] split(MemorySegment a, MemorySegment b) throws Throwable
10291032
} catch (Throwable e) {
10301033
// Clean up allocated manifolds on failure
10311034
try {
1032-
functions.get("manifold_delete_manifold").invoke(firstMem);
1035+
delete(firstMem);
10331036
} catch (Throwable ignored) {
1037+
ignored.printStackTrace();
10341038
}
10351039

10361040
try {
1037-
functions.get("manifold_delete_manifold").invoke(secondMem);
1041+
delete(secondMem);
10381042
} catch (Throwable ignored) {
1043+
ignored.printStackTrace();
10391044
}
10401045

10411046
throw e;
@@ -1070,12 +1075,9 @@ public MemorySegment boundingBox(MemorySegment m) throws Throwable {
10701075
}
10711076

10721077
public MemorySegment importMeshGL(float[] vertices, int[] triangles, long nVerts, long nTris) throws Throwable {
1073-
System.out.println("Importing mesh: " + nVerts + " vertices, " + nTris + " triangles");
10741078

1075-
// Validate input arrays match declared sizes
10761079
if (vertices.length < nVerts * 3 || triangles.length < nTris * 3) {
1077-
throw new IllegalArgumentException("Array length mismatch: vertices=" + vertices.length + " (expected "
1078-
+ (nVerts * 3) + "), triangles=" + triangles.length + " (expected " + (nTris * 3) + ")");
1080+
throw new IllegalArgumentException("Array length mismatch");
10791081
}
10801082

10811083
MethodHandle mh = functions.get("manifold_meshgl");
@@ -1084,79 +1086,53 @@ public MemorySegment importMeshGL(float[] vertices, int[] triangles, long nVerts
10841086

10851087
try (Arena tempArena = Arena.ofConfined()) {
10861088

1087-
// Copy vertices
10881089
MemorySegment vertPtr = tempArena.allocate(nVerts * 3 * Float.BYTES);
10891090
vertPtr.copyFrom(MemorySegment.ofArray(vertices));
10901091

1091-
// Copy triangles
10921092
MemorySegment triPtr = tempArena.allocate(nTris * 3 * Integer.BYTES);
10931093
triPtr.copyFrom(MemorySegment.ofArray(triangles));
10941094

1095-
// Allocate MeshGL (native heap - must track for cleanup)
1096-
MemorySegment meshGLmem = (MemorySegment) functions.get("manifold_alloc_meshgl").invoke();
1097-
MemorySegment meshGL = null;
1098-
1099-
try { // Create MeshGL
1095+
// Allocate both up front so cleanup is uniform
1096+
MemorySegment meshGLMem = (MemorySegment) functions.get("manifold_alloc_meshgl").invoke();
11001097

1101-
meshGL = (MemorySegment) mh.invoke(meshGLmem, vertPtr, nVerts, 3L, triPtr, nTris);
1102-
1103-
// Merge (native heap - must track for cleanup)
1104-
MemorySegment mergedMem = (MemorySegment) functions.get("manifold_alloc_meshgl").invoke();
1105-
MemorySegment merged = null;
1106-
1107-
try {
1108-
merged = (MemorySegment) functions.get("manifold_meshgl_merge").invoke(mergedMem, meshGL);
1109-
1110-
// Create Manifold
1111-
MemorySegment manMem = (MemorySegment) functions.get("manifold_alloc_manifold").invoke();
1112-
MemorySegment manifold = (MemorySegment) functions.get("manifold_of_meshgl").invoke(manMem, merged);
1113-
1114-
// Success path: cleanup intermediates, return result
1115-
try {
1116-
functions.get("manifold_delete_meshgl").invoke(merged);
1117-
} catch (Throwable ignored) {
1118-
}
1119-
1120-
try {
1121-
functions.get("manifold_delete_meshgl").invoke(meshGL);
1122-
} catch (Throwable ignored) {
1123-
}
1124-
1125-
return manifold;
1126-
1127-
} catch (Throwable e) { // Cleanup merged on failure
1128-
1129-
if (merged != null) {
1130-
try {
1131-
functions.get("manifold_delete_meshgl").invoke(merged);
1132-
} catch (Throwable ignored) {
1133-
}
1134-
} else {
1135-
try {
1136-
functions.get("manifold_delete_meshgl").invoke(mergedMem);
1137-
} catch (Throwable ignored) {
1138-
}
1139-
}
1140-
throw e;
1141-
}
1142-
1143-
} catch (Throwable e) {// Cleanup meshGL on failure
1098+
MemorySegment mergedMem = (MemorySegment) functions.get("manifold_alloc_meshgl").invoke();
1099+
MemorySegment merged = null;
1100+
MemorySegment meshGL = null;
1101+
MemorySegment manMem = null;
1102+
MemorySegment manifold = null;
1103+
try {
1104+
// Both of these likely return the same pointer they were given
1105+
meshGL = (MemorySegment) mh.invoke(meshGLMem, vertPtr, nVerts, 3L, triPtr, nTris);
1106+
merged = (MemorySegment) functions.get("manifold_meshgl_merge").invoke(mergedMem, meshGL);
1107+
1108+
manMem = (MemorySegment) functions.get("manifold_alloc_manifold").invoke();
1109+
manifold = (MemorySegment) functions.get("manifold_of_meshgl").invoke(manMem, merged);
1110+
// System.out.println("meshGLMem addr: " + meshGLMem.address());
1111+
// System.out.println("meshGL addr: " + meshGL.address());
1112+
// System.out.println("mergedMem addr: " + mergedMem.address());
1113+
// System.out.println("merged addr: " + merged.address());
1114+
// System.out.println("manMem addr: " + manMem.address());
1115+
// System.out.println("manifold addr: " + manifold.address());
1116+
return manifold;
1117+
1118+
} finally {
1119+
// Collect unique addresses to delete
1120+
Set<Long> freed = new HashSet<>();
1121+
for (MemorySegment seg : new MemorySegment[] { merged, meshGL }) {
1122+
if (seg == null)
1123+
continue;
1124+
if (seg.address() == manifold.address())
1125+
continue;
1126+
if (freed.add(seg.address())) { // add() returns false if already present
1127+
//System.out.print("\nFreeing: " + seg.address());
1128+
functions.get("manifold_delete_meshgl").invoke(seg);
1129+
//System.out.print(" done\n ");
11441130

1145-
if (meshGL != null) {
1146-
try {
1147-
functions.get("manifold_delete_meshgl").invoke(meshGL);
1148-
} catch (Throwable ignored) {
1149-
}
1150-
} else {
1151-
try {
1152-
functions.get("manifold_delete_meshgl").invoke(meshGLmem);
1153-
} catch (Throwable ignored) {
11541131
}
11551132
}
1156-
throw e;
1133+
freed.clear();
11571134
}
1158-
1159-
} // tempArena closed here, vertPtr and triPtr freed automatically
1135+
}
11601136
}
11611137

11621138
public MeshData exportMeshGL(MemorySegment manifold) throws Throwable {
@@ -1332,8 +1308,16 @@ public long estimateMemoryBytes(MemorySegment m) throws Throwable {
13321308
return manifoldHandle + internalTotal + exportTotal + bvhOverhead;
13331309
}
13341310

1311+
//private HashMap<MemorySegment,Exception> deleted = new HashMap<>();
1312+
13351313
public void delete(MemorySegment manifold) throws Throwable {
1314+
// if (deleted.containsKey(manifold)) {
1315+
// System.err.println("Memory was deleted at ");
1316+
// deleted.get(manifold).printStackTrace();
1317+
// throw new DoubleFreeException();
1318+
// }
13361319
functions.get("manifold_delete_manifold").invoke(manifold);
1320+
//deleted.put(manifold, new Exception());
13371321
}
13381322

13391323
// Null-safe delete
@@ -1735,7 +1719,7 @@ private static List<RawMesh> parseModelXml(String xml) {
17351719
verts.add(attrFloat(tag, "x"));
17361720
verts.add(attrFloat(tag, "y"));
17371721
verts.add(attrFloat(tag, "z"));
1738-
} else if (tris != null && tag.startsWith("triangle")) {
1722+
} else if (tris != null && tag.startsWith("triangle") && !tag.startsWith("triangles")) {
17391723
// <triangle v1=… v2=… v3=…/> — local indices, no offset needed
17401724
tris.add(attrInt(tag, "v1"));
17411725
tris.add(attrInt(tag, "v2"));

0 commit comments

Comments
 (0)