Skip to content
Open
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
2 changes: 1 addition & 1 deletion docs/about/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ priorities and future plans.

- Preserve named typeglob stringification when copying package stash entries (#1927).

- Reduce eval STRING overhead with lazy goto collections, parser-set AST flags that skip unneeded BytecodeCompiler analysis passes, ScopedSymbolTable snapshot caching, per-thread eval caches without synchronization, cached constructor/class-field metadata, and a re-entrant fast-path in `PerlRuntime.bind()` (#1911).
- Reduce eval STRING overhead with lazy goto collections, parser-set AST flags that skip unneeded BytecodeCompiler analysis passes, ScopedSymbolTable snapshot caching, per-thread eval caches without synchronization, cached constructor/class-field metadata, a re-entrant fast-path in `PerlRuntime.bind()`, and a lexer `sawBeginOrSub` flag that skips the O(visible_vars) outer-lexical alias loop on the common path (#1911, #1961).

- Preserve arbitrary bytes in SQLite text columns across DBI fetches (#1923).

Expand Down
10 changes: 9 additions & 1 deletion src/main/java/org/perlonjava/frontend/lexer/Lexer.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ public class Lexer {
public int position;
// Length of the input
public int length;
// Set to true if the token stream contains BEGIN, sub, use, or no —
// keywords that require the outer-lexical alias loop in evalStringWithInterpreter().
// False on the common path (simple expressions, while loops, etc.).
public boolean sawBeginOrSub = false;

// Constructor to initialize the Lexer with input string
public Lexer(String input) {
Expand Down Expand Up @@ -368,7 +372,11 @@ public LexerToken consumeIdentifier() {
}
}
// Build token text using substring to preserve surrogate pairs correctly
return new LexerToken(LexerTokenType.IDENTIFIER, input.substring(start, position));
String text = input.substring(start, position);
if (!sawBeginOrSub
&& (text.equals("BEGIN") || text.equals("sub")
|| text.equals("use") || text.equals("no"))) sawBeginOrSub = true;
return new LexerToken(LexerTokenType.IDENTIFIER, text);
}

public LexerToken consumeOperator() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1062,15 +1062,18 @@ public ScopedSymbolTable snapShot(SymbolTable prebuiltTable) {

// Clone warning flags
st.warningFlagsStack.pop(); // Remove the initial value pushed by enterScope
st.warningFlagsStack.push((BitSet) this.warningFlagsStack.peek().clone());
BitSet wf = this.warningFlagsStack.peek();
st.warningFlagsStack.push(wf.isEmpty() ? new BitSet() : (BitSet) wf.clone());

// Clone disabled warnings flags
st.warningDisabledStack.pop(); // Remove the initial value pushed by enterScope
st.warningDisabledStack.push((BitSet) this.warningDisabledStack.peek().clone());
BitSet wd = this.warningDisabledStack.peek();
st.warningDisabledStack.push(wd.isEmpty() ? new BitSet() : (BitSet) wd.clone());

// Clone fatal warnings flags
st.warningFatalStack.pop(); // Remove the initial value pushed by enterScope
st.warningFatalStack.push((BitSet) this.warningFatalStack.peek().clone());
BitSet wft = this.warningFatalStack.peek();
st.warningFatalStack.push(wft.isEmpty() ? new BitSet() : (BitSet) wft.clone());

// Clone feature flags
st.featureFlagsStack.pop(); // Remove the initial value pushed by enterScope
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5393,10 +5393,14 @@ public static RuntimeList evalStringWithInterpreter(
: topLevelMain ? runtimePackage : capturedSymbolTable.getCurrentPackage();

try {
// Parse the eval string (tokenize first so sawBegin is available for the alias gate below)
Lexer lexer = new Lexer(evalString);
tokens = lexer.tokenize();
// Alias captured outer variables before parsing.
// BEGIN/use execute during parsing, so their aliases must be in place first.
// Named subs in eval also access outer lexicals via these aliases.
{
// Skip this O(visible_vars) loop on the common path (no BEGIN or named sub).
if (lexer.sawBeginOrSub) {
Map<Integer, SymbolTable.SymbolEntry> capturedVars = capturedSymbolTable.getAllVisibleVariables();
for (SymbolTable.SymbolEntry entry : capturedVars.values()) {
if (!entry.name().equals("@_") && !entry.decl().isEmpty() && !entry.name().startsWith("&")) {
Expand Down Expand Up @@ -5452,9 +5456,6 @@ public static RuntimeList evalStringWithInterpreter(
}
evalCompilerOptions.fileName = evalFilename;
warnSignatureArgsInEval(evalString, evalFilename);
// Parse the eval string
Lexer lexer = new Lexer(evalString);
tokens = lexer.tokenize();
// Create parser context
ScopedSymbolTable parseSymbolTable = capturedSymbolTable.snapShot();
parseSymbolTable.setCurrentPackage(
Expand Down
Loading