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
8 changes: 7 additions & 1 deletion cfml.parsing/src/main/antlr4/cfml/CFSCRIPTParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,14 @@ interfaceDeclaration
: INTERFACE componentAttribute* componentGuts //-> ( COMPDECL componentAttribute* componentGuts)
;

// A member inside a static block may carry an access type: static { public myVar = "v"; }.
// accessType sits on the member rather than the block, so it is optional per statement.
staticBlock
: STATIC LEFTCURLYBRACKET ( statement )* RIGHTCURLYBRACKET
: STATIC LEFTCURLYBRACKET ( staticMember )* RIGHTCURLYBRACKET
;

staticMember
: accessType? statement
;

element
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package cfml.parsing.cfscript.script;

import java.util.ArrayList;
import java.util.List;

import org.antlr.v4.runtime.Token;

import cfml.parsing.cfscript.CFExpression;
import cfml.parsing.util.ArrayBuilder;

/**
* A <code>static { ... }</code> block inside a component.
*
* Without this the block had no visitor at all, so ANTLR's default <code>visitChildren</code>
* flattened it: <code>component { static { myVar = "v"; } }</code> decompiled to
* <code>component { myVar = 'v' }</code>, losing the fact that the member was static. Members
* are held in source order and reachable through {@link #decomposeScript()}.
*
* A member may carry its own access type -- <code>static { public myVar = "v"; }</code> -- which
* is recorded per member rather than on the block, since that is where CFML puts it.
*/
public class CFStaticBlockStatement extends CFParsedStatement {

private static final long serialVersionUID = 1L;

private final List<CFScriptStatement> members;
private final List<String> accessTypes;

public CFStaticBlockStatement(Token _t, List<CFScriptStatement> _members, List<String> _accessTypes) {
super(_t);
members = _members == null ? new ArrayList<CFScriptStatement>() : _members;
accessTypes = _accessTypes == null ? new ArrayList<String>() : _accessTypes;
for (CFScriptStatement member : members) {
if (member != null) {
member.setParent(this);
}
}
}

/** The block's members, in source order. */
public List<CFScriptStatement> getMembers() {
return members;
}

/**
* The access type written on each member, positionally aligned with {@link #getMembers()}.
* Null where a member carried none, which is the common case.
*/
public List<String> getAccessTypes() {
return accessTypes;
}

@Override
public String Decompile(int indent) {
StringBuilder sb = new StringBuilder();
sb.append(Indent(indent));
sb.append("static {\n");
for (int i = 0; i < members.size(); i++) {
sb.append(Indent(indent + 2));
String accessType = i < accessTypes.size() ? accessTypes.get(i) : null;
if (accessType != null) {
sb.append(accessType).append(" ");
}
sb.append(members.get(i).Decompile(0));
sb.append(";\n");
}
sb.append(Indent(indent)).append("}");
return sb.toString();
}

@Override
public List<CFExpression> decomposeExpression() {
return ArrayBuilder.createCFExpression();
}

@Override
public List<CFScriptStatement> decomposeScript() {
return members;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@
import cfml.CFSCRIPTParser.StartExpressionContext;
import cfml.CFSCRIPTParser.StatementContext;
import cfml.CFSCRIPTParser.SwitchStatementContext;
import cfml.CFSCRIPTParser.StaticBlockContext;
import cfml.CFSCRIPTParser.StaticMemberContext;
import cfml.CFSCRIPTParser.TemplateBlockContext;
import cfml.CFSCRIPTParser.TagFunctionStatementContext;
import cfml.CFSCRIPTParser.TagStatementContext;
Expand Down Expand Up @@ -99,6 +101,7 @@
import cfml.parsing.cfscript.script.CFScriptStatement;
import cfml.parsing.cfscript.script.CFSwitchStatement;
import cfml.parsing.cfscript.script.CFTagThrowStatement;
import cfml.parsing.cfscript.script.CFStaticBlockStatement;
import cfml.parsing.cfscript.script.CFTemplateBlockStatement;
import cfml.parsing.cfscript.script.CFThreadStatement;
import cfml.parsing.cfscript.script.CFThrowStatement;
Expand Down Expand Up @@ -590,6 +593,22 @@ public CFScriptStatement visitExitStatement(ExitStatementContext ctx) {
return exitStatement;
}

@Override
public CFScriptStatement visitStaticBlock(StaticBlockContext ctx) {
// Without a visitor here the block was flattened by visitChildren: static { myVar = "v"; }
// decompiled to plain myVar = 'v', losing the fact that the member was static at all.
List<CFScriptStatement> members = new ArrayList<CFScriptStatement>();
List<String> accessTypes = new ArrayList<String>();
for (StaticMemberContext member : ctx.staticMember()) {
CFScriptStatement statement = visit(member.statement());
if (statement != null) {
members.add(statement);
accessTypes.add(member.accessType() == null ? null : member.accessType().getText());
}
}
return new CFStaticBlockStatement(ctx.STATIC().getSymbol(), members, accessTypes);
}

@Override
public CFScriptStatement visitTemplateBlock(TemplateBlockContext ctx) {
// The body is markup, not cfscript, so it is kept verbatim rather than modelled. Only the
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
component {
static {
public myPublic = "p";
private myPrivate = "q";
myPlain = "r";
}

function f() {
return myPlain;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/*===TOKENS===*/
COMPONENT <component>
'{' <{>
Hidden:NEWLINE <>
STATIC <static>
'{' <{>
Hidden:NEWLINE <>
PUBLIC <public>
IDENTIFIER <myPublic>
'=' <=>
OPEN_STRING <">
STRING_LITERAL <p>
CLOSE_STRING <">
';' <;>
Hidden:NEWLINE <>
PRIVATE <private>
IDENTIFIER <myPrivate>
'=' <=>
OPEN_STRING <">
STRING_LITERAL <q>
CLOSE_STRING <">
';' <;>
Hidden:NEWLINE <>
IDENTIFIER <myPlain>
'=' <=>
OPEN_STRING <">
STRING_LITERAL <r>
CLOSE_STRING <">
';' <;>
Hidden:NEWLINE <>
'}' <}>
Hidden:NEWLINE <>
FUNCTION <function>
IDENTIFIER <f>
'(' <(>
')' <)>
'{' <{>
Hidden:NEWLINE <>
RETURN <return>
IDENTIFIER <myPlain>
';' <;>
Hidden:NEWLINE <>
'}' <}>
Hidden:NEWLINE <>
'}' <}>
Hidden:NEWLINE <>
/*===TREE===*/
(scriptBlock
(componentDeclaration
component
(componentGuts
{
(element
(staticBlock
static
{
(staticMember
(accessType public)
(statement
(assignmentExpression
(startExpression
(baseExpression (unaryExpression (memberExpression (identifier myPublic))))
)
=
(startExpression
(baseExpression
(unaryExpression
(primaryExpression
(literalExpression (stringLiteral " (stringLiteralPart p) "))
)
)
)
)
)
(endOfStatement ;)
)
)
(staticMember
(accessType private)
(statement
(assignmentExpression
(startExpression
(baseExpression (unaryExpression (memberExpression (identifier myPrivate))))
)
=
(startExpression
(baseExpression
(unaryExpression
(primaryExpression
(literalExpression (stringLiteral " (stringLiteralPart q) "))
)
)
)
)
)
(endOfStatement ;)
)
)
(staticMember
(statement
(assignmentExpression
(startExpression
(baseExpression (unaryExpression (memberExpression (identifier myPlain))))
)
=
(startExpression
(baseExpression
(unaryExpression
(primaryExpression
(literalExpression (stringLiteral " (stringLiteralPart r) "))
)
)
)
)
)
(endOfStatement ;)
)
)
}
)
)
(element
(functionDeclaration
function
(identifier f)
(
parameterList
)
(compoundStatement
{
(statement
(returnStatement
return
(anExpression
(startExpression
(baseExpression (unaryExpression (memberExpression (identifier myPlain))))
)
)
)
(endOfStatement ;)
)
}
)
)
)
}
)
)
)
/*======*/
/*===DECOMPILE===*/
component {
{
static {
public myPublic = 'p';
private myPrivate = 'q';
myPlain = 'r';
};
public function f() {
return myPlain;

};

}
}
/*======*/
Loading