Skip to content

Conversation

@hexaredecimal
Copy link
Collaborator

@hexaredecimal hexaredecimal commented Aug 8, 2025

The drawing context is now a reference value that is managed by the interpreter. This helps with reasoning about the mutable state of the context when we draw into it. This change allowed me to find a new home for common draw functions and context methods. You can now create sub contexts and render to them directly and its all tied together.

Summary by CodeRabbit

  • New Features

    • Introduced a new drawing module with functions to draw lines, rectangles, rounded rectangles, and ovals.
    • Added functions for creating and managing graphic contexts, including support for context clipping and disposal.
  • Bug Fixes

    • Corrected a typographical error in the "Navigate" menu label.
  • Refactor

    • Updated drawing functions to use the new drawing module for improved graphics handling.
    • Simplified graphics context management for more direct and consistent handling of native objects.
  • Chores

    • Improved documentation for function return types.

@hexaredecimal hexaredecimal added the enhancement New feature or request label Aug 8, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 8, 2025

Walkthrough

This set of changes introduces a new drawing module (Pen) and a new context module (Context), migrating all line drawing functionality from the previous Render module to Pen. The Java native backend is updated to support these new abstractions, with direct handling of graphics contexts via references and new native functions for drawing and context manipulation. Example code is updated to use the new Pen module.

Changes

Cohort / File(s) Change Summary
Pen Drawing Module Introduction
piccode/pen/draw.pics, src/main/java/org/editor/nativemods/PiccodePenModule.java
Introduces the Pen module with functions for drawing lines, rectangles, rounded rectangles, and ovals, both in the language and as native Java implementations.
Context Management Module
piccode/context/ctx.pics, src/main/java/org/editor/nativemods/PiccodeGfxModule.java
Adds a new Context module for creating or modifying graphics contexts, introduces corresponding native Java functions for context creation and clipping, and updates context handling to use direct references.
Render Module Refactor and Cleanup
piccode/render/context.pics
Removes the drawLine function from the Render module and updates documentation to reflect correct return types.
Example Migration to Pen
examples/starrect.pics
Updates all drawing calls to use Pen::drawLine instead of Render::drawLine in the example file.
UI Label Correction
src/main/java/org/editor/menu/Menus.java
Fixes a typo in a menu label from "Navigage" to "Navigate".
Native App Module Registration Update
src/main/java/org/piccode/piccode/Piccode.java
Registers the new PiccodePenModule native functions alongside existing native modules during application initialization.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ExampleCode
    participant PenModule
    participant NativePenFunctions
    participant Graphics2D

    User->>ExampleCode: Run drawing function
    ExampleCode->>PenModule: Pen::drawLine(ctx, x1, y1, x2, y2)
    PenModule->>NativePenFunctions: pic_nat_draw_line(ctx, x1, y1, x2, y2)
    NativePenFunctions->>Graphics2D: drawLine(x1, y1, x2, y2)
    Graphics2D-->>NativePenFunctions: (draws line)
    NativePenFunctions-->>PenModule: return ctx
    PenModule-->>ExampleCode: return ctx
Loading
sequenceDiagram
    participant User
    participant ExampleCode
    participant ContextModule
    participant NativeContextFunctions
    participant Graphics2D

    User->>ExampleCode: Request context from rect
    ExampleCode->>ContextModule: Context::fromRect(ctx, x, y, w, h)
    ContextModule->>NativeContextFunctions: pic_nat_gfx_from_rect(ctx, x, y, w, h)
    NativeContextFunctions->>Graphics2D: create(x, y, w, h)
    Graphics2D-->>NativeContextFunctions: returns new Graphics2D context
    NativeContextFunctions-->>ContextModule: return new ctx
    ContextModule-->>ExampleCode: return new ctx
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

A hop and a skip, new lines to draw,
From Render to Pen, we rabbits saw.
Contexts now clip, with references neat,
Ovals and rectangles—art’s at our feet!
Menus corrected, typos all gone,
With Pen in our paws, we sketch on and on!
🐇✏️

Note

🔌 MCP (Model Context Protocol) integration is now available in Early Access!

Pro users can now connect to remote MCP servers under the Integrations page to get reviews and chat conversations that understand additional development context.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch Sibusiso

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (9)
examples/starrect.pics (1)

16-19: Optional: simplify rectangle rendering via Pen::drawRect

Use the native rectangle primitive for clarity and potential performance.

-   |> 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::drawRect(x, y, w, h)
piccode/render/context.pics (1)

12-12: Doc nit: “an handler” ➜ “a handle”

Minor grammar fix in the return docs.

-  //   - (Reference) an handler to the Graphics2D. 
+  //   - (Reference) a handle to the Graphics2D.
piccode/context/ctx.pics (2)

17-18: Doc accuracy: clarify return value for fromRect

These functions create derived contexts, not “rendered element” results.

-  // Returns:
-  //   - (Reference) A modified context with the rendered element. 
+  // Returns:
+  //   - (Reference) A new graphics sub-context derived from the given context.

27-28: Doc accuracy: clarify return value for from

Same wording improvement as fromRect.

-  // Returns:
-  //   - (Reference) A modified context with the rendered element. 
+  // Returns:
+  //   - (Reference) A new graphics context derived from the given context.
piccode/pen/draw.pics (3)

19-20: Doc nit: “rect” ➜ “rectangle”

Minor clarity improvement.

-  // Draws a rect inside the specified context, based on the provided coordinates
+  // Draws a rectangle inside the specified context, based on the provided coordinates

33-34: Doc nit: specify “rounded rectangle”

Clarify shape name.

-  // Draws a rect inside the specified context, based on the provided coordinates
+  // Draws a rounded rectangle inside the specified context, based on the provided coordinates

49-50: Doc nit: specify “oval (ellipse)”

Clarify shape name.

-  // Draws a rect inside the specified context, based on the provided coordinates
+  // Draws an oval (ellipse) inside the specified context, based on the provided coordinates
src/main/java/org/editor/nativemods/PiccodePenModule.java (1)

52-55: Silent precision loss when casting doubles to ints

Coordinates are truncated with (int)(double); fractional values are silently lost and large doubles can overflow int. Consider using Math.round or validating the range to keep behaviour predictable.

src/main/java/org/editor/nativemods/PiccodeGfxModule.java (1)

31-60: Duplicate verbose type checks → extract helper

The four sequential verifyType calls repeat in every native. Extract a small utility (e.g., NativeChecks.expectNumber(caller, "x", x)) to cut boilerplate and reduce copy-paste errors.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between acb8fb7 and a65e357.

📒 Files selected for processing (7)
  • examples/starrect.pics (1 hunks)
  • piccode/context/ctx.pics (1 hunks)
  • piccode/pen/draw.pics (1 hunks)
  • piccode/render/context.pics (1 hunks)
  • src/main/java/org/editor/menu/Menus.java (1 hunks)
  • src/main/java/org/editor/nativemods/PiccodeGfxModule.java (2 hunks)
  • src/main/java/org/editor/nativemods/PiccodePenModule.java (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-06T23:34:19.770Z
Learnt from: hexaredecimal
PR: Glimmr-Lang/PicassoCode#26
File: tests/test_readme_validation.py:233-236
Timestamp: 2025-08-06T23:34:19.770Z
Learning: The PicassoCode project README has specific formatting requirements including: app icon at "./src/main/resources/applogo/appicon.png" with 40% width, repository URL "gitgithub.com:hexaredecimal/Piccode.git", JAR file "Piccode-1.0-SNAPSHOT-jar-with-dependencies.jar", and elaborate ASCII art with "Creativity + Logic + Math" tagline in the license section.

Applied to files:

  • src/main/java/org/editor/nativemods/PiccodePenModule.java
🧬 Code Graph Analysis (2)
src/main/java/org/editor/nativemods/PiccodePenModule.java (2)
src/main/java/org/editor/CanvasFrame.java (2)
  • CanvasFrame (41-333)
  • Override (105-127)
src/main/java/org/piccode/piccode/Piccode.java (3)
  • initializeNativeAppModules (23-25)
  • main (15-21)
  • Piccode (13-26)
src/main/java/org/editor/nativemods/PiccodeGfxModule.java (1)
src/main/java/org/piccode/piccode/Piccode.java (1)
  • initializeNativeAppModules (23-25)
🔇 Additional comments (7)
src/main/java/org/editor/menu/Menus.java (1)

121-121: Typo fix in menu label looks good

"Navigate" label is correct and aligns with the rest of the UI.

examples/starrect.pics (5)

5-5: Switched to Pen module import — OK

The example now depends on the new Pen API as intended.


9-12: Migration to Pen::drawLine is correct

Chained usage preserves the pipe style and returns ctx as expected.


23-25: OK: depth lines migrated to Pen

Coordinates preserved; chaining remains consistent.


29-30: OK: back lines migrated to Pen

All good.


5-5: No remaining Render::drawLine usages found

All searches for Render::drawLine returned zero matches, and piccode/render no longer exports drawLine. The new Pen::drawLine entry points are in place:

  • examples/starrect.pics:5 Pen :: import piccode.pen
  • piccode/pen/draw.pics:2 Pen :: module { … }
  • src/main/java/org/editor/nativemods/PiccodePenModule.java:23 public class PiccodePenModule

Nothing else needs to be migrated.

piccode/pen/draw.pics (1)

16-16: Pen::drawLine binding looks correct

Signature and return value align with piping semantics.

Comment on lines +16 to +19
|> 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)
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.

// Returns:
// - (Reference) A modified context with the rendered element.
from:: (ctx) = pic_nat_from_rect(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`.

Comment on lines +23 to +26
public class PiccodePenModule {

public static void addFunctions() {

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

Module never registered → functions unusable

PiccodePenModule.addFunctions() is not wired into Piccode.initializeNativeAppModules(), so the new draw_* natives will never be exposed. Add the registration call next to PiccodeGfxModule::addFunctions or they will fail at runtime.

🤖 Prompt for AI Agents
In src/main/java/org/editor/nativemods/PiccodePenModule.java around lines 23 to
26, the addFunctions method is defined but never registered in
Piccode.initializeNativeAppModules(), causing the draw_* native functions to be
unavailable. To fix this, locate the Piccode.initializeNativeAppModules() method
and add a call to PiccodePenModule.addFunctions() alongside the existing
PiccodeGfxModule.addFunctions() call to ensure the new natives are properly
exposed at runtime.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a65e357 and 5401717.

📒 Files selected for processing (5)
  • piccode/context/ctx.pics (1 hunks)
  • piccode/pen/draw.pics (1 hunks)
  • piccode/render/context.pics (1 hunks)
  • src/main/java/org/editor/nativemods/PiccodeGfxModule.java (2 hunks)
  • src/main/java/org/piccode/piccode/Piccode.java (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • piccode/render/context.pics
  • piccode/pen/draw.pics
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/java/org/piccode/piccode/Piccode.java (1)
src/main/java/org/editor/nativemods/PiccodePenModule.java (1)
  • PiccodePenModule (23-170)
🔇 Additional comments (4)
src/main/java/org/piccode/piccode/Piccode.java (1)

6-6: Pen native module correctly registered

Import and registration look correct and keep initialization order sensible (Gfx first, then Pen). No issues.

Also applies to: 26-26

piccode/context/ctx.pics (2)

18-18: fromRect maps correctly to native

Native call name aligns with Java side: gfx_from_rect → pic_nat_gfx_from_rect. Good.


28-28: from mapping and syntax fixed

Signature from :: (ctx) and native pic_nat_gfx_from are consistent with backend. Looks good.

src/main/java/org/editor/nativemods/PiccodeGfxModule.java (1)

64-83: LGTM: gfx_from creates a proper clone

Type checks, deref, and gfx.create() usage are correct. Return as PiccodeReference aligns with the new reference semantics.

//
// 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.

Comment on lines 26 to 29
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);
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.

Comment on lines +31 to +63
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);
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.

Comment on lines +85 to +103
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);
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.

@hexaredecimal hexaredecimal merged commit eb331b8 into main Aug 8, 2025
2 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Aug 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants