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
2 changes: 2 additions & 0 deletions docs/about/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ priorities and future plans.

## Work in progress

- Reduce repeated eval STRING overhead with lazy state and caching for context-free integer-literal evals (#1911).

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

- Run MakeMaker `MY::post_initialize` hooks so generated files join `pm_to_blib`, and parse Perl's `$]` version variable.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.perlonjava.runtime.runtimetypes.*;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -320,9 +321,15 @@ private static RuntimeList evalStringList(String perlCode,
boolean savedCurrentScopeUtf8 = savedCurrentScope.isStrictOptionEnabled(Strict.HINT_UTF8);
boolean savedMutationScopeUtf8 = compileTimeMutationScope.isStrictOptionEnabled(Strict.HINT_UTF8);
RuntimeHash activeHintHash = GlobalVariable.getGlobalHash(GlobalContext.encodeSpecialVar("H"));
Map<String, RuntimeScalar> savedHintHash = new HashMap<>();
for (Map.Entry<String, RuntimeScalar> entry : activeHintHash.elements.entrySet()) {
savedHintHash.put(entry.getKey(), new RuntimeScalar(entry.getValue()));
boolean hintHashEmpty = activeHintHash.elements.isEmpty();
Map<String, RuntimeScalar> savedHintHash;
if (hintHashEmpty) {
savedHintHash = Collections.emptyMap();
} else {
savedHintHash = new HashMap<>(activeHintHash.elements.size());
for (Map.Entry<String, RuntimeScalar> entry : activeHintHash.elements.entrySet()) {
savedHintHash.put(entry.getKey(), new RuntimeScalar(entry.getValue()));
}
}
int savedCallSiteHintHashId = HintHashRegistry.getCallSiteHintHashId();
Map<String, RuntimeScalar> lexicalHintHash =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ private static boolean shouldKeepRuntimeDeparseSource(String sourceName) {
if (sourceName == null || sourceName.isEmpty()) {
return true;
}
if ("-e".equals(sourceName)) {
if ("-e".equals(sourceName) || sourceName.charAt(0) == '(') {
return true;
}
return !new java.io.File(sourceName).isFile();
Expand Down
13 changes: 7 additions & 6 deletions src/main/java/org/perlonjava/backend/jvm/EmitterContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class EmitterContext {
/**
* Cache for contexts with different ContextTypes
*/
private final Map<Integer, EmitterContext> contextCache = new HashMap<>();
private Map<Integer, EmitterContext> contextCache;

/**
* CompilerOptions is a configuration class that holds various settings and flags
Expand Down Expand Up @@ -162,11 +162,12 @@ static void fixupContext(EmitterContext ctx) {
* @return a new EmitterContext with the updated context type
*/
public EmitterContext with(int contextType) {
// Check if the context is already cached
if (contextCache.containsKey(contextType)) {
return contextCache.get(contextType);
if (contextCache != null) {
EmitterContext cached = contextCache.get(contextType);
if (cached != null) return cached;
} else {
contextCache = new HashMap<>();
}
// Create a new context and cache it
EmitterContext newContext = new EmitterContext(
this.javaClassInfo, this.symbolTable,
this.mv, this.cw, contextType, this.isBoxed, this.errorUtil, this.compilerOptions,
Expand All @@ -184,7 +185,7 @@ public void logDebug(String message) {
}

public void clearContextCache() {
contextCache.clear();
if (contextCache != null) contextCache.clear();
}

@Override
Expand Down
45 changes: 30 additions & 15 deletions src/main/java/org/perlonjava/backend/jvm/JavaClassInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ public TryFinallyContext(Label finallyStart, int returnValueSlot,
public String javaClassName;

/** Static fields holding this class's package-scalar pins, keyed by the pinned name. */
private final Map<String, String> scalarPinFields = new LinkedHashMap<>();
private final Map<String, String> formatExpressionFields = new LinkedHashMap<>();
private final IdentityHashMap<StringNode, String> stringLiteralFields = new IdentityHashMap<>();
private final IdentityHashMap<NumberNode, String> integerLiteralFields = new IdentityHashMap<>();
private Map<String, String> scalarPinFields;
private Map<String, String> formatExpressionFields;
private IdentityHashMap<StringNode, String> stringLiteralFields;
private IdentityHashMap<NumberNode, String> integerLiteralFields;

/** Optional proven read-only {@code my (...) = @_} fast path for this generated CV. */
public ReadOnlyArgumentUnpackAnalysis.Candidate readOnlyArgumentCandidate;
Expand All @@ -87,36 +87,47 @@ public TryFinallyContext(Label finallyStart, int returnValueSlot,

/** The pin field already declared for {@code name} in this class, or null. */
public String scalarPinField(String name) {
return scalarPinFields.get(name);
return scalarPinFields != null ? scalarPinFields.get(name) : null;
}

/** Reserve a pin field for {@code name} in this class and return its name. */
public String registerScalarPinField(String name) {
if (scalarPinFields == null) scalarPinFields = new LinkedHashMap<>();
String field = "scalarPin$" + scalarPinFields.size();
scalarPinFields.put(name, field);
return field;
}

/** Reserve a static field initialized from a compiled format-expression template. */
public String registerFormatExpressionField(String registryKey, ClassWriter cw) {
String field = formatExpressionFields.get(registryKey);
if (field != null) return field;
field = "formatExpressions$" + formatExpressionFields.size();
if (formatExpressionFields != null) {
String field = formatExpressionFields.get(registryKey);
if (field != null) return field;
} else {
formatExpressionFields = new LinkedHashMap<>();
}
String field = "formatExpressions$" + formatExpressionFields.size();
cw.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL,
field, "Ljava/util/List;", null, null).visitEnd();
formatExpressionFields.put(registryKey, field);
return field;
}

public Map<String, String> getFormatExpressionFields() {
return Collections.unmodifiableMap(formatExpressionFields);
return formatExpressionFields != null
? Collections.unmodifiableMap(formatExpressionFields)
: Collections.emptyMap();
}

/** Register an instance field for one compiled string literal occurrence. */
public String stringLiteralField(StringNode node, ClassWriter cw) {
String field = stringLiteralFields.get(node);
if (field != null) return field;
field = "stringLiteral$" + stringLiteralFields.size();
if (stringLiteralFields != null) {
String field = stringLiteralFields.get(node);
if (field != null) return field;
} else {
stringLiteralFields = new IdentityHashMap<>();
}
String field = "stringLiteral$" + stringLiteralFields.size();
stringLiteralFields.put(node, field);
cw.visitField(Opcodes.ACC_PRIVATE, field,
"Lorg/perlonjava/runtime/runtimetypes/RuntimeScalarReadOnly;", null, null).visitEnd();
Expand All @@ -125,9 +136,13 @@ public String stringLiteralField(StringNode node, ClassWriter cw) {

/** Register an instance field for one boxed integer literal outside the shared cache. */
public String integerLiteralField(NumberNode node, ClassWriter cw) {
String field = integerLiteralFields.get(node);
if (field != null) return field;
field = "integerLiteral$" + integerLiteralFields.size();
if (integerLiteralFields != null) {
String field = integerLiteralFields.get(node);
if (field != null) return field;
} else {
integerLiteralFields = new IdentityHashMap<>();
}
String field = "integerLiteral$" + integerLiteralFields.size();
integerLiteralFields.put(node, field);
cw.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_VOLATILE, field,
"Lorg/perlonjava/runtime/runtimetypes/RuntimeScalarReadOnly;", null, null).visitEnd();
Expand Down
20 changes: 11 additions & 9 deletions src/main/java/org/perlonjava/frontend/parser/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,8 @@ public boolean hasVisibleLexicalSubroutine(String name) {
// to rescan every preceding token for every quote-like operator, which made
// large generated Perl data files (notably CPAN CHECKSUMS) quadratic to parse.
// Identity keys remain valid when parsing rewrites or removes nearby tokens.
private final IdentityHashMap<LexerToken, Integer> sourceLineOffsets = new IdentityHashMap<>();
private int[] sourceLineOffsets;
private int sourceLineOffsetsTokenCount;
/**
* When {@code true} (qq and normal strings), {@link Variable#parseBracedVariable} may rewrite
* {@code \"} before {@code "} inside {@code ${...}} so patterns like {@code "${\"name\"}"}
Expand Down Expand Up @@ -380,10 +381,13 @@ public java.util.Map<String, ParserKeywordPlugin> getVisibleParserKeywordPlugins
}

private void indexSourceLines() {
int size = tokens.size();
sourceLineOffsetsTokenCount = size;
sourceLineOffsets = new int[size];
int lineOffset = 0;
for (LexerToken token : tokens) {
sourceLineOffsets.put(token, lineOffset);
if (token.type == LexerTokenType.NEWLINE) {
for (int i = 0; i < size; i++) {
sourceLineOffsets[i] = lineOffset;
if (tokens.get(i).type == LexerTokenType.NEWLINE) {
lineOffset++;
}
}
Expand All @@ -392,11 +396,9 @@ private void indexSourceLines() {
/** Return the source line at a token without repeatedly scanning the token list. */
public int sourceLineAt(int tokenIndex) {
int baseLine = baseLineNumber > 0 ? baseLineNumber : 1;
if (tokenIndex >= 0 && tokenIndex < tokens.size()) {
Integer offset = sourceLineOffsets.get(tokens.get(tokenIndex));
if (offset != null) {
return baseLine + offset;
}
if (tokenIndex >= 0 && tokenIndex < sourceLineOffsetsTokenCount
&& tokens.size() == sourceLineOffsetsTokenCount) {
return baseLine + sourceLineOffsets[tokenIndex];
}

// Synthetic tokens are rare; retain a correctness fallback for them.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1171,8 +1171,11 @@ public boolean isWarningCategoryDisabled(String category) {
}

public Set<String> getDisabledWarningCategories() {
Set<String> categories = new HashSet<>();
BitSet disabled = warningDisabledStack.peek();
if (disabled.isEmpty()) {
return Collections.emptySet();
}
Set<String> categories = new HashSet<>();
for (Map.Entry<String, Integer> entry : warningBitPositions().entrySet()) {
int bitPosition = entry.getValue();
if (bitPosition >= 0 && disabled.get(bitPosition)) {
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/org/perlonjava/runtime/operators/WarnDie.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@ public class WarnDie {

private static final ThreadLocal<Boolean> WRITING_WARNING =
ThreadLocal.withInitial(() -> Boolean.FALSE);

private static final ThreadLocal<Boolean> COMPILE_TIME_WARNING_EMITTED =
ThreadLocal.withInitial(() -> Boolean.FALSE);

public static void clearCompileTimeWarningFlag() {
COMPILE_TIME_WARNING_EMITTED.set(Boolean.FALSE);
}

public static boolean wasCompileTimeWarningEmitted() {
return COMPILE_TIME_WARNING_EMITTED.get();
}

public static boolean isInsideUnhandledDieHandler() {
return PerlRuntime.current().executionState().insideUnhandledDieHandler;
}
Expand Down Expand Up @@ -348,6 +360,7 @@ public static RuntimeBase warn(RuntimeBase message, RuntimeScalar where) {
}

public static RuntimeBase warn(RuntimeBase message, RuntimeScalar where, String fileName, int lineNumber) {
COMPILE_TIME_WARNING_EMITTED.set(Boolean.TRUE);
// PERL5DB is debugger bootstrap code; Perl emits its implicit warning
// without the eval-string source suffix in this special context.
if (org.perlonjava.runtime.debugger.DebugHooks.isExecutingPerl5db()) {
Expand Down Expand Up @@ -594,6 +607,7 @@ public static RuntimeBase warnWithCategoryFromCode(RuntimeBase message, RuntimeS

private static RuntimeBase warnWithCategory(RuntimeBase message, RuntimeScalar where, String category,
String fileName, int lineNumber, boolean enabledByDefault, String warningBitsOverride) {
COMPILE_TIME_WARNING_EMITTED.set(Boolean.TRUE);
if (WarningFlags.areWarningsForcedOff()) {
return new RuntimeScalar();
}
Expand Down
Loading
Loading