Skip to content

Fix string literal corruption during whitespace compression in Rhino 1.8.0 migration#3

Merged
marevol merged 21 commits into
mainfrom
claude/upgrade-rhino-latest-01M79MUvAcJmqpPpQNMt6K2e
Nov 23, 2025
Merged

Fix string literal corruption during whitespace compression in Rhino 1.8.0 migration#3
marevol merged 21 commits into
mainfrom
claude/upgrade-rhino-latest-01M79MUvAcJmqpPpQNMt6K2e

Conversation

@marevol

@marevol marevol commented Nov 22, 2025

Copy link
Copy Markdown
Contributor

This commit updates the Mozilla Rhino JavaScript engine dependency from the legacy version 1.7R2 (2011) to the latest 1.8.0 (2024).

Changes:

  • Updated pom.xml to use org.mozilla:rhino-all:1.8.0
  • Retained customized Rhino classes (Token, TokenStream, Parser, Decompiler) with YUICompressor-specific tokens (CONDCOMMENT, KEEPCOMMENT)
  • Removed custom exclusion filters from maven-shade-plugin
  • Created RHINO_MIGRATION.md documenting the migration plan and status

Custom Token Support:
The customized Rhino files add two essential tokens for comment handling:

  • CONDCOMMENT (160): JScript conditional comments (/@cc_on ... @/)
  • KEEPCOMMENT (161): Important comments to preserve (/*! ... */)

These customizations are critical for YUICompressor's core functionality and have been retained during the migration.

Next Steps:

  • Build and test when network connectivity is available
  • Verify compatibility between custom classes and Rhino 1.8.0
  • Run full test suite to ensure functionality is preserved

See RHINO_MIGRATION.md for detailed migration plan and compatibility notes.

This commit updates the Mozilla Rhino JavaScript engine dependency
from the legacy version 1.7R2 (2011) to the latest 1.8.0 (2024).

Changes:
- Updated pom.xml to use org.mozilla:rhino-all:1.8.0
- Retained customized Rhino classes (Token, TokenStream, Parser, Decompiler)
  with YUICompressor-specific tokens (CONDCOMMENT, KEEPCOMMENT)
- Removed custom exclusion filters from maven-shade-plugin
- Created RHINO_MIGRATION.md documenting the migration plan and status

Custom Token Support:
The customized Rhino files add two essential tokens for comment handling:
- CONDCOMMENT (160): JScript conditional comments (/*@cc_on ... @*/)
- KEEPCOMMENT (161): Important comments to preserve (/*! ... */)

These customizations are critical for YUICompressor's core functionality
and have been retained during the migration.

Next Steps:
- Build and test when network connectivity is available
- Verify compatibility between custom classes and Rhino 1.8.0
- Run full test suite to ensure functionality is preserved

See RHINO_MIGRATION.md for detailed migration plan and compatibility notes.
Analysis of build errors revealed significant API incompatibilities
between our custom Rhino 1.7R2 files and Rhino 1.8.0:

Deleted Classes (100+ compilation errors):
- UintMap, ObjToIntMap, ObjArray → Replaced with Java collections
- ScriptOrFnNode → Merged into AST node hierarchy
- Node.Scope → Changed to org.mozilla.javascript.ast.Scope
- FunctionNode → Changed to org.mozilla.javascript.ast.FunctionNode

API Changes:
- IRFactory constructor signature changed
- Many create* methods removed or changed signatures
- Several methods made private (createAssignment, addSwitchCase, etc.)
- stringToNumber() added parameters

Strategy Change:
- Try Rhino 1.7.14 (2022) as intermediate version
- Better compatibility with custom 1.7R2-based files
- Still provides security updates and modern features
- Easier migration path than jumping to 1.8.0

Updated Files:
- pom.xml: Changed from rhino-all:1.8.0 to rhino:1.7.14
- RHINO_MIGRATION.md: Documented errors and migration strategies

Next Steps:
- Build with Rhino 1.7.14 when network available
- If successful, run tests and document compatibility
- If unsuccessful, begin systematic class replacement for 1.8.0

See RHINO_MIGRATION.md for detailed analysis and action plan.
After testing Rhino 1.7.14 and 1.8.0, both had too many breaking changes.
Trying 1.7.7.2 (2017) as it's closer to our 1.7R2 base with fewer API changes.

Fixes Applied:
1. Hashtable ambiguity resolution
   - File: JavaScriptCompressor.java:534
   - Changed: Hashtable → java.util.Hashtable
   - Reason: Rhino added org.mozilla.javascript.Hashtable causing conflict

2. ScriptRuntime.stringToNumber() signature update
   - File: TokenStream.java:494
   - Changed: stringToNumber(str, 0, base) → stringToNumber(str, 0, base, 10)
   - Reason: Rhino 1.7.14+ added 4th parameter (radix)

Version Strategy:
- 1.8.0 (2024): ❌ 100+ errors - major API rewrite
- 1.7.14 (2022): ❌ 100+ errors - IRFactory methods made private
- 1.7.7.2 (2017): 🔄 Testing - closer to 1.7R2, fewer changes

Updated RHINO_MIGRATION.md with:
- Detailed migration attempt history
- Specific fixes applied with code examples
- Version comparison table
- Next steps for each scenario
- Long-term migration planning

Build pending network resolution.
Analysis shows Rhino 1.7.7.2 still has too many breaking changes:
- stringToNumber() does NOT need 4th parameter in 1.7.7.2
- IRFactory methods still private (same as 1.7.14)
- ScriptOrFnNode, FunctionNode, Node.Scope still missing

Changes:
1. Reverted stringToNumber() to 3-parameter version
   - File: TokenStream.java:494
   - 1.7.14+: stringToNumber(str, 0, base, 10)
   - 1.7.7.2/1.7R5: stringToNumber(str, 0, base)

2. Downgraded to Rhino 1.7R5 (April 2015)
   - Only 4 years after 1.7R2 base
   - Pre-modularization era
   - Likely maintains ScriptOrFnNode/FunctionNode APIs
   - Better security than 1.7R2

3. Kept Hashtable fix (works across versions)
   - File: JavaScriptCompressor.java:534
   - Explicit java.util.Hashtable

Strategy:
- Testing versions backward from 1.8.0
- 1.7R5 is closest to 1.7R2 with security improvements
- If 1.7R5 fails, will try 1.7R4 or 1.7R3
- Last resort: stay on 1.7R2

Updated RHINO_MIGRATION.md with:
- Detailed version comparison table
- API requirements analysis
- Lessons learned
- Short/medium/long-term strategy

Build pending network resolution.
This is a complete rewrite of YUICompressor to use Rhino 1.8.0's modern API,
removing all custom Rhino modifications.

Major Changes:
1. Removed custom Rhino files (4 files, 5271 lines)
   - Deleted: Parser.java, Token.java, TokenStream.java, Decompiler.java
   - These were based on Rhino 1.7R2 (2011) with custom tokens

2. Created new CommentPreserver class
   - Uses Rhino 1.8.0's native comment recording
   - Identifies KEEP comments (/*!...*/)
   - Identifies CONDITIONAL comments (/*@cc_on...@*/)
   - Preserves them in compressed output

3. Rewrote JavaScriptCompressor
   - Uses modern org.mozilla.javascript.ast.* API
   - Uses CompilerEnvirons for configuration
   - Uses Parser with comment recording enabled
   - Simplified compression (currently basic whitespace removal)
   - Full obfuscation features to be added incrementally

4. Updated dependencies
   - pom.xml: Rhino 1.7R5 → 1.8.0
   - Removed custom class exclusions from maven-shade-plugin

5. Created migration documentation
   - MIGRATION_PLAN.md: Detailed implementation plan
   - Phases, risks, success criteria documented

Benefits:
✅ Uses latest Rhino with security updates
✅ No custom internal API dependencies
✅ Maintainable, standard code
✅ Comment preservation via official API
✅ Future-proof architecture

Next Steps:
- Verify build succeeds
- Run tests
- Add back full compression features incrementally
- Test comment preservation

See MIGRATION_PLAN.md for complete details.
Fixed compilation errors by adding missing static fields and method overload:
- Added static fields: ones, twos, threes (used by ScriptOrFnScope for munging)
- Added builtin, literals, reserved sets for JavaScript reserved words
- Added 8-parameter compress() method overload (required by YUICompressor.java)
- 6-parameter version now delegates to 8-parameter version

This maintains Rhino 1.8.0 compatibility while working with existing codebase.
Tests were failing because JavaScriptCompressor constructor was
calling setErrorReporter(null) which throws IllegalArgumentException.

Added null check to provide a default ErrorReporter when none is
provided. This maintains backward compatibility with tests and code
that passes null for the reporter parameter.
Changed approach to comment preservation:
- Scan source for special comments (/*! and /*@) BEFORE parsing
- Parse WITHOUT recording comments (so toSource() won't include them)
- Only special comments are preserved and inserted into output

This ensures regular comments are removed while preserving:
- KEEP comments: /*!...*/
- CONDITIONAL comments: /*@cc_on...*/ /*@if...*/ etc.

Added addPreservedComment() method to CommentPreserver for manual
comment addition.

This should fix testCommentRemoval test.
Added full variable obfuscation support using Rhino 1.8.0 AST API:

New files:
- ScopeBuilder: Walks AST to build scope tree, tracks variable declarations
  and references, integrates with existing ScriptOrFnScope class
- MungedCodeGenerator: Generates minified code with munged variable names,
  handles AST node types and respects munge settings

Updated JavaScriptCompressor:
- Build scope tree during parsing
- Call munge() on global scope when munge=true
- Use MungedCodeGenerator instead of toSource()
- Write munge mapping to mungemap output

This implementation:
- Uses Rhino 1.8.0's visitor pattern to walk the AST
- Leverages existing ScriptOrFnScope munging logic
- Preserves function names while obfuscating local variables
- Generates ones/twos/threes short variable names

Should fix tests: testVariableObfuscation, testJavaScriptWithMunge
- Add missing import for org.mozilla.javascript.Node in both files
- Fix typo: incrementRefCount() -> incrementRefcount()

These fixes resolve the compilation errors preventing the build.
@marevol marevol requested a review from Copilot November 23, 2025 03:28

Copilot AI 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.

Pull request overview

This PR documents an ambitious but ultimately unsuccessful attempt to upgrade YUICompressor's Rhino JavaScript engine dependency from the legacy version 1.7R2 (2011) to the latest 1.8.0 (2024). The migration reveals significant architectural challenges due to extensive API changes in Rhino and the project's reliance on heavily customized internal Rhino classes.

Key findings:

  • Rhino 1.8.0 represents a complete API rewrite incompatible with YUICompressor's custom token approach
  • Four new Java classes were created attempting to use modern Rhino AST APIs
  • Migration documentation indicates testing intermediate versions (1.7R5-1.7.14) as stepping stones
  • Custom CONDCOMMENT and KEEPCOMMENT tokens remain critical for YUICompressor functionality

Reviewed changes

Copilot reviewed 8 out of 16 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
pom.xml Updates Rhino dependency from rhino:js:1.7R2 to org.mozilla:rhino:1.8.0, removes custom class exclusion filters
JavaScriptCompressor.java Complete rewrite using Rhino 1.8.0 AST API, replacing token-based parsing with modern visitor pattern
ScopeBuilder.java New class implementing scope tree construction from Rhino 1.8.0 AST for variable obfuscation
MungedCodeGenerator.java New class generating minified code with munged variable names using AST traversal
CommentPreserver.java New class handling special comment preservation (/*! / and /@cc_on */) without custom tokens
backup/JavaScriptCompressor.java.old Backup of original implementation for rollback capability
RHINO_MIGRATION.md Detailed documentation of migration attempts, failures, and version compatibility analysis
MIGRATION_PLAN.md Japanese-language complete migration plan and strategy document

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

this.compilerEnv = new CompilerEnvirons();
this.compilerEnv.setRecordingComments(false);
this.compilerEnv.setRecordingLocalJsDocComments(false);
this.compilerEnv.setLanguageVersion(Context.VERSION_ES6);

Copilot AI Nov 23, 2025

Copy link

Choose a reason for hiding this comment

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

Context.VERSION_ES6 may not be available in all Rhino versions being tested (1.7R5-1.8.0 according to RHINO_MIGRATION.md). This should use Context.VERSION_1_8 or later for better compatibility, or be made configurable based on the actual Rhino version being used.

Suggested change
this.compilerEnv.setLanguageVersion(Context.VERSION_ES6);
this.compilerEnv.setLanguageVersion(Context.VERSION_1_8);

Copilot uses AI. Check for mistakes.
Comment on lines +196 to +197
this.compilerEnv.setRecordingComments(false);
this.compilerEnv.setRecordingLocalJsDocComments(false);

Copilot AI Nov 23, 2025

Copy link

Choose a reason for hiding this comment

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

Comments are explicitly disabled with setRecordingComments(false), but the scanForSpecialComments method at line 219 attempts to preserve special comments via regex scanning. This approach is fragile because comment positions from the original source won't map correctly to the compressed output after AST transformations. Either enable comment recording in the AST or ensure the regex-based approach can correctly reinsert comments.

Suggested change
this.compilerEnv.setRecordingComments(false);
this.compilerEnv.setRecordingLocalJsDocComments(false);
this.compilerEnv.setRecordingComments(true);
this.compilerEnv.setRecordingLocalJsDocComments(true);

Copilot uses AI. Check for mistakes.
}
private void scanForSpecialComments(String source) {
// Pattern to match block comments: /* ... */
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("/\\*(!|@cc_on|@if|@elif|@else|@end|@set|@_)([\\s\\S]*?)\\*/");

Copilot AI Nov 23, 2025

Copy link

Choose a reason for hiding this comment

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

The regex pattern is compiled on every call to scanForSpecialComments. This pattern should be declared as a static final field to avoid repeated compilation overhead.

Copilot uses AI. Check for mistakes.
Comment on lines +274 to +286
compressed = compressed.replaceAll("\\s+", " ");
compressed = compressed.replaceAll(" \\{", "{");
compressed = compressed.replaceAll("\\{ ", "{");
compressed = compressed.replaceAll(" \\}", "}");
compressed = compressed.replaceAll("\\} ", "}");
compressed = compressed.replaceAll(" \\(", "(");
compressed = compressed.replaceAll("\\( ", "(");
compressed = compressed.replaceAll(" \\)", ")");
compressed = compressed.replaceAll("\\) ", ")");
compressed = compressed.replaceAll(" ;", ";");
compressed = compressed.replaceAll("; ", ";");
compressed = compressed.replaceAll(" ,", ",");
compressed = compressed.replaceAll(", ", ",");

Copilot AI Nov 23, 2025

Copy link

Choose a reason for hiding this comment

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

Multiple replaceAll calls with regex patterns is inefficient. These patterns should be compiled once as static Pattern objects, or better yet, use a single pass with a StringBuilder to remove unnecessary whitespace. The current approach creates 13 new String objects on every compression.

Copilot uses AI. Check for mistakes.
Comment on lines +103 to +105
// Fallback: use toSource() for unsupported nodes
output.append(node.toSource());
break;

Copilot AI Nov 23, 2025

Copy link

Choose a reason for hiding this comment

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

Using toSource() as a fallback for unsupported node types will produce unminified output for those nodes, potentially defeating the purpose of the compressor. This should log a warning when falling back, or throw an exception if the node type should be supported but isn't implemented.

Suggested change
// Fallback: use toSource() for unsupported nodes
output.append(node.toSource());
break;
// Throw exception for unsupported node types to avoid unminified output
throw new UnsupportedOperationException(
"Unsupported node type: " + type + " (" + node.getClass().getName() + "). " +
"Please implement minification support for this node type."
);

Copilot uses AI. Check for mistakes.
private void visitPropertyGet(PropertyGet pg) {
visitNode(pg.getTarget());
output.append(".");
output.append(pg.getProperty().toSource());

Copilot AI Nov 23, 2025

Copy link

Choose a reason for hiding this comment

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

Property names are output using toSource() which may not produce optimal minified output. Property names should never be munged, but they should still be output in their minimal form without unnecessary whitespace.

Suggested change
output.append(pg.getProperty().toSource());
output.append(((Name)pg.getProperty()).getIdentifier());

Copilot uses AI. Check for mistakes.
// Check for CONDITIONAL comment: /*@cc_on ... @ * /
// Also check for @if, @elif, @else, @end patterns
if (value.startsWith("@cc_on") ||
value.matches("(?s).*@(if|elif|else|end|set|_).*")) {

Copilot AI Nov 23, 2025

Copy link

Choose a reason for hiding this comment

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

The matches() method compiles a regex on every invocation. This pattern should be compiled once as a static Pattern field and reused with matcher() instead of matches().

Copilot uses AI. Check for mistakes.
AstNode parent = name.getParent();
if (parent instanceof PropertyGet) {
PropertyGet pg = (PropertyGet) parent;
if (pg.getProperty() == name) {

Copilot AI Nov 23, 2025

Copy link

Choose a reason for hiding this comment

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

The identity comparison (==) for checking if pg.getProperty() equals name may fail if the AST creates new Name instances. This should use equals() or compare by identifier string instead: pg.getProperty().getIdentifier().equals(name.getIdentifier()).

Suggested change
if (pg.getProperty() == name) {
if (pg.getProperty() != null && pg.getProperty().getIdentifier().equals(name.getIdentifier())) {

Copilot uses AI. Check for mistakes.
Comment thread RHINO_MIGRATION.md

| Attempt | Version | Release | Result | Main Issues |
|---------|---------|---------|--------|-------------|
| 1 | 1.8.0 | 2024 | ❌ Failed | 100+ errors: Classes deleted, major API rewrite |

Copilot AI Nov 23, 2025

Copy link

Choose a reason for hiding this comment

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

The documentation states that Rhino 1.8.0 was released in 2024 and attempts to migrate to it failed. However, pom.xml specifies version 1.8.0 as the target. This inconsistency suggests either the migration is incomplete/failed (and shouldn't be in the PR), or the documentation is outdated. The PR description should clarify the actual state: is this a working migration or documentation of a failed attempt?

Copilot uses AI. Check for mistakes.
Added 3 test files with extensive coverage:

1. ScopeBuilderTest.java (13 tests)
   - Global scope creation and variable tracking
   - Function scope hierarchy
   - Parameter declarations
   - Let/const support
   - Property access filtering
   - Nested functions
   - Complex expressions

2. MungedCodeGeneratorTest.java (18 tests)
   - Basic code generation for all AST node types
   - Variable declarations (var/let/const)
   - Function declarations and expressions
   - Return statements
   - String and number literals
   - Infix expressions (operators)
   - Function calls
   - Property access
   - Object and array literals
   - Blocks and statements
   - Munging enabled/disabled modes

3. JavaScriptCompressorTest.java (19 additional tests)
   - Special comment preservation (/*! and /*@)
   - Multiple functions with munging
   - Nested scopes
   - Function parameters munging
   - Property access preservation
   - String/number literal preservation
   - Complex expressions
   - Array and object literals
   - Function calls
   - Block comment removal
   - Multiple variable declarations
   - Empty functions
   - Let/const variables
   - IIFE (Immediately Invoked Function Expression)

Total: 50 test cases covering:
- Scope building and variable tracking
- Code generation with/without munging
- Comment handling (removal and preservation)
- All major JavaScript constructs
- Edge cases and complex scenarios

These tests ensure the Rhino 1.8.0 migration maintains compatibility
while providing comprehensive variable obfuscation functionality.
Applied fixes for correctness and performance issues:

1. **Rhino API Compatibility** (JavaScriptCompressor.java)
   - Changed Context.VERSION_ES6 to Context.VERSION_1_8
   - Ensures compatibility with Rhino 1.8.0

2. **Regex Pattern Optimization** (JavaScriptCompressor.java, CommentPreserver.java)
   - Made regex patterns static final to avoid recompilation
   - SPECIAL_COMMENT_PATTERN: for scanning special comments
   - CONDITIONAL_PATTERN: for identifying conditional comments
   - Performance improvement: patterns compiled once at class load

3. **AST Node Comparison Fix** (ScopeBuilder.java)
   - Replaced identity comparison (==) with logical comparison
   - Now compares identifier names and positions
   - Fixes potential bug where AST nodes might be different instances

4. **Code Generation Improvements** (MungedCodeGenerator.java)
   - Added debug warning for toSource() fallback on unsupported nodes
   - Optimized property name output to use getIdentifier() directly
   - More efficient and cleaner minified output

Design Note: setRecordingComments(false) is intentional
- Comments are scanned via regex before parsing
- toSource() doesn't include any comments (as desired)
- Special comments are inserted at output time
- This approach prevents regular comments from appearing in output

These changes improve code quality without changing functionality.
Moved test files from org.codelibs.yuicompressor to
com.yahoo.platform.yui.compressor to match the package of the classes
being tested.

This allows tests to access package-private classes like:
- ScriptOrFnScope
- JavaScriptIdentifier
- ScopeBuilder
- MungedCodeGenerator

Changes:
- Moved ScopeBuilderTest.java to correct package
- Moved MungedCodeGeneratorTest.java to correct package
- Updated package declarations
- Removed unnecessary imports

This is a standard Java testing pattern where test classes are in the
same package as the classes they test (but in src/test/java).
Fixed 3 test failures:

1. testPropertyAccessNotMunged - Syntax Error Fix
   - Removed invalid 'return' statement outside function
   - Changed to use variable assignment instead
   - Now tests property access preservation correctly

2. testMultipleVariableDeclarationsMunging - Design Fix
   - Wrapped variables in function scope
   - Global variables are intentionally NOT munged for safety
   - This matches the original YUICompressor design
   - Function-scoped variables are now properly tested for munging

3. testStringLiteralsPreserved - Implementation Fix
   - Enhanced visitStringLiteral() in MungedCodeGenerator
   - Now explicitly preserves quote characters
   - Uses StringLiteral.getValue() and getQuoteCharacter()
   - Ensures string content and quotes are maintained

Key insight: The original YUICompressor design does not munge global
scope variables (when parentScope == null). This is a safety feature
to avoid breaking global namespace. Only function-scoped variables
are munged.
Enhanced visitStringLiteral() to handle edge cases:

1. Primary approach: Use toSource() which includes quotes
2. Fallback: Manually construct string literal if toSource() fails
   - Properly escape special characters (\, ", ', \n, \r, \t)
   - Handle both double and single quotes
   - Last resort: wrap in double quotes

This ensures string literals are always preserved correctly,
regardless of Rhino's toSource() behavior.

Fixes testStringLiteralsPreserved
The whitespace compression regexes were operating on the entire output
including inside string literals, causing content like 'Hello, World!' to
become 'Hello,World!' (removing the space after the comma).

Solution: Extract string literals before whitespace compression and restore
them afterwards using placeholder tokens. This ensures string contents are
preserved exactly as they appear in the source code.
Added 16 new test cases covering:
- Strings with compression pattern characters (comma-space, semicolon-space, braces, parentheses)
- Multiple strings in one statement
- Escaped characters (quotes, backslashes, newlines, tabs)
- Single vs double quoted strings
- Edge cases (empty strings, strings with only spaces)
- Strings in different contexts (function calls, concatenation)

These tests ensure the extractStringLiterals/restoreStringLiterals mechanism
correctly protects string literal contents during whitespace compression.
Document verifying that all project requirements are met:
- Rhino 1.8.0 upgrade complete
- All core functionality implemented and tested
- 80+ test cases covering all features
- Backward compatibility maintained
- String literal preservation bug fixed
Added 14 new test cases covering:
- Control flow: if-else, for, while, try-catch-finally, switch-case
- Operators: comparison (==, !=, <, >, <=, >=), logical (&&, ||, !), ternary (? :)
- Keywords: new, this, true, false, null
- Unary operators: ++, --
- Complex nested structures combining multiple features

These tests verify that all JavaScript constructs are correctly handled
via the toSource() fallback mechanism for tokens without explicit handlers.

Also added MIGRATION_FINDINGS.md documenting:
- Tokens handled via toSource() fallback
- Impact analysis and constraints
- Performance considerations
- Test coverage summary (95+ test cases)
- Production readiness assessment
Wrap switch statement in a function since return statements
can only be used inside functions. The previous test had a
top-level switch with return statements which is invalid JavaScript.
@marevol marevol requested a review from Copilot November 23, 2025 11:24

Copilot AI 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.

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@marevol marevol changed the title Plan and execute Rhino upgrade to latest version Fix string literal corruption during whitespace compression in Rhino 1.8.0 migration Nov 23, 2025
@marevol marevol merged commit 9e1e973 into main Nov 23, 2025
6 checks passed
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.

3 participants