Skip to content

fix: Dynamic code now accepts bare Unity Object calls#1469

Merged
hatayama merged 4 commits into
mainfrom
fix/object-execute-code
Jul 3, 2026
Merged

fix: Dynamic code now accepts bare Unity Object calls#1469
hatayama merged 4 commits into
mainfrom
fix/object-execute-code

Conversation

@hatayama

@hatayama hatayama commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Summary

  • Dynamic code snippets can now use bare Object.Instantiate and related Unity object calls without qualifying UnityEngine.Object.
  • Existing custom Object aliases are respected, so snippets that define their own alias do not get duplicate using directives.

User Impact

  • Before this change, snippets could fail with an ambiguous Object reference because the wrapper imported both System and UnityEngine.
  • After this change, common Unity-style snippets using Object compile as expected in script mode.

Changes

  • Add a default Object = UnityEngine.Object alias to wrapped script snippets.
  • Skip alias injection when a snippet already declares an Object alias.
  • Add tests for alias injection, duplicate prevention, and custom alias preservation.

Verification

  • uloop compile
  • uloop run-tests --test-mode EditMode --filter-type regex --filter-value "DynamicCodeSourcePreparerTests"
  • uloop execute-dynamic-code --compile-only true --code "GameObject go = new GameObject(\"compile-only\"); Object.Instantiate(go); return null;"

Review in cubic

This PR updates dynamic code preparation/wrapping so Unity-style calls like Object.Instantiate work without requiring UnityEngine.Object, while preventing ambiguous Object references with System.Object.

What changed

  • SourceShaper.Analyze now centralizes using-directive collection via a helper that also extracts alias declarations from using ... = Alias (supports optional global, inline comments, and verbatim identifier aliases like using @object = ...).
  • The analysis result now includes AliasedNames, which is passed through DynamicCodeSourcePreparer into WrapperTemplate.Build.
  • WrapperTemplate.Build now accepts aliasedNames and injects default wrapper aliases only when the corresponding alias is requested/needed (currently:
    • using Object = UnityEngine.Object;
    • using Random = UnityEngine.Random;),
      while using the parsed alias set to avoid duplicate injection when the user already defines Object (including verbatim @Object) or Random.
  • SourceShaper also refactors comment-aware scanning used during parsing/advancing, improving robustness for alias detection around comments.

Tests

  • Added/extended NUnit coverage for wrapper alias injection and duplicate prevention in DynamicCodeSourcePreparerTests:
    • inject using Object = UnityEngine.Object; when the snippet uses bare Object
    • avoid duplicating the alias when using Object = UnityEngine.Object; already exists
    • preserve user-defined aliases like using Object = System.Object;
    • respect verbatim aliases like using @object = System.Object;
    • inject using Random = UnityEngine.Random; when bare Random is used
  • Added DynamicCodeTestStringUtility.CountSubstring and used it to safely assert substring counts (including returning 0 for empty targets).
  • Expanded SourceShaperTests to verify alias extraction correctness, including global using with comments and verbatim alias syntax.
  • Updated existing wrapper/pre-using resolver tests to match the new WrapperTemplate.Build signature (including added empty alias-collection arguments).

Add a UnityEngine.Object alias to wrapped execute-dynamic-code snippets so bare Object.Instantiate and related Unity APIs do not collide with System.Object. Preserve user-provided Object aliases to avoid duplicate using directives.
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3d41aea2-3166-45cc-ae81-93839e417314

📥 Commits

Reviewing files that changed from the base of the PR and between 2ce5a96 and 77ed877.

📒 Files selected for processing (4)
  • Assets/Tests/Editor/DynamicCodeToolTests/DynamicCodeSourcePreparerTests.cs
  • Assets/Tests/Editor/DynamicCodeToolTests/SourceShaperTests.cs
  • Packages/src/Editor/Compilation/SourceShaper.cs
  • Packages/src/Editor/Compilation/WrapperTemplate.cs
💤 Files with no reviewable changes (1)
  • Assets/Tests/Editor/DynamicCodeToolTests/DynamicCodeSourcePreparerTests.cs
🚧 Files skipped from review as they are similar to previous changes (2)
  • Packages/src/Editor/Compilation/WrapperTemplate.cs
  • Packages/src/Editor/Compilation/SourceShaper.cs

📝 Walkthrough

Walkthrough

Adds alias tracking to source shaping, passes collected alias names into wrapper generation, and updates tests for default Unity alias injection, alias preservation, and the revised wrapper call signature.

Changes

Dynamic code alias propagation

Layer / File(s) Summary
Alias extraction in source shaping
Packages/src/Editor/Compilation/SourceShaper.cs
Analyze now registers using directives through a shared helper, extracts alias names from normal, verbatim, and global using forms, and stores them on SourceShapeResult.
Alias-aware wrapper generation
Packages/src/Editor/Compilation/DynamicCodeSourcePreparer.cs, Packages/src/Editor/Compilation/WrapperTemplate.cs
Prepare passes collected alias names into WrapperTemplate.Build, and WrapperTemplate adds default alias mappings plus helper logic to emit matching using ... = ...; directives before caller-supplied directives.
Updated tests for alias behavior
Assets/Tests/Editor/DynamicCodeToolTests/SourceShaperTests.cs, Assets/Tests/Editor/DynamicCodeToolTests/DynamicCodeSourcePreparerTests.cs, Assets/Tests/Editor/DynamicCodeToolTests/PreUsingResolverTests.cs, Assets/Tests/Editor/DynamicCodeToolTests/AutoInjectedNamespacesTests.cs, Assets/Tests/Editor/DynamicCodeToolTests/DynamicCodeTestStringUtility.cs
Tests cover alias extraction, default Object and Random alias injection, duplicate suppression, preserved custom aliases, and the revised Build call sites in resolver tests.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SourceShaper as SourceShaper.Analyze
  participant SourceShapeResult as SourceShapeResult
  participant DynamicCodeSourcePreparer as DynamicCodeSourcePreparer.Prepare
  participant WrapperTemplate as WrapperTemplate.Build
  SourceShaper as SourceShaper.Analyze->>SourceShapeResult: collect UsingDirectives and AliasedNames
  DynamicCodeSourcePreparer as DynamicCodeSourcePreparer.Prepare->>WrapperTemplate: pass shape.AliasedNames
  WrapperTemplate as WrapperTemplate.Build->>WrapperTemplate: emit default aliases before caller using directives
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: enabling bare Unity Object calls in dynamic code.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/object-execute-code

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

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
Packages/src/Editor/Compilation/WrapperTemplate.cs (1)

15-147: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Consider extracting alias-detection helpers out of WrapperTemplate.

WrapperTemplate was previously a pure string-templating class; the new HasObjectAlias/IsObjectAliasDirective/SkipWhitespaceAndComments/IsCommentStart helpers add a small hand-rolled tokenizer/parser responsibility on top of that. Moving this logic into a dedicated helper (e.g., alongside SourceShaper, which already owns directive-scanning concerns) would keep WrapperTemplate focused on template assembly and consolidate using-directive parsing in one place.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Packages/src/Editor/Compilation/WrapperTemplate.cs` around lines 15 - 147,
Move the new alias-detection logic out of WrapperTemplate.Build so the class
stays focused on template assembly; extract HasObjectAlias,
IsObjectAliasDirective, SkipWhitespaceAndComments, and IsCommentStart into a
dedicated helper near SourceShaper, which already handles directive scanning.
Keep Build only responsible for writing the wrapper and call the helper from
there when deciding whether to emit the Object alias using directive.
Assets/Tests/Editor/DynamicCodeToolTests/DynamicCodeSourcePreparerTests.cs (1)

42-81: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider adding a test for global using Object = ...; and comment-containing directives.

Current tests don't exercise the global alias path in IsObjectAliasDirective, nor a directive with an embedded comment (e.g. using /* x */ Object = UnityEngine.Object;), which is the scenario tied to the potential infinite-loop risk flagged in WrapperTemplate.cs. Adding these would close the coverage gap for the new tokenizing logic.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Assets/Tests/Editor/DynamicCodeToolTests/DynamicCodeSourcePreparerTests.cs`
around lines 42 - 81, Add coverage in DynamicCodeSourcePreparerTests for the
alias-detection paths that are currently untested: create tests around
DynamicCodeSourcePreparer.Prepare that verify a global using Object = ...
directive is recognized correctly, and that a directive containing an embedded
comment such as using /* ... */ Object = ... does not break parsing or cause
duplicate insertion. Reuse the existing
Prepare_WhenObjectAliasAlreadyExists_ShouldNotAddDuplicateAlias and
Prepare_WhenCustomObjectAliasAlreadyExists_ShouldRespectUserAlias patterns to
assert the prepared source contains exactly one alias directive and that the
user-provided alias is respected.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@Assets/Tests/Editor/DynamicCodeToolTests/DynamicCodeSourcePreparerTests.cs`:
- Around line 42-81: Add coverage in DynamicCodeSourcePreparerTests for the
alias-detection paths that are currently untested: create tests around
DynamicCodeSourcePreparer.Prepare that verify a global using Object = ...
directive is recognized correctly, and that a directive containing an embedded
comment such as using /* ... */ Object = ... does not break parsing or cause
duplicate insertion. Reuse the existing
Prepare_WhenObjectAliasAlreadyExists_ShouldNotAddDuplicateAlias and
Prepare_WhenCustomObjectAliasAlreadyExists_ShouldRespectUserAlias patterns to
assert the prepared source contains exactly one alias directive and that the
user-provided alias is respected.

In `@Packages/src/Editor/Compilation/WrapperTemplate.cs`:
- Around line 15-147: Move the new alias-detection logic out of
WrapperTemplate.Build so the class stays focused on template assembly; extract
HasObjectAlias, IsObjectAliasDirective, SkipWhitespaceAndComments, and
IsCommentStart into a dedicated helper near SourceShaper, which already handles
directive scanning. Keep Build only responsible for writing the wrapper and call
the helper from there when deciding whether to emit the Object alias using
directive.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 169c71bc-1a13-4d00-9824-b74045b5e305

📥 Commits

Reviewing files that changed from the base of the PR and between 90e7483 and d88fa3e.

📒 Files selected for processing (2)
  • Assets/Tests/Editor/DynamicCodeToolTests/DynamicCodeSourcePreparerTests.cs
  • Packages/src/Editor/Compilation/WrapperTemplate.cs

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No issues found across 2 files

Re-trigger cubic

Track using aliases during source shaping so wrapped snippets can avoid duplicate aliases, including verbatim identifiers such as @object. Add UnityEngine.Random as a default alias alongside UnityEngine.Object to cover the remaining System/Unity ambiguity.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
Packages/src/Editor/Compilation/SourceShaper.cs (1)

685-697: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider a readonly struct for AliasNameParseResult.

This small immutable pair is allocated on every parsed using directive. Since it's a value-like tuple of (string, int), a readonly struct (or even a ValueTuple<string,int>) would avoid the heap allocation without changing behavior.

♻️ Proposed refactor
-        private sealed class AliasNameParseResult
+        private readonly struct AliasNameParseResult
         {
             public string Name { get; }
 
             public int EndPosition { get; }
 
             public AliasNameParseResult(string name, int endPosition)
             {
                 Name = name;
                 EndPosition = endPosition;
             }
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Packages/src/Editor/Compilation/SourceShaper.cs` around lines 685 - 697, The
AliasNameParseResult type is an immutable value-like pair currently allocated on
every parsed using directive, so replace the sealed class with a readonly struct
(or equivalent ValueTuple usage) while preserving the Name and EndPosition
members and constructor semantics. Update the AliasNameParseResult declaration
inside SourceShaper to be stack-allocated/value-based, and make sure the parsing
flow that returns or consumes AliasNameParseResult continues to behave
identically with the new value type.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@Packages/src/Editor/Compilation/SourceShaper.cs`:
- Around line 685-697: The AliasNameParseResult type is an immutable value-like
pair currently allocated on every parsed using directive, so replace the sealed
class with a readonly struct (or equivalent ValueTuple usage) while preserving
the Name and EndPosition members and constructor semantics. Update the
AliasNameParseResult declaration inside SourceShaper to be
stack-allocated/value-based, and make sure the parsing flow that returns or
consumes AliasNameParseResult continues to behave identically with the new value
type.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e4618636-cb28-432d-95d2-4c3c90928c18

📥 Commits

Reviewing files that changed from the base of the PR and between d88fa3e and f20f03b.

⛔ Files ignored due to path filters (1)
  • Assets/Tests/Editor/DynamicCodeToolTests/DynamicCodeTestStringUtility.cs.meta is excluded by none and included by none
📒 Files selected for processing (8)
  • Assets/Tests/Editor/DynamicCodeToolTests/AutoInjectedNamespacesTests.cs
  • Assets/Tests/Editor/DynamicCodeToolTests/DynamicCodeSourcePreparerTests.cs
  • Assets/Tests/Editor/DynamicCodeToolTests/DynamicCodeTestStringUtility.cs
  • Assets/Tests/Editor/DynamicCodeToolTests/PreUsingResolverTests.cs
  • Assets/Tests/Editor/DynamicCodeToolTests/SourceShaperTests.cs
  • Packages/src/Editor/Compilation/DynamicCodeSourcePreparer.cs
  • Packages/src/Editor/Compilation/SourceShaper.cs
  • Packages/src/Editor/Compilation/WrapperTemplate.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • Assets/Tests/Editor/DynamicCodeToolTests/DynamicCodeSourcePreparerTests.cs

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

2 issues found across 9 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

hatayama added 2 commits July 3, 2026 20:14
Prevent the shared substring test helper from hanging on empty targets, and assert that verbatim Object aliases are preserved when wrapping snippets.
Use the parsed alias set directly when adding default wrapper aliases, share comment skipping in SourceShaper, and cover comment-separated using aliases with a focused test.
@hatayama
hatayama merged commit 045f525 into main Jul 3, 2026
9 checks passed
@hatayama
hatayama deleted the fix/object-execute-code branch July 3, 2026 12:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant