Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 14 additions & 13 deletions examples/starrect.pics
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,32 @@
IO :: import std.io
Virtual :: import std.virtual
Render :: import piccode.render
Pen :: import piccode.pen

drawStar :: (ctx, padding=0) =
ctx
|> Render::drawLine(50+padding, 50+padding, 150-padding, 150-padding)
|> Render::drawLine(150-padding, 50+padding, 50+padding, 150-padding)
|> Render::drawLine(50+padding, 100, 150-padding, 100)
|> Render::drawLine(100, 50+padding, 100, 150-padding)
|> Pen::drawLine(50+padding, 50+padding, 150-padding, 150-padding)
|> Pen::drawLine(150-padding, 50+padding, 50+padding, 150-padding)
|> Pen::drawLine(50+padding, 100, 150-padding, 100)
|> Pen::drawLine(100, 50+padding, 100, 150-padding)

drawRect :: (ctx, x, y, w, h) =
ctx
|> Render::drawLine(x, y, x + w, y)
|> Render::drawLine(x, y + h, x + w, y + h)
|> Render::drawLine(x, y, x, y + w)
|> Render::drawLine(x + w, y, x + w, y + h)
|> Pen::drawLine(x, y, x + w, y)
|> Pen::drawLine(x, y + h, x + w, y + h)
|> Pen::drawLine(x, y, x, y + w)
|> Pen::drawLine(x + w, y, x + w, y + h)
Comment on lines +16 to +19
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Bug: drawRect uses y + w instead of y + h for the left side

This draws a too-tall left edge when w != h.

Apply this minimal fix:

-   |> Pen::drawLine(x, y, x, y + w)
+   |> Pen::drawLine(x, y, x, y + h)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
|> Pen::drawLine(x, y, x + w, y)
|> Pen::drawLine(x, y + h, x + w, y + h)
|> Pen::drawLine(x, y, x, y + w)
|> Pen::drawLine(x + w, y, x + w, y + h)
|> Pen::drawLine(x, y, x + w, y)
|> Pen::drawLine(x, y + h, x + w, y + h)
- |> Pen::drawLine(x, y, x, y + w)
+ |> Pen::drawLine(x, y, x, y + h)
|> Pen::drawLine(x + w, y, x + w, y + h)
🤖 Prompt for AI Agents
In examples/starrect.pics around lines 16 to 19, the drawRect function
incorrectly uses y + w instead of y + h for the left side line, causing the left
edge to be too tall when width and height differ. Change the third drawLine call
to use y + h instead of y + w to correctly draw the rectangle's left edge.


drawDepth :: (ctx) =
ctx
|> Render::drawLine(150, 50, 200, 70)
|> Render::drawLine(150, 150, 200, 170)
|> Render::drawLine(50, 150, 100, 170)
|> Pen::drawLine(150, 50, 200, 70)
|> Pen::drawLine(150, 150, 200, 170)
|> Pen::drawLine(50, 150, 100, 170)

drawBackLines :: (ctx) =
ctx
|> Render::drawLine(100, 170, 200, 170)
|> Render::drawLine(200, 70, 200, 170)
|> Pen::drawLine(100, 170, 200, 170)
|> Pen::drawLine(200, 70, 200, 170)

main :: () =
let
Expand Down
39 changes: 39 additions & 0 deletions piccode/context/ctx.pics
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@

// Module: Context
// Contains functions for creating contexts
Context :: module {

// Function: fromRect
// Creates a new context based on the one provided
//
// Parameters:
// ctx - (Reference) The graphic context
// x - (Number) The x position
// y - (Number) The y position
// w - (Number) The width
// h - (Number) The height
//
// Returns:
// - (Reference) A new graphics sub-context derived from the given context.
fromRect :: (ctx, x, y, w, h) = pic_nat_gfx_from_rect(ctx, x, y, w, h)

// Function: from
// Creates a new context based on the one provided
//
// Parameters:
// ctx - (Reference) The graphic context
//
// Returns:
// - (Reference) A new graphics context derived from the given context.
from :: (ctx) = pic_nat_gfx_from(ctx)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Critical: wrong native call and syntax in Context.from

Should be from :: (ctx) (space) and call the matching native pic_nat_from, not pic_nat_from_rect.

-  from:: (ctx) = pic_nat_from_rect(ctx)
+  from :: (ctx) = pic_nat_from(ctx)

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In piccode/context/ctx.pics at line 29, the Context.from function uses the wrong
native call and incorrect syntax. Change the function signature to use a space
as in `from :: (ctx)` and replace the native call from `pic_nat_from_rect` to
the correct `pic_nat_from`.

// Function: drop
// Frees the given context
//
// Parameters:
// ctx - (Reference) The graphic context
//
// Returns:
// - (Unit) Nothing
drop :: (ctx) = pic_nat_gfx_(ctx)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Critical: wrong native call in drop()

drop :: (ctx) = pic_nat_gfx_(ctx) is missing the function suffix. It should call gfx_drop.

Apply this fix:

-  drop :: (ctx) = pic_nat_gfx_(ctx)
+  drop :: (ctx) = pic_nat_gfx_drop(ctx)

Run to verify symbol consistency:

I can push a commit to fix this and add a quick example using drop() if you want.


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Pic scripts referencing pic_nat_gfx_*:"
rg -n "pic_nat_gfx_" -- piccode || true

echo "Native functions declared (Java):"
rg -n 'NativeFunctionFactory\.create\("gfx_[a-z_]+"' -- src/main/java || true

echo "Check for missing pic_nat_gfx_drop references:"
rg -n "pic_nat_gfx_drop" -- piccode || true

Length of output: 1141


Critical: fix native drop call in ctx.pics

The drop binding is calling a non-existent native symbol (pic_nat_gfx_). It needs the drop suffix to match the Java declaration (gfx_drop).

Location: piccode/context/ctx.pics:38

-  drop :: (ctx) = pic_nat_gfx_(ctx)
+  drop :: (ctx) = pic_nat_gfx_drop(ctx)

This aligns with the native function created in PiccodeGfxModule.java.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
drop :: (ctx) = pic_nat_gfx_(ctx)
drop :: (ctx) = pic_nat_gfx_drop(ctx)
🤖 Prompt for AI Agents
In piccode/context/ctx.pics at line 38, the drop binding incorrectly calls the
native symbol pic_nat_gfx_ which does not exist. Update the call to include the
drop suffix, changing it to pic_nat_gfx_drop to match the native Java
declaration in PiccodeGfxModule.java.

}
63 changes: 63 additions & 0 deletions piccode/pen/draw.pics
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@

Pen :: module {

// Function: drawLine
// Draws a line inside the specified context, based on the provided coordinates
//
// Parameters:
// ctx - (Reference) The graphic context
// startx - (Number) The start x position
// starty - (Number) The start y position
// endx - (Number) The end x position
// endy - (Number) The end y position
//
// Returns:
// - (Reference) A modified context with the rendered element.
drawLine :: (ctx, startx, starty, endx, endy) = pic_nat_draw_line(ctx, startx, starty, endx, endy)

// Function: drawRect
// Draws a rect inside the specified context, based on the provided coordinates
//
// Parameters:
// ctx - (Reference) The graphic context
// x - (Number) The x position
// y - (Number) The y position
// w - (Number) The width
// h - (Number) The height
//
// Returns:
// - (Reference) A modified context with the rendered element.
drawRect :: (ctx, x, y, w, h) = pic_nat_draw_rect(ctx, x, y, w, h)

// Function: drawRoundRect
// Draws a rounded rectangle inside the specified context, based on the provided coordinates
//
// Parameters:
// ctx - (Reference) The graphic context
// x - (Number) The x position
// y - (Number) The y position
// w - (Number) The width
// h - (Number) The height
// aw - (Number) The arc width
// ah - (Number) The arc height
//
// Returns:
// - (Reference) A modified context with the rendered element.
drawRoundRect :: (ctx, x, y, w, h, aw, ah) = pic_nat_draw_round_rect(ctx, x, y, w, h, aw, ah)

// Function: drawOval
// Draws a rect inside the specified context, based on the provided coordinates
//
// Parameters:
// ctx - (Reference) The graphic context
// x - (Number) The x position
// y - (Number) The y position
// w - (Number) The width
// h - (Number) The height
//
// Returns:
// - (Reference) A modified context with the rendered element.
drawOval :: (ctx, x, y, w, h) = pic_nat_draw_oval(ctx, x, y, w, h)

}

15 changes: 1 addition & 14 deletions piccode/render/context.pics
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,8 @@ Render :: module {
// None
//
// Returns:
// - (Object) an handler to the Graphics2D.
// - (Reference) a handler to the Graphics2D.
getContext :: () = pic_nat_get_gfx()

// Function: drawLine
// Draws a line inside the specified context, based on the provided coordinates
//
// Parameters:
// ctx - (Object) The graphic context
// startx - (Number) The start x position
// starty - (Number) The start y position
// endx - (Number) The end x position
// endy - (Number) The end y position
//
// Returns:
// - (Object) A modified context with the rendered element.
drawLine :: (ctx, startx, starty, endx, endy) = pic_nat_draw_line(ctx, startx, starty, endx, endy)
}

2 changes: 1 addition & 1 deletion src/main/java/org/editor/menu/Menus.java
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ private static void addEditMenu(JMenuBar menu_bar) {
}

private static void addNavigateMenu(JMenuBar menu_bar) {
JMenu navMenu = new JMenu("Navigage");
JMenu navMenu = new JMenu("Navigate");

JMenu tabs = new JMenu("Tabs");
tabs.setIcon(Icons.getIcon("layout"));
Expand Down
110 changes: 64 additions & 46 deletions src/main/java/org/editor/nativemods/PiccodeGfxModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.piccode.rt.PiccodeException;
import org.piccode.rt.PiccodeNumber;
import org.piccode.rt.PiccodeObject;
import org.piccode.rt.PiccodeReference;
import org.piccode.rt.PiccodeString;
import org.piccode.rt.PiccodeUnit;
import org.piccode.rt.PiccodeValue;
Expand All @@ -24,65 +25,82 @@ public class PiccodeGfxModule {
public static void addFunctions() {
NativeFunctionFactory.create("get_gfx", List.of(), (args, namedArgs, frame) -> {
var gfx = CanvasFrame.gfx;
var obj = Context.getObject(gfx.hashCode());
if (obj == null) {
Context.allocate(gfx);
return makeObj(gfx);
}
return makeObj(gfx);
return new PiccodeReference(gfx);
}, null);
Comment on lines 26 to 29
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Guard get_gfx against null and surface a clear error

If CanvasFrame.gfx is null, returning a reference will fail later with a confusing type error. Retrieve caller info and throw a clear message when uninitialized.

Proposed change:

 NativeFunctionFactory.create("get_gfx", List.of(), (args, namedArgs, frame) -> {
-    var gfx = CanvasFrame.gfx;
-    return new PiccodeReference(gfx);
+    var gfx = CanvasFrame.gfx;
+    var ctx = frame == null ? Context.top : Context.getContextAt(frame);
+    var caller = ctx.getTopFrame().caller;
+    if (gfx == null) {
+        throw new PiccodeException(caller.file, caller.line, caller.column,
+            "Graphics is not initialized (CanvasFrame.gfx is null)");
+    }
+    return new PiccodeReference(gfx);
 }, null);

I need to confirm that the helper classes and methods used in the proposed guard (Context, getContextAt, getTopFrame, caller, and the PiccodeException constructor signature) actually exist and match this usage. Let me verify their definitions.


🏁 Script executed:

#!/bin/bash
# Locate CanvasFrame and its gfx field
rg -n "class CanvasFrame" || echo "CanvasFrame class not found"
rg -n "gfx" src

# Check for a Context class and its APIs
rg -n "class Context" || echo "Context class not found"
rg -n "getContextAt" -n src || echo "getContextAt not found"
rg -n "top" -n src || echo "Context\\.top not found"
rg -n "getTopFrame" -n src || echo "getTopFrame not found"
rg -n "caller" -n src || echo "caller field not found"

# Inspect PiccodeException constructors
rg -n "class PiccodeException" -n src || echo "PiccodeException not found"
rg -n "new PiccodeException" -n src

Length of output: 14658


Guard get_gfx against uninitialized CanvasFrame.gfx

Add the same context–caller lookup and throw a clear PiccodeException when CanvasFrame.gfx is null, mirroring the other native gfx APIs.

• File: src/main/java/org/editor/nativemods/PiccodeGfxModule.java
• Lines: 26–29

   NativeFunctionFactory.create("get_gfx", List.of(), (args, namedArgs, frame) -> {
-       var gfx = CanvasFrame.gfx;
-       return new PiccodeReference(gfx);
+       var gfx = CanvasFrame.gfx;
+       // derive caller location for proper error reporting
+       var ctx    = frame == null ? Context.top : Context.getContextAt(frame);
+       var caller = ctx.getTopFrame().caller;
+       if (gfx == null) {
+           throw new PiccodeException(
+               caller.file, caller.line, caller.column,
+               "Graphics is not initialized (CanvasFrame.gfx is null)"
+           );
+       }
+       return new PiccodeReference(gfx);
   }, null);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
NativeFunctionFactory.create("get_gfx", List.of(), (args, namedArgs, frame) -> {
var gfx = CanvasFrame.gfx;
var obj = Context.getObject(gfx.hashCode());
if (obj == null) {
Context.allocate(gfx);
return makeObj(gfx);
}
return makeObj(gfx);
return new PiccodeReference(gfx);
}, null);
NativeFunctionFactory.create("get_gfx", List.of(), (args, namedArgs, frame) -> {
var gfx = CanvasFrame.gfx;
// derive caller location for proper error reporting
var ctx = frame == null ? Context.top : Context.getContextAt(frame);
var caller = ctx.getTopFrame().caller;
if (gfx == null) {
throw new PiccodeException(
caller.file, caller.line, caller.column,
"Graphics is not initialized (CanvasFrame.gfx is null)"
);
}
return new PiccodeReference(gfx);
}, null);
🤖 Prompt for AI Agents
In src/main/java/org/editor/nativemods/PiccodeGfxModule.java lines 26 to 29, the
get_gfx native function does not check if CanvasFrame.gfx is null before
returning it. Add a guard to check if CanvasFrame.gfx is null, and if so,
retrieve the caller context and throw a PiccodeException with a clear error
message indicating that the graphics context is uninitialized. This should match
the error handling pattern used in other native gfx APIs.


NativeFunctionFactory.create("draw_line", List.of("ctx", "x1", "y1", "x2", "y2"), (args, namedArgs, frame) -> {
NativeFunctionFactory.create("gfx_from_rect", List.of("ctx", "x", "y", "w", "h"), (args, namedArgs, frame) -> {
var _ctx = namedArgs.get("ctx");
var _x1 = namedArgs.get("x1");
var _y1 = namedArgs.get("y1");
var _x2 = namedArgs.get("x2");
var _y2 = namedArgs.get("y2");

var ctx = frame == null ?
Context.top
: Context.getContextAt(frame);
var x = namedArgs.get("x");
var y = namedArgs.get("y");
var w = namedArgs.get("w");
var h = namedArgs.get("h");

var ctx = frame == null
? Context.top
: Context.getContextAt(frame);
var caller = ctx.getTopFrame().caller;

PiccodeValue.verifyType(caller, _ctx, Type.OBJECT);
PiccodeValue.verifyType(caller, _x1, Type.NUMBER);
PiccodeValue.verifyType(caller, _y1, Type.NUMBER);
PiccodeValue.verifyType(caller, _x2, Type.NUMBER);
PiccodeValue.verifyType(caller, _y2, Type.NUMBER);

var obj = (PiccodeObject) _ctx;
var map = obj.obj;
if (!map.containsKey("hash")) {
throw new PiccodeException(caller.file, caller.line, caller.column, "Context is not an object");
}

var _hash = map.get("hash");
PiccodeValue.verifyType(caller, _hash, Type.NUMBER);
var hash = (int) (double) ((PiccodeNumber) _hash).raw();
var _gfx = Context.getObject(hash);
if (_gfx == null) {
throw new PiccodeException(caller.file, caller.line, caller.column, "Context is not allocated");

PiccodeValue.verifyType(caller, _ctx, Type.REFERENCE);
PiccodeValue.verifyType(caller, x, Type.NUMBER);
PiccodeValue.verifyType(caller, y, Type.NUMBER);
PiccodeValue.verifyType(caller, w, Type.NUMBER);
PiccodeValue.verifyType(caller, h, Type.NUMBER);

var obj = (PiccodeReference) _ctx;
var _gfx = obj.deref();
if (!(_gfx instanceof Graphics2D)) {
throw new PiccodeException(caller.file, caller.line, caller.column, "Context is not a correct object. Expected Graphics2D");
}

var gfx = (Graphics2D) _gfx;
var _x = (int) (double) ((PiccodeNumber) x).raw();
var _y = (int) (double) ((PiccodeNumber) y).raw();
var _w = (int) (double) ((PiccodeNumber) w).raw();
var _h = (int) (double) ((PiccodeNumber) h).raw();

var gfx2 = (Graphics2D) gfx.create(_x, _y, _w, _h);
return new PiccodeReference(gfx2);
}, null);
Comment on lines +31 to +63
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Validate rectangle dimensions before creating sub-contexts

Negative or zero width/height can cause runtime errors or undefined behavior in Graphics2D#create(x, y, w, h). Validate early and provide a precise error.

Apply after the numeric casts:

 var _x = (int) (double) ((PiccodeNumber) x).raw();
 var _y = (int) (double) ((PiccodeNumber) y).raw();
 var _w = (int) (double) ((PiccodeNumber) w).raw();
 var _h = (int) (double) ((PiccodeNumber) h).raw();
 
+if (_w <= 0 || _h <= 0) {
+    throw new PiccodeException(caller.file, caller.line, caller.column,
+        "Width and height must be positive. Got w=" + _w + ", h=" + _h);
+}
 var gfx2 = (Graphics2D) gfx.create(_x, _y, _w, _h);
 return new PiccodeReference(gfx2);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
NativeFunctionFactory.create("gfx_from_rect", List.of("ctx", "x", "y", "w", "h"), (args, namedArgs, frame) -> {
var _ctx = namedArgs.get("ctx");
var _x1 = namedArgs.get("x1");
var _y1 = namedArgs.get("y1");
var _x2 = namedArgs.get("x2");
var _y2 = namedArgs.get("y2");
var ctx = frame == null ?
Context.top
: Context.getContextAt(frame);
var x = namedArgs.get("x");
var y = namedArgs.get("y");
var w = namedArgs.get("w");
var h = namedArgs.get("h");
var ctx = frame == null
? Context.top
: Context.getContextAt(frame);
var caller = ctx.getTopFrame().caller;
PiccodeValue.verifyType(caller, _ctx, Type.OBJECT);
PiccodeValue.verifyType(caller, _x1, Type.NUMBER);
PiccodeValue.verifyType(caller, _y1, Type.NUMBER);
PiccodeValue.verifyType(caller, _x2, Type.NUMBER);
PiccodeValue.verifyType(caller, _y2, Type.NUMBER);
var obj = (PiccodeObject) _ctx;
var map = obj.obj;
if (!map.containsKey("hash")) {
throw new PiccodeException(caller.file, caller.line, caller.column, "Context is not an object");
}
var _hash = map.get("hash");
PiccodeValue.verifyType(caller, _hash, Type.NUMBER);
var hash = (int) (double) ((PiccodeNumber) _hash).raw();
var _gfx = Context.getObject(hash);
if (_gfx == null) {
throw new PiccodeException(caller.file, caller.line, caller.column, "Context is not allocated");
PiccodeValue.verifyType(caller, _ctx, Type.REFERENCE);
PiccodeValue.verifyType(caller, x, Type.NUMBER);
PiccodeValue.verifyType(caller, y, Type.NUMBER);
PiccodeValue.verifyType(caller, w, Type.NUMBER);
PiccodeValue.verifyType(caller, h, Type.NUMBER);
var obj = (PiccodeReference) _ctx;
var _gfx = obj.deref();
if (!(_gfx instanceof Graphics2D)) {
throw new PiccodeException(caller.file, caller.line, caller.column, "Context is not a correct object. Expected Graphics2D");
}
var gfx = (Graphics2D) _gfx;
var _x = (int) (double) ((PiccodeNumber) x).raw();
var _y = (int) (double) ((PiccodeNumber) y).raw();
var _w = (int) (double) ((PiccodeNumber) w).raw();
var _h = (int) (double) ((PiccodeNumber) h).raw();
var gfx2 = (Graphics2D) gfx.create(_x, _y, _w, _h);
return new PiccodeReference(gfx2);
}, null);
var _x = (int) (double) ((PiccodeNumber) x).raw();
var _y = (int) (double) ((PiccodeNumber) y).raw();
var _w = (int) (double) ((PiccodeNumber) w).raw();
var _h = (int) (double) ((PiccodeNumber) h).raw();
if (_w <= 0 || _h <= 0) {
throw new PiccodeException(caller.file, caller.line, caller.column,
"Width and height must be positive. Got w=" + _w + ", h=" + _h);
}
var gfx2 = (Graphics2D) gfx.create(_x, _y, _w, _h);
return new PiccodeReference(gfx2);
🤖 Prompt for AI Agents
In src/main/java/org/editor/nativemods/PiccodeGfxModule.java between lines 31
and 63, after casting width and height to integers, add validation to check if
_w and _h are greater than zero. If either is zero or negative, throw a
PiccodeException with a clear message indicating invalid rectangle dimensions.
This prevents runtime errors from passing invalid dimensions to
Graphics2D#create.

NativeFunctionFactory.create("gfx_from", List.of("ctx"), (args, namedArgs, frame) -> {
var _ctx = namedArgs.get("ctx");

var ctx = frame == null
? Context.top
: Context.getContextAt(frame);
var caller = ctx.getTopFrame().caller;

PiccodeValue.verifyType(caller, _ctx, Type.REFERENCE);
var obj = (PiccodeReference) _ctx;
var _gfx = obj.deref();
if (!(_gfx instanceof Graphics2D)) {
throw new PiccodeException(caller.file, caller.line, caller.column, "Context is not a correct object. Expected Graphics2D");
}

var gfx = (Graphics2D) _gfx;
var x1 = (int) (double) ((PiccodeNumber) _x1).raw();
var y1 = (int) (double) ((PiccodeNumber) _y1).raw();
var x2 = (int) (double) ((PiccodeNumber) _x2).raw();
var y2 = (int) (double) ((PiccodeNumber) _y2).raw();

gfx.drawLine(x1, y1, x2, y2);
return obj;
var gfx2 = (Graphics2D) gfx.create();
return new PiccodeReference(gfx2);
}, null);

}
NativeFunctionFactory.create("gfx_drop", List.of("ctx"), (args, namedArgs, frame) -> {
var _ctx = namedArgs.get("ctx");

private static PiccodeValue makeObj(Graphics2D obj) {
var _obj = new HashMap<String, PiccodeValue>();
_obj.put("hash", new PiccodeNumber(obj.hashCode()));
_obj.put("class", new PiccodeString(obj.getClass().getName()));
return new PiccodeObject(_obj);
var ctx = frame == null
? Context.top
: Context.getContextAt(frame);
var caller = ctx.getTopFrame().caller;

PiccodeValue.verifyType(caller, _ctx, Type.REFERENCE);
var obj = (PiccodeReference) _ctx;
var _gfx = obj.deref();
if (!(_gfx instanceof Graphics2D)) {
throw new PiccodeException(caller.file, caller.line, caller.column, "Context is not a correct object. Expected Graphics2D");
}

var gfx = (Graphics2D) _gfx;
gfx.dispose();
return new PiccodeUnit();
}, null);
Comment on lines +85 to +103
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Prevent accidental disposal of the root graphics context

Calling gfx_drop on the root CanvasFrame.gfx can break rendering for the entire app. Guard against disposing the root and provide a targeted error.

Apply:

 var obj = (PiccodeReference) _ctx;
 var _gfx = obj.deref();
 if (!(_gfx instanceof Graphics2D)) {
   throw new PiccodeException(caller.file, caller.line, caller.column, "Context is not a correct object. Expected Graphics2D");
 }
 
 var gfx = (Graphics2D) _gfx;
-gfx.dispose();
+if (gfx == CanvasFrame.gfx) {
+  throw new PiccodeException(caller.file, caller.line, caller.column,
+    "Cannot dispose the root graphics context. Only sub-contexts created via gfx_from/gfx_from_rect should be dropped.");
+}
+gfx.dispose();
 return new PiccodeUnit();

Optional: If you prefer silent no-op instead of error, replace the throw with return new PiccodeUnit();.


🏁 Script executed:

#!/bin/bash
# Locate the CanvasFrame class definition
rg -n "class CanvasFrame" -C 5 src/main/java

# Find where a Graphics2D gfx field is declared
rg -n "Graphics2D\s.*gfx" -C 5 src/main/java

# Check for any direct references to CanvasFrame.gfx
rg -n "CanvasFrame\\.gfx" -n src/main/java

Length of output: 1772


Guard against disposing the root CanvasFrame.gfx

Before calling dispose() in gfx_drop, check if the target is the root context and error out:

File: src/main/java/org/editor/nativemods/PiccodeGfxModule.java
Lines: ~85–103

Apply:

   var gfx = (Graphics2D) _gfx;
+  if (gfx == CanvasFrame.gfx) {
+    throw new PiccodeException(caller.file, caller.line, caller.column,
+      "Cannot dispose the root graphics context. Only sub-contexts created via gfx_from/gfx_from_rect should be dropped.");
+  }
   gfx.dispose();
   return new PiccodeUnit();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
NativeFunctionFactory.create("gfx_drop", List.of("ctx"), (args, namedArgs, frame) -> {
var _ctx = namedArgs.get("ctx");
private static PiccodeValue makeObj(Graphics2D obj) {
var _obj = new HashMap<String, PiccodeValue>();
_obj.put("hash", new PiccodeNumber(obj.hashCode()));
_obj.put("class", new PiccodeString(obj.getClass().getName()));
return new PiccodeObject(_obj);
var ctx = frame == null
? Context.top
: Context.getContextAt(frame);
var caller = ctx.getTopFrame().caller;
PiccodeValue.verifyType(caller, _ctx, Type.REFERENCE);
var obj = (PiccodeReference) _ctx;
var _gfx = obj.deref();
if (!(_gfx instanceof Graphics2D)) {
throw new PiccodeException(caller.file, caller.line, caller.column, "Context is not a correct object. Expected Graphics2D");
}
var gfx = (Graphics2D) _gfx;
gfx.dispose();
return new PiccodeUnit();
}, null);
NativeFunctionFactory.create("gfx_drop", List.of("ctx"), (args, namedArgs, frame) -> {
var _ctx = namedArgs.get("ctx");
var ctx = frame == null
? Context.top
: Context.getContextAt(frame);
var caller = ctx.getTopFrame().caller;
PiccodeValue.verifyType(caller, _ctx, Type.REFERENCE);
var obj = (PiccodeReference) _ctx;
var _gfx = obj.deref();
if (!(_gfx instanceof Graphics2D)) {
throw new PiccodeException(caller.file, caller.line, caller.column, "Context is not a correct object. Expected Graphics2D");
}
var gfx = (Graphics2D) _gfx;
if (gfx == CanvasFrame.gfx) {
throw new PiccodeException(caller.file, caller.line, caller.column,
"Cannot dispose the root graphics context. Only sub-contexts created via gfx_from/gfx_from_rect should be dropped.");
}
gfx.dispose();
return new PiccodeUnit();
}, null);
🤖 Prompt for AI Agents
In src/main/java/org/editor/nativemods/PiccodeGfxModule.java around lines 85 to
103, before calling dispose() on the Graphics2D object in the gfx_drop function,
add a check to determine if the target Graphics2D is the root CanvasFrame.gfx.
If it is the root context, throw an error instead of disposing it to prevent
invalid disposal. Implement this guard by comparing the _gfx object with the
root Graphics2D instance and conditionally throwing a PiccodeException if they
match.

}

}
Loading