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
19 changes: 19 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,25 @@ empties the cache.
Cache hits cost ~7% of a miss (0.002ms vs 0.032ms), worth roughly 30% of expression-parse time on
a large file.

### Decompile(0) is memoised — setters must invalidate

`Decompile(int)` is declared on the `CFStatement` and `CFScriptStatement` interfaces, and both
abstract `CFParsedStatement` bases implement it as a wrapper that caches the `indent == 0` result
and delegates to `decompileImpl(int)`. **Concrete nodes override `decompileImpl`, not `Decompile`.**
A non-zero indent is only reached from a parent already rendering itself, so it is not cached.

`CFCase` and `CFCatchStatement` implement the interface directly rather than extending a base, so
they still override `Decompile` and get no caching.

The cached string is valid only while the node is unchanged. Nothing mutates a node after the
visitor finishes building it — which is before any consumer can hold a reference — but **a setter
that changes rendering must call `invalidateDecompiled()`**, or `Decompile(0)` keeps handing back
the text from before the change. `setIsShortHand`, `setStatic`, `setModifier` and
`setMemberOperator` are the kind of setter this applies to.

Worth roughly 6% of a large CFLint scan (74% of `Decompile(0)` calls were repeats), and nothing on
a small one, where DFA warm-up dominates — see below.

## Downstream: CFLint

`cfmleditor/CFLint` is the main consumer. It declares the cfparser version **twice** — a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public CFCompDeclStatement getComponentDeclaration() {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
return "new " + componentDeclaration.Decompile(0);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public byte getType() {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
return (funcDeclStatement.Decompile(0));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public void addElement(CFExpression _e) {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
if (elements.size() > 0) {
StringBuilder sb = new StringBuilder();
sb.append('[');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public void checkIndirectAssignments(String[] scriptSource) {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
StringBuilder sb = new StringBuilder();
if (left != null) {
sb.append(left.Decompile(indent));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public byte getType() {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
String endChar = "";
if (_kind == CFSCRIPTLexer.LEFTBRACKET) {
endChar = "]";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public CFElvisExpression(Token t, CFExpression left, CFExpression right) {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
return "" + _left.Decompile(indent) + " " + operatorImage + " " + _right.Decompile(indent);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public void addMember(CFExpression _right) {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
StringBuilder sb = new StringBuilder();
for (CFExpression expression : expressions) {
if (sb.length() > 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public boolean isUDF() {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
String s = nameId == null ? "" : nameId.Decompile(indent);
s += "(";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public Token getToken() {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
return getFullName();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public void setOnMethodMissing() {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
StringBuilder sb = new StringBuilder();
sb.append(name.Decompile(indent));
sb.append("(");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public CFExpression getExpressionBody() {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
CFFuncDeclStatement declaration = getFuncDeclStatement();
StringBuilder sb = new StringBuilder();
sb.append("(");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public String getStringImage() {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
try {
if (CFSCRIPTLexer.STRING_LITERAL == kind && val != null) {
return "'" + val + "'";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public Token getToken() {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
return "[" + expression.Decompile(0) + "]";
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ public CFMultipartIdentifier(CFIdentifier... identifiers) {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
StringBuffer sb = new StringBuffer();
sb.append(super.Decompile(indent));
sb.append(super.decompileImpl(indent));
for (int i = 1; i < ids.size(); i++) {
sb.append(".").append(ids.get(i).Decompile(0));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public byte getType() {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
StringBuilder sb = new StringBuilder();
sb.append('#');
sb.append(sub.Decompile(0));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public CFNewExpression(Token _t, CFExpression _component, String _pathPrefix, Ar
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
StringBuilder sb = new StringBuilder();
sb.append("new ");
if (pathPrefix != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,40 @@ public void setToken(Token t) {
token = t;
}

/**
* Decompile(0) is the form consumers ask for, and they ask repeatedly: a CFLint scan
* runs every rule over every node and several of them decompile the same node, so a
* 3,002-file scan made 1.25M calls of which 74% were repeats. Each one rebuilds the
* whole subtree's text by concatenation, so the repeats are pure waste. Cache that
* one form; a non-zero indent is only ever reached from a parent already rendering
* itself, so it is not worth keying on.
*
* The cached string is only valid while the node is unchanged. Nothing mutates a node
* after the visitor finishes building it, which is before any consumer can hold a
* reference -- but a setter added later that changes rendering must call
* invalidateDecompiled(), or it will hand back the text from before the change.
*/
@Override
public abstract String Decompile(int indent);
public String Decompile(int indent) {
if (indent != 0) {
return decompileImpl(indent);
}
String cached = decompiled0;
if (cached == null) {
cached = decompileImpl(0);
decompiled0 = cached;
}
return cached;
}

/** Drops the cached Decompile(0) text. Call from any setter that changes rendering. */
protected void invalidateDecompiled() {
decompiled0 = null;
}

private transient String decompiled0;

protected abstract String decompileImpl(int indent);

@Override
public void checkIndirectAssignments(String[] scriptSource) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public CFExpression getBy() {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
StringBuilder sb = new StringBuilder("[");
sb.append(decompileBound(getFrom()));
sb.append(":");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public CFStringExpression(Token t) {
private static final long serialVersionUID = 233826913909000678L;

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
StringBuilder sb = new StringBuilder();
sb.append("'");
for (CFExpression expression : subExpressions) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public String toString() {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
StringBuilder sb = new StringBuilder();
sb.append(key.Decompile(0));
sb.append(':');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public void addElement(CFStructElementExpression _element) {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
StringBuilder sb = new StringBuilder();
sb.append(ordered ? '[' : '{');
if (elements.isEmpty() && emptyMarker != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public byte getType() {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
// String endChar = "";
// if (_kind == CFSCRIPTLexer.LEFTBRACKET) {
// endChar = "]";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public boolean isPrefixop() {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
StringBuilder sb = new StringBuilder();

switch (kind) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public CFVarDeclExpression(Token _t, CFExpression _var, CFExpression _init) {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
StringBuilder s = new StringBuilder(Indent(indent));
s.append(staticDecl ? "static " : finalDecl ? "final " : "var ");
s.append(var.Decompile(0));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public CFAbortStatement(org.antlr.v4.runtime.Token t, CFExpression _message) {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
if (message != null) {
return "abort \"" + message + "\"";
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public CFAdminStatement(org.antlr.v4.runtime.Token t, Map<CFIdentifier, CFExpres
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
StringBuilder sb = new StringBuilder();
sb.append("admin");
DecompileAttributes(sb);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public CFBreakStatement(org.antlr.v4.runtime.Token t) {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
return Indent(indent) + "break";
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public CFStatementResult Exec(CFContext context) {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
StringBuilder sb = new StringBuilder();
sb.append(Indent(indent));
if (modifier != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public void checkIndirectAssignments(String[] scriptSource) {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
StringBuilder s = new StringBuilder();
s.append(Indent(indent));
s.append("{\n");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public CFContinueStatement(Token t) {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
return Indent(indent) + "continue";
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public void checkIndirectAssignments(String[] scriptSource) {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
StringBuilder sb = new StringBuilder();
sb.append(Indent(indent));
sb.append("do");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public CFEmptyStatement() {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
return Indent(indent) + ";";
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public CFExitStatement(org.antlr.v4.runtime.Token t, CFExpression _method) {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
if (methodArg != null) {
return "exit \"" + methodArg + "\"";
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public CFExpressionStatement(CFExpression _e) {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
return expression.Decompile(indent);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public void checkIndirectAssignments(String[] scriptSource) {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
StringBuilder sb = new StringBuilder();
sb.append(Indent(indent));
sb.append("for( ");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public void checkIndirectAssignments(String[] scriptSource) {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
String s = Indent(indent) + "for(";
if (init != null) {
s += init.Decompile(indent);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public CFStatementResult Exec(CFContext context) {
}

@Override
public String Decompile(int indent) {
protected String decompileImpl(int indent) {
StringBuilder sb = new StringBuilder();
sb.append(Indent(indent));

Expand Down
Loading
Loading