JS: Extract placeholders in HTML

This commit is contained in:
Asger Feldthaus
2021-07-20 10:33:54 +02:00
parent b1ce3d1c5a
commit 8666bc1894
8 changed files with 948 additions and 25 deletions

View File

@@ -81,8 +81,7 @@ public class HTMLExtractor implements IExtractor {
source,
content.getBegin(),
isTypeScript,
elt,
context);
context.getNodeLabel(elt));
}
}
} else {
@@ -93,6 +92,14 @@ public class HTMLExtractor implements IExtractor {
// ignore empty attributes
if (attr.getValue() == null || attr.getValue().isEmpty()) continue;
extractTemplateTags(
textualExtractor,
scopeManager,
attr.getSource(),
attr.getBegin(),
attr.getEnd(),
() -> context.getNodeLabel(attr));
String source = attr.getValue();
int valueStart = attr.getValueSegment().getBegin();
if (JS_ATTRIBUTE.matcher(attr.getName()).matches()) {
@@ -104,8 +111,7 @@ public class HTMLExtractor implements IExtractor {
source,
valueStart,
false /* isTypeScript */,
attr,
context);
context.getNodeLabel(attr));
} else if (isAngularTemplateAttributeName(attr.getName())) {
// For an attribute *ngFor="let var of EXPR", start parsing at EXPR
int offset = 0;
@@ -125,8 +131,7 @@ public class HTMLExtractor implements IExtractor {
source,
valueStart + offset,
false /* isTypeScript */,
attr,
context);
context.getNodeLabel(attr));
} else if (source.startsWith("javascript:")) {
source = source.substring(11);
extractSnippet(
@@ -137,13 +142,19 @@ public class HTMLExtractor implements IExtractor {
source,
valueStart + 11,
false /* isTypeScript */,
attr,
context);
context.getNodeLabel(attr));
}
}
}
}
@Override
public void handleText(
Source src, int textBegin, int textEnd, Label parentLabel, boolean isCData) {
extractTemplateTags(
textualExtractor, scopeManager, src, textBegin, textEnd, () -> parentLabel);
}
@Override
public boolean shouldExtractAttributes(Element element) {
Attributes attributes = element.getAttributes();
@@ -294,8 +305,7 @@ public class HTMLExtractor implements IExtractor {
String source,
int offset,
boolean isTypeScript,
Segment parentHtmlNode,
HtmlPopulator.Context context) {
Label parentLabel) {
TrapWriter trapWriter = textualExtractor.getTrapwriter();
LocationManager locationManager = textualExtractor.getLocationManager();
// JavaScript AST extraction does not currently support source maps, so just set
@@ -330,7 +340,7 @@ public class HTMLExtractor implements IExtractor {
scriptLocationManager.getFileLabel(),
scriptLocationManager.getStartLine(),
scriptLocationManager.getStartColumn());
emitTopLevelXmlNodeBinding(parentHtmlNode, topLevelLabel, context, trapWriter);
emitTopLevelXmlNodeBinding(parentLabel, topLevelLabel, trapWriter);
// Note: LoC info is accounted for later, so not added here.
return;
}
@@ -347,7 +357,7 @@ public class HTMLExtractor implements IExtractor {
Pair<Label, LoCInfo> result = extractor.extract(tx, source, toplevelKind, scopeManager);
Label toplevelLabel = result.fst();
if (toplevelLabel != null) { // can be null when script ends up being parsed as JSON
emitTopLevelXmlNodeBinding(parentHtmlNode, toplevelLabel, context, trapWriter);
emitTopLevelXmlNodeBinding(parentLabel, toplevelLabel, trapWriter);
}
locInfo.add(result.snd());
} catch (ParseError e) {
@@ -356,8 +366,88 @@ public class HTMLExtractor implements IExtractor {
}
}
private void emitTopLevelXmlNodeBinding(Segment parentHtmlNode, Label topLevelLabel, HtmlPopulator.Context context, TrapWriter writer) {
Label htmlNodeLabel = context.getNodeLabel(parentHtmlNode);
private void emitTopLevelXmlNodeBinding(
Label htmlNodeLabel, Label topLevelLabel, TrapWriter writer) {
writer.addTuple("toplevel_parent_xml_node", topLevelLabel, htmlNodeLabel);
}
private static final String MUSTACHE_TAG_DOUBLE = "\\{\\{(?!\\{)(.*?)\\}\\}"; // {{ x }}
private static final String MUSTACHE_TAG_TRIPLE = "\\{\\{\\{(.*?)\\}\\}\\}"; // {{{ x }}}
private static final String MUSTACHE_TAG_PERCENT = "\\{%(?!>)(.*?)%\\}"; // {% x %}
private static final String EJS_TAG = "<%(?![%<>}])[-=]?(.*?)[_-]?%>"; // <% x %>
/** Pattern for a template tag whose contents should be parsed as an expression */
private static final Pattern TEMPLATE_EXPR_OPENING_TAG = Pattern.compile("^(?:\\{\\{\\{?|<%[-=])"); // {{, {{{, <%=, <%-
private static final Pattern TEMPLATE_TAGS =
Pattern.compile(
StringUtil.glue(
"|", MUSTACHE_TAG_DOUBLE, MUSTACHE_TAG_TRIPLE, MUSTACHE_TAG_PERCENT, EJS_TAG),
Pattern.DOTALL);
private void extractTemplateTags(
TextualExtractor textualExtractor,
ScopeManager scopeManager,
Source root,
int start,
int end,
Supplier<Label> parentLabel) {
if (isEmbedded) return; // Do not extract template tags for HTML snippets embedded in a JS file
LocationManager locationManager = textualExtractor.getLocationManager();
TrapWriter trapwriter = textualExtractor.getTrapwriter();
Matcher m = TEMPLATE_TAGS.matcher(textualExtractor.getSource()).region(start, end);
while (m.find()) {
int startOffset = m.start();
int endOffset = m.end();
if (endOffset - startOffset > 10_000) {
// Do not extract long template strings as they're likely to be FP matches and
// unlikely to be parsed correctly.
continue;
}
// Emit an entity for the template tag
Label lbl = trapwriter.freshLabel();
String rawText = m.group();
trapwriter.addTuple("template_placeholder_tag_info", lbl, parentLabel.get(), rawText);
// Emit location
Position startPos = textualExtractor.getSourceMap().getStart(startOffset);
Position endPos = textualExtractor.getSourceMap().getEnd(endOffset - 1);
int endColumn = endPos.getColumn() - 1; // Convert to inclusive end position (still 1-based)
locationManager.emitFileLocation(
lbl, startPos.getLine(), startPos.getColumn(), endPos.getLine(), endColumn);
// Parse the contents as a template expression, if the delimiter expects an expression.
Matcher delimMatcher = TEMPLATE_EXPR_OPENING_TAG.matcher(rawText);
if (delimMatcher.find()) {
// The body of the template tag is stored in the first capture group of each pattern
int bodyGroup = getNonNullCaptureGroup(m);
if (bodyGroup != -1) {
extractSnippet(
TopLevelKind.ANGULAR_TEMPLATE,
config.withSourceType(SourceType.ANGULAR_TEMPLATE),
scopeManager,
textualExtractor,
m.group(bodyGroup),
m.start(bodyGroup),
false /* isTypeScript */,
lbl);
}
}
}
}
/**
* Returns the index of the first capture group that captured something
* (apart from group zero which is the whole match).
*/
private static int getNonNullCaptureGroup(Matcher m) {
for (int i = 1; i <= m.groupCount(); ++i) {
if (m.group(i) != null) {
return i;
}
}
return -1;
}
}

View File

@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<body>
<h1>{{title}}</h1>
{{{subtitle_html}}}
<p><%- body_html %></p>
<p><%= footer %></p>
<script>
var data1 = {{{ user_data1 }}};
var data2 = {{ user_data2 | json | safe }};
var data3 = <%- user_data3 %>;
</script>
</body>
</html>

View File

@@ -0,0 +1,775 @@
#10000=@"/test.html;sourcefile"
files(#10000,"/test.html","test","html",0)
#10001=@"/;folder"
folders(#10001,"/","")
containerparent(#10001,#10000)
#10002=@"loc,{#10000},0,0,0,0"
locations_default(#10002,#10000,0,0,0,0)
hasLocation(#10000,#10002)
#20000=@"global_scope"
scopes(#20000,0)
#20001=*
#20002=@"script;{#10000},8,13"
#20003=*
lines(#20003,#20002,"","
")
#20004=@"loc,{#10000},8,13,8,12"
locations_default(#20004,#10000,8,13,8,12)
hasLocation(#20003,#20004)
#20005=*
lines(#20005,#20002," var data1 = {{{ user_data1 }}};","
")
#20006=@"loc,{#10000},9,1,9,37"
locations_default(#20006,#10000,9,1,9,37)
hasLocation(#20005,#20006)
indentation(#10000,9," ",6)
#20007=*
lines(#20007,#20002," var data2 = {{ user_data2 | json | safe }};","
")
#20008=@"loc,{#10000},10,1,10,49"
locations_default(#20008,#10000,10,1,10,49)
hasLocation(#20007,#20008)
indentation(#10000,10," ",6)
#20009=*
lines(#20009,#20002," var data3 = <%- user_data3 %>;","
")
#20010=@"loc,{#10000},11,1,11,36"
locations_default(#20010,#10000,11,1,11,36)
hasLocation(#20009,#20010)
indentation(#10000,11," ",6)
#20011=*
lines(#20011,#20002," ","")
#20012=@"loc,{#10000},12,1,12,4"
locations_default(#20012,#10000,12,1,12,4)
hasLocation(#20011,#20012)
numlines(#20002,5,3,0)
#20013=*
tokeninfo(#20013,7,#20002,0,"var")
#20014=@"loc,{#10000},9,7,9,9"
locations_default(#20014,#10000,9,7,9,9)
hasLocation(#20013,#20014)
#20015=*
tokeninfo(#20015,6,#20002,1,"data1")
#20016=@"loc,{#10000},9,11,9,15"
locations_default(#20016,#10000,9,11,9,15)
hasLocation(#20015,#20016)
#20017=*
tokeninfo(#20017,8,#20002,2,"=")
#20018=@"loc,{#10000},9,17,9,17"
locations_default(#20018,#10000,9,17,9,17)
hasLocation(#20017,#20018)
#20019=*
tokeninfo(#20019,8,#20002,3,"}}}")
#20020=@"loc,{#10000},9,34,9,36"
locations_default(#20020,#10000,9,34,9,36)
hasLocation(#20019,#20020)
#20021=*
tokeninfo(#20021,8,#20002,4,";")
#20022=@"loc,{#10000},9,37,9,37"
locations_default(#20022,#10000,9,37,9,37)
hasLocation(#20021,#20022)
#20023=*
tokeninfo(#20023,7,#20002,5,"var")
#20024=@"loc,{#10000},10,7,10,9"
locations_default(#20024,#10000,10,7,10,9)
hasLocation(#20023,#20024)
#20025=*
tokeninfo(#20025,6,#20002,6,"data2")
#20026=@"loc,{#10000},10,11,10,15"
locations_default(#20026,#10000,10,11,10,15)
hasLocation(#20025,#20026)
#20027=*
tokeninfo(#20027,8,#20002,7,"=")
#20028=@"loc,{#10000},10,17,10,17"
locations_default(#20028,#10000,10,17,10,17)
hasLocation(#20027,#20028)
#20029=*
tokeninfo(#20029,8,#20002,8,"}}")
#20030=@"loc,{#10000},10,47,10,48"
locations_default(#20030,#10000,10,47,10,48)
hasLocation(#20029,#20030)
#20031=*
tokeninfo(#20031,8,#20002,9,";")
#20032=@"loc,{#10000},10,49,10,49"
locations_default(#20032,#10000,10,49,10,49)
hasLocation(#20031,#20032)
#20033=*
tokeninfo(#20033,7,#20002,10,"var")
#20034=@"loc,{#10000},11,7,11,9"
locations_default(#20034,#10000,11,7,11,9)
hasLocation(#20033,#20034)
#20035=*
tokeninfo(#20035,6,#20002,11,"data3")
#20036=@"loc,{#10000},11,11,11,15"
locations_default(#20036,#10000,11,11,11,15)
hasLocation(#20035,#20036)
#20037=*
tokeninfo(#20037,8,#20002,12,"=")
#20038=@"loc,{#10000},11,17,11,17"
locations_default(#20038,#10000,11,17,11,17)
hasLocation(#20037,#20038)
#20039=*
tokeninfo(#20039,8,#20002,13,"%>")
#20040=@"loc,{#10000},11,34,11,35"
locations_default(#20040,#10000,11,34,11,35)
hasLocation(#20039,#20040)
#20041=*
tokeninfo(#20041,8,#20002,14,";")
#20042=@"loc,{#10000},11,36,11,36"
locations_default(#20042,#10000,11,36,11,36)
hasLocation(#20041,#20042)
#20043=*
tokeninfo(#20043,0,#20002,15,"")
#20044=@"loc,{#10000},12,5,12,4"
locations_default(#20044,#10000,12,5,12,4)
hasLocation(#20043,#20044)
toplevels(#20002,1)
#20045=@"loc,{#10000},8,13,12,4"
locations_default(#20045,#10000,8,13,12,4)
hasLocation(#20002,#20045)
#20046=@"var;{data1};{#20000}"
variables(#20046,"data1",#20000)
#20047=@"var;{data2};{#20000}"
variables(#20047,"data2",#20000)
#20048=@"var;{data3};{#20000}"
variables(#20048,"data3",#20000)
#20049=*
stmts(#20049,18,#20002,0,"var dat ... a1 }}};")
#20050=@"loc,{#10000},9,7,9,37"
locations_default(#20050,#10000,9,7,9,37)
hasLocation(#20049,#20050)
stmt_containers(#20049,#20002)
#20051=*
exprs(#20051,64,#20049,0,"data1 = ... ta1 }}}")
#20052=@"loc,{#10000},9,11,9,36"
locations_default(#20052,#10000,9,11,9,36)
hasLocation(#20051,#20052)
enclosing_stmt(#20051,#20049)
expr_containers(#20051,#20002)
#20053=*
exprs(#20053,78,#20051,0,"data1")
hasLocation(#20053,#20016)
enclosing_stmt(#20053,#20049)
expr_containers(#20053,#20002)
literals("data1","data1",#20053)
decl(#20053,#20046)
#20054=*
exprs(#20054,120,#20051,1,"{{{ user_data1 }}}")
#20055=@"loc,{#10000},9,19,9,36"
locations_default(#20055,#10000,9,19,9,36)
hasLocation(#20054,#20055)
enclosing_stmt(#20054,#20049)
expr_containers(#20054,#20002)
generated_code_expr_info(#20054,"{{{","}}}"," user_data1 ")
#20056=*
stmts(#20056,18,#20002,1,"var dat ... afe }};")
#20057=@"loc,{#10000},10,7,10,49"
locations_default(#20057,#10000,10,7,10,49)
hasLocation(#20056,#20057)
stmt_containers(#20056,#20002)
#20058=*
exprs(#20058,64,#20056,0,"data2 = ... safe }}")
#20059=@"loc,{#10000},10,11,10,48"
locations_default(#20059,#10000,10,11,10,48)
hasLocation(#20058,#20059)
enclosing_stmt(#20058,#20056)
expr_containers(#20058,#20002)
#20060=*
exprs(#20060,78,#20058,0,"data2")
hasLocation(#20060,#20026)
enclosing_stmt(#20060,#20056)
expr_containers(#20060,#20002)
literals("data2","data2",#20060)
decl(#20060,#20047)
#20061=*
exprs(#20061,120,#20058,1,"{{ user ... safe }}")
#20062=@"loc,{#10000},10,19,10,48"
locations_default(#20062,#10000,10,19,10,48)
hasLocation(#20061,#20062)
enclosing_stmt(#20061,#20056)
expr_containers(#20061,#20002)
generated_code_expr_info(#20061,"{{","}}"," user_data2 | json | safe ")
#20063=*
stmts(#20063,18,#20002,2,"var dat ... ta3 %>;")
#20064=@"loc,{#10000},11,7,11,36"
locations_default(#20064,#10000,11,7,11,36)
hasLocation(#20063,#20064)
stmt_containers(#20063,#20002)
#20065=*
exprs(#20065,64,#20063,0,"data3 = ... ata3 %>")
#20066=@"loc,{#10000},11,11,11,35"
locations_default(#20066,#10000,11,11,11,35)
hasLocation(#20065,#20066)
enclosing_stmt(#20065,#20063)
expr_containers(#20065,#20002)
#20067=*
exprs(#20067,78,#20065,0,"data3")
hasLocation(#20067,#20036)
enclosing_stmt(#20067,#20063)
expr_containers(#20067,#20002)
literals("data3","data3",#20067)
decl(#20067,#20048)
#20068=*
exprs(#20068,120,#20065,1,"<%- user_data3 %>")
#20069=@"loc,{#10000},11,19,11,35"
locations_default(#20069,#10000,11,19,11,35)
hasLocation(#20068,#20069)
enclosing_stmt(#20068,#20063)
expr_containers(#20068,#20002)
generated_code_expr_info(#20068,"<%-","%>"," user_data3 ")
#20070=*
entry_cfg_node(#20070,#20002)
hasLocation(#20070,#20004)
#20071=*
exit_cfg_node(#20071,#20002)
hasLocation(#20071,#20044)
successor(#20063,#20067)
successor(#20068,#20065)
successor(#20067,#20068)
successor(#20065,#20071)
successor(#20056,#20060)
successor(#20061,#20058)
successor(#20060,#20061)
successor(#20058,#20063)
successor(#20049,#20053)
successor(#20054,#20051)
successor(#20053,#20054)
successor(#20051,#20056)
successor(#20070,#20049)
toplevel_parent_xml_node(#20002,#20001)
#20072=*
xmlChars(#20072,"
",#10000,0,0,#10000)
#20073=@"loc,{#10000},1,16,1,16"
locations_default(#20073,#10000,1,16,1,16)
xmllocations(#20072,#20073)
#20074=*
xmlElements(#20074,"html",#10000,1,#10000)
#20075=@"loc,{#10000},2,1,14,7"
locations_default(#20075,#10000,2,1,14,7)
xmllocations(#20074,#20075)
#20076=*
xmlChars(#20076,"
",#20074,0,0,#10000)
#20077=@"loc,{#10000},2,7,3,2"
locations_default(#20077,#10000,2,7,3,2)
xmllocations(#20076,#20077)
#20078=*
xmlChars(#20078,"
",#20074,2,0,#10000)
#20079=@"loc,{#10000},13,10,13,10"
locations_default(#20079,#10000,13,10,13,10)
xmllocations(#20078,#20079)
#20080=*
xmlElements(#20080,"body",#20074,1,#10000)
#20081=@"loc,{#10000},3,3,13,9"
locations_default(#20081,#10000,3,3,13,9)
xmllocations(#20080,#20081)
#20082=*
xmlChars(#20082,"
",#20080,0,0,#10000)
#20083=@"loc,{#10000},3,9,4,4"
locations_default(#20083,#10000,3,9,4,4)
xmllocations(#20082,#20083)
#20084=*
template_placeholder_tag_info(#20084,#20080,"{{{subtitle_html}}}")
#20085=@"loc,{#10000},5,5,5,23"
locations_default(#20085,#10000,5,5,5,23)
hasLocation(#20084,#20085)
#20086=@"script;{#10000},5,8"
#20087=*
lines(#20087,#20086,"subtitle_html","")
#20088=@"loc,{#10000},5,8,5,20"
locations_default(#20088,#10000,5,8,5,20)
hasLocation(#20087,#20088)
numlines(#20086,1,1,0)
#20089=*
tokeninfo(#20089,6,#20086,0,"subtitle_html")
hasLocation(#20089,#20088)
#20090=*
tokeninfo(#20090,0,#20086,1,"")
#20091=@"loc,{#10000},5,21,5,20"
locations_default(#20091,#10000,5,21,5,20)
hasLocation(#20090,#20091)
toplevels(#20086,4)
hasLocation(#20086,#20088)
#20092=@"module;{#10000},5,8"
scopes(#20092,3)
scopenodes(#20086,#20092)
scopenesting(#20092,#20000)
is_module(#20086)
#20093=*
stmts(#20093,2,#20086,0,"subtitle_html")
hasLocation(#20093,#20088)
stmt_containers(#20093,#20086)
#20094=*
exprs(#20094,79,#20093,0,"subtitle_html")
hasLocation(#20094,#20088)
enclosing_stmt(#20094,#20093)
expr_containers(#20094,#20086)
literals("subtitle_html","subtitle_html",#20094)
#20095=@"var;{subtitle_html};{#20092}"
variables(#20095,"subtitle_html",#20092)
bind(#20094,#20095)
#20096=*
entry_cfg_node(#20096,#20086)
#20097=@"loc,{#10000},5,8,5,7"
locations_default(#20097,#10000,5,8,5,7)
hasLocation(#20096,#20097)
#20098=*
exit_cfg_node(#20098,#20086)
hasLocation(#20098,#20091)
successor(#20093,#20094)
successor(#20094,#20098)
successor(#20096,#20093)
toplevel_parent_xml_node(#20086,#20084)
#20099=*
xmlChars(#20099,"
{{{subtitle_html}}}
",#20080,2,0,#10000)
#20100=@"loc,{#10000},4,23,6,4"
locations_default(#20100,#10000,4,23,6,4)
xmllocations(#20099,#20100)
#20101=*
xmlChars(#20101,"
",#20080,4,0,#10000)
#20102=@"loc,{#10000},6,28,7,4"
locations_default(#20102,#10000,6,28,7,4)
xmllocations(#20101,#20102)
#20103=*
xmlChars(#20103,"
",#20080,6,0,#10000)
#20104=@"loc,{#10000},7,25,8,4"
locations_default(#20104,#10000,7,25,8,4)
xmllocations(#20103,#20104)
#20105=*
xmlChars(#20105,"
",#20080,8,0,#10000)
#20106=@"loc,{#10000},12,14,13,2"
locations_default(#20106,#10000,12,14,13,2)
xmllocations(#20105,#20106)
xmlElements(#20001,"script",#20080,7,#10000)
#20107=@"loc,{#10000},8,5,12,13"
locations_default(#20107,#10000,8,5,12,13)
xmllocations(#20001,#20107)
#20108=*
template_placeholder_tag_info(#20108,#20001,"{{{ user_data1 }}}")
locations_default(#20055,#10000,9,19,9,36)
hasLocation(#20108,#20055)
#20109=@"script;{#10000},9,22"
#20110=*
lines(#20110,#20109," user_data1 ","")
#20111=@"loc,{#10000},9,22,9,33"
locations_default(#20111,#10000,9,22,9,33)
hasLocation(#20110,#20111)
indentation(#10000,9," ",1)
numlines(#20109,1,1,0)
#20112=*
tokeninfo(#20112,6,#20109,0,"user_data1")
#20113=@"loc,{#10000},9,23,9,32"
locations_default(#20113,#10000,9,23,9,32)
hasLocation(#20112,#20113)
#20114=*
tokeninfo(#20114,0,#20109,1,"")
#20115=@"loc,{#10000},9,34,9,33"
locations_default(#20115,#10000,9,34,9,33)
hasLocation(#20114,#20115)
toplevels(#20109,4)
hasLocation(#20109,#20111)
#20116=@"module;{#10000},9,22"
scopes(#20116,3)
scopenodes(#20109,#20116)
scopenesting(#20116,#20000)
is_module(#20109)
#20117=*
stmts(#20117,2,#20109,0,"user_data1")
hasLocation(#20117,#20113)
stmt_containers(#20117,#20109)
#20118=*
exprs(#20118,79,#20117,0,"user_data1")
hasLocation(#20118,#20113)
enclosing_stmt(#20118,#20117)
expr_containers(#20118,#20109)
literals("user_data1","user_data1",#20118)
#20119=@"var;{user_data1};{#20116}"
variables(#20119,"user_data1",#20116)
bind(#20118,#20119)
#20120=*
entry_cfg_node(#20120,#20109)
#20121=@"loc,{#10000},9,22,9,21"
locations_default(#20121,#10000,9,22,9,21)
hasLocation(#20120,#20121)
#20122=*
exit_cfg_node(#20122,#20109)
hasLocation(#20122,#20115)
successor(#20117,#20118)
successor(#20118,#20122)
successor(#20120,#20117)
toplevel_parent_xml_node(#20109,#20108)
#20123=*
template_placeholder_tag_info(#20123,#20001,"{{ user_data2 | json | safe }}")
locations_default(#20062,#10000,10,19,10,48)
hasLocation(#20123,#20062)
#20124=@"script;{#10000},10,21"
#20125=*
lines(#20125,#20124," user_data2 | json | safe ","")
#20126=@"loc,{#10000},10,21,10,46"
locations_default(#20126,#10000,10,21,10,46)
hasLocation(#20125,#20126)
indentation(#10000,10," ",1)
numlines(#20124,1,1,0)
#20127=*
tokeninfo(#20127,6,#20124,0,"user_data2")
#20128=@"loc,{#10000},10,22,10,31"
locations_default(#20128,#10000,10,22,10,31)
hasLocation(#20127,#20128)
#20129=*
tokeninfo(#20129,8,#20124,1,"|")
#20130=@"loc,{#10000},10,33,10,33"
locations_default(#20130,#10000,10,33,10,33)
hasLocation(#20129,#20130)
#20131=*
tokeninfo(#20131,6,#20124,2,"json")
#20132=@"loc,{#10000},10,35,10,38"
locations_default(#20132,#10000,10,35,10,38)
hasLocation(#20131,#20132)
#20133=*
tokeninfo(#20133,8,#20124,3,"|")
#20134=@"loc,{#10000},10,40,10,40"
locations_default(#20134,#10000,10,40,10,40)
hasLocation(#20133,#20134)
#20135=*
tokeninfo(#20135,6,#20124,4,"safe")
#20136=@"loc,{#10000},10,42,10,45"
locations_default(#20136,#10000,10,42,10,45)
hasLocation(#20135,#20136)
#20137=*
tokeninfo(#20137,0,#20124,5,"")
#20138=@"loc,{#10000},10,47,10,46"
locations_default(#20138,#10000,10,47,10,46)
hasLocation(#20137,#20138)
toplevels(#20124,4)
hasLocation(#20124,#20126)
#20139=@"module;{#10000},10,21"
scopes(#20139,3)
scopenodes(#20124,#20139)
scopenesting(#20139,#20000)
is_module(#20124)
#20140=*
stmts(#20140,2,#20124,0,"user_da ... | safe")
#20141=@"loc,{#10000},10,22,10,45"
locations_default(#20141,#10000,10,22,10,45)
hasLocation(#20140,#20141)
stmt_containers(#20140,#20124)
#20142=*
exprs(#20142,13,#20140,0,"user_da ... | safe")
hasLocation(#20142,#20141)
enclosing_stmt(#20142,#20140)
expr_containers(#20142,#20124)
#20143=*
exprs(#20143,119,#20142,-1,"safe")
hasLocation(#20143,#20136)
enclosing_stmt(#20143,#20140)
expr_containers(#20143,#20124)
#20144=*
exprs(#20144,0,#20143,0,"safe")
hasLocation(#20144,#20136)
enclosing_stmt(#20144,#20140)
expr_containers(#20144,#20124)
literals("safe","safe",#20144)
#20145=*
exprs(#20145,13,#20142,0,"user_data2 | json")
#20146=@"loc,{#10000},10,22,10,38"
locations_default(#20146,#10000,10,22,10,38)
hasLocation(#20145,#20146)
enclosing_stmt(#20145,#20140)
expr_containers(#20145,#20124)
#20147=*
exprs(#20147,119,#20145,-1,"json")
hasLocation(#20147,#20132)
enclosing_stmt(#20147,#20140)
expr_containers(#20147,#20124)
#20148=*
exprs(#20148,0,#20147,0,"json")
hasLocation(#20148,#20132)
enclosing_stmt(#20148,#20140)
expr_containers(#20148,#20124)
literals("json","json",#20148)
#20149=*
exprs(#20149,79,#20145,0,"user_data2")
hasLocation(#20149,#20128)
enclosing_stmt(#20149,#20140)
expr_containers(#20149,#20124)
literals("user_data2","user_data2",#20149)
#20150=@"var;{user_data2};{#20139}"
variables(#20150,"user_data2",#20139)
bind(#20149,#20150)
#20151=*
entry_cfg_node(#20151,#20124)
#20152=@"loc,{#10000},10,21,10,20"
locations_default(#20152,#10000,10,21,10,20)
hasLocation(#20151,#20152)
#20153=*
exit_cfg_node(#20153,#20124)
hasLocation(#20153,#20138)
successor(#20140,#20143)
successor(#20149,#20145)
successor(#20147,#20149)
successor(#20145,#20142)
successor(#20143,#20147)
successor(#20142,#20153)
successor(#20151,#20140)
toplevel_parent_xml_node(#20124,#20123)
#20154=*
template_placeholder_tag_info(#20154,#20001,"<%- user_data3 %>")
locations_default(#20069,#10000,11,19,11,35)
hasLocation(#20154,#20069)
#20155=@"script;{#10000},11,22"
#20156=*
lines(#20156,#20155," user_data3 ","")
#20157=@"loc,{#10000},11,22,11,33"
locations_default(#20157,#10000,11,22,11,33)
hasLocation(#20156,#20157)
indentation(#10000,11," ",1)
numlines(#20155,1,1,0)
#20158=*
tokeninfo(#20158,6,#20155,0,"user_data3")
#20159=@"loc,{#10000},11,23,11,32"
locations_default(#20159,#10000,11,23,11,32)
hasLocation(#20158,#20159)
#20160=*
tokeninfo(#20160,0,#20155,1,"")
#20161=@"loc,{#10000},11,34,11,33"
locations_default(#20161,#10000,11,34,11,33)
hasLocation(#20160,#20161)
toplevels(#20155,4)
hasLocation(#20155,#20157)
#20162=@"module;{#10000},11,22"
scopes(#20162,3)
scopenodes(#20155,#20162)
scopenesting(#20162,#20000)
is_module(#20155)
#20163=*
stmts(#20163,2,#20155,0,"user_data3")
hasLocation(#20163,#20159)
stmt_containers(#20163,#20155)
#20164=*
exprs(#20164,79,#20163,0,"user_data3")
hasLocation(#20164,#20159)
enclosing_stmt(#20164,#20163)
expr_containers(#20164,#20155)
literals("user_data3","user_data3",#20164)
#20165=@"var;{user_data3};{#20162}"
variables(#20165,"user_data3",#20162)
bind(#20164,#20165)
#20166=*
entry_cfg_node(#20166,#20155)
#20167=@"loc,{#10000},11,22,11,21"
locations_default(#20167,#10000,11,22,11,21)
hasLocation(#20166,#20167)
#20168=*
exit_cfg_node(#20168,#20155)
hasLocation(#20168,#20161)
successor(#20163,#20164)
successor(#20164,#20168)
successor(#20166,#20163)
toplevel_parent_xml_node(#20155,#20154)
#20169=*
xmlChars(#20169,"
var data1 = {{{ user_data1 }}};
var data2 = {{ user_data2 | json | safe }};
var data3 = <%- user_data3 %>;
",#20001,0,0,#10000)
locations_default(#20045,#10000,8,13,12,4)
xmllocations(#20169,#20045)
#20170=*
xmlElements(#20170,"p",#20080,5,#10000)
#20171=@"loc,{#10000},7,5,7,24"
locations_default(#20171,#10000,7,5,7,24)
xmllocations(#20170,#20171)
#20172=*
template_placeholder_tag_info(#20172,#20170,"<%= footer %>")
#20173=@"loc,{#10000},7,8,7,20"
locations_default(#20173,#10000,7,8,7,20)
hasLocation(#20172,#20173)
#20174=@"script;{#10000},7,11"
#20175=*
lines(#20175,#20174," footer ","")
#20176=@"loc,{#10000},7,11,7,18"
locations_default(#20176,#10000,7,11,7,18)
hasLocation(#20175,#20176)
indentation(#10000,7," ",1)
numlines(#20174,1,1,0)
#20177=*
tokeninfo(#20177,6,#20174,0,"footer")
#20178=@"loc,{#10000},7,12,7,17"
locations_default(#20178,#10000,7,12,7,17)
hasLocation(#20177,#20178)
#20179=*
tokeninfo(#20179,0,#20174,1,"")
#20180=@"loc,{#10000},7,19,7,18"
locations_default(#20180,#10000,7,19,7,18)
hasLocation(#20179,#20180)
toplevels(#20174,4)
hasLocation(#20174,#20176)
#20181=@"module;{#10000},7,11"
scopes(#20181,3)
scopenodes(#20174,#20181)
scopenesting(#20181,#20000)
is_module(#20174)
#20182=*
stmts(#20182,2,#20174,0,"footer")
hasLocation(#20182,#20178)
stmt_containers(#20182,#20174)
#20183=*
exprs(#20183,79,#20182,0,"footer")
hasLocation(#20183,#20178)
enclosing_stmt(#20183,#20182)
expr_containers(#20183,#20174)
literals("footer","footer",#20183)
#20184=@"var;{footer};{#20181}"
variables(#20184,"footer",#20181)
bind(#20183,#20184)
#20185=*
entry_cfg_node(#20185,#20174)
#20186=@"loc,{#10000},7,11,7,10"
locations_default(#20186,#10000,7,11,7,10)
hasLocation(#20185,#20186)
#20187=*
exit_cfg_node(#20187,#20174)
hasLocation(#20187,#20180)
successor(#20182,#20183)
successor(#20183,#20187)
successor(#20185,#20182)
toplevel_parent_xml_node(#20174,#20172)
#20188=*
xmlChars(#20188,"<%= footer %>",#20170,0,0,#10000)
locations_default(#20173,#10000,7,8,7,20)
xmllocations(#20188,#20173)
#20189=*
xmlElements(#20189,"p",#20080,3,#10000)
#20190=@"loc,{#10000},6,5,6,27"
locations_default(#20190,#10000,6,5,6,27)
xmllocations(#20189,#20190)
#20191=*
template_placeholder_tag_info(#20191,#20189,"<%- body_html %>")
#20192=@"loc,{#10000},6,8,6,23"
locations_default(#20192,#10000,6,8,6,23)
hasLocation(#20191,#20192)
#20193=@"script;{#10000},6,11"
#20194=*
lines(#20194,#20193," body_html ","")
#20195=@"loc,{#10000},6,11,6,21"
locations_default(#20195,#10000,6,11,6,21)
hasLocation(#20194,#20195)
indentation(#10000,6," ",1)
numlines(#20193,1,1,0)
#20196=*
tokeninfo(#20196,6,#20193,0,"body_html")
#20197=@"loc,{#10000},6,12,6,20"
locations_default(#20197,#10000,6,12,6,20)
hasLocation(#20196,#20197)
#20198=*
tokeninfo(#20198,0,#20193,1,"")
#20199=@"loc,{#10000},6,22,6,21"
locations_default(#20199,#10000,6,22,6,21)
hasLocation(#20198,#20199)
toplevels(#20193,4)
hasLocation(#20193,#20195)
#20200=@"module;{#10000},6,11"
scopes(#20200,3)
scopenodes(#20193,#20200)
scopenesting(#20200,#20000)
is_module(#20193)
#20201=*
stmts(#20201,2,#20193,0,"body_html")
hasLocation(#20201,#20197)
stmt_containers(#20201,#20193)
#20202=*
exprs(#20202,79,#20201,0,"body_html")
hasLocation(#20202,#20197)
enclosing_stmt(#20202,#20201)
expr_containers(#20202,#20193)
literals("body_html","body_html",#20202)
#20203=@"var;{body_html};{#20200}"
variables(#20203,"body_html",#20200)
bind(#20202,#20203)
#20204=*
entry_cfg_node(#20204,#20193)
#20205=@"loc,{#10000},6,11,6,10"
locations_default(#20205,#10000,6,11,6,10)
hasLocation(#20204,#20205)
#20206=*
exit_cfg_node(#20206,#20193)
hasLocation(#20206,#20199)
successor(#20201,#20202)
successor(#20202,#20206)
successor(#20204,#20201)
toplevel_parent_xml_node(#20193,#20191)
#20207=*
xmlChars(#20207,"<%- body_html %>",#20189,0,0,#10000)
locations_default(#20192,#10000,6,8,6,23)
xmllocations(#20207,#20192)
#20208=*
xmlElements(#20208,"h1",#20080,1,#10000)
#20209=@"loc,{#10000},4,5,4,22"
locations_default(#20209,#10000,4,5,4,22)
xmllocations(#20208,#20209)
#20210=*
template_placeholder_tag_info(#20210,#20208,"{{title}}")
#20211=@"loc,{#10000},4,9,4,17"
locations_default(#20211,#10000,4,9,4,17)
hasLocation(#20210,#20211)
#20212=@"script;{#10000},4,11"
#20213=*
lines(#20213,#20212,"title","")
#20214=@"loc,{#10000},4,11,4,15"
locations_default(#20214,#10000,4,11,4,15)
hasLocation(#20213,#20214)
numlines(#20212,1,1,0)
#20215=*
tokeninfo(#20215,6,#20212,0,"title")
hasLocation(#20215,#20214)
#20216=*
tokeninfo(#20216,0,#20212,1,"")
#20217=@"loc,{#10000},4,16,4,15"
locations_default(#20217,#10000,4,16,4,15)
hasLocation(#20216,#20217)
toplevels(#20212,4)
hasLocation(#20212,#20214)
#20218=@"module;{#10000},4,11"
scopes(#20218,3)
scopenodes(#20212,#20218)
scopenesting(#20218,#20000)
is_module(#20212)
#20219=*
stmts(#20219,2,#20212,0,"title")
hasLocation(#20219,#20214)
stmt_containers(#20219,#20212)
#20220=*
exprs(#20220,79,#20219,0,"title")
hasLocation(#20220,#20214)
enclosing_stmt(#20220,#20219)
expr_containers(#20220,#20212)
literals("title","title",#20220)
#20221=@"var;{title};{#20218}"
variables(#20221,"title",#20218)
bind(#20220,#20221)
#20222=*
entry_cfg_node(#20222,#20212)
#20223=@"loc,{#10000},4,11,4,10"
locations_default(#20223,#10000,4,11,4,10)
hasLocation(#20222,#20223)
#20224=*
exit_cfg_node(#20224,#20212)
hasLocation(#20224,#20217)
successor(#20219,#20220)
successor(#20220,#20224)
successor(#20222,#20219)
toplevel_parent_xml_node(#20212,#20210)
#20225=*
xmlChars(#20225,"{{title}}",#20208,0,0,#10000)
locations_default(#20211,#10000,4,9,4,17)
xmllocations(#20225,#20211)
numlines(#10000,14,10,0)
filetype(#10000,"html")

View File

@@ -2894,18 +2894,17 @@ class ImportMetaExpr extends @import_meta_expr, Expr {
*/
class GeneratedCodeExpr extends @generated_code_expr, Expr {
/** Gets the opening delimiter, such as `{{` or `{{{`. */
string getOpeningDelimiter() {
generated_code_expr_info(this, result, _, _)
}
string getOpeningDelimiter() { generated_code_expr_info(this, result, _, _) }
/** Gets the closing delimiter, such as `}}` or `}}}`. */
string getClosingDelimiter() {
generated_code_expr_info(this, _, result, _)
}
string getClosingDelimiter() { generated_code_expr_info(this, _, result, _) }
/** Gets the text between the delimiters, including any surrounding whitespace, such as the `x` in `{{x}}`. */
string getBody() {
generated_code_expr_info(this, _, _, result)
string getBody() { generated_code_expr_info(this, _, _, result) }
/** Gets the placeholder tag that was parsed as an expression. */
Templating::TemplatePlaceholderTag getPlaceholderTag() {
result.getLocation() = this.getLocation()
}
override string getAPrimaryQlClass() { result = "GeneratedCodeExpr" }

View File

@@ -1268,6 +1268,22 @@ module DataFlow {
override string toString() { result = variable.getName() }
}
/** A data flow node representing the value plugged into a template tag. */
class TemplatePlaceholderTagNode extends Node, TTemplatePlaceholderTag {
/** Gets the template tag represented by this data flow node. */
Templating::TemplatePlaceholderTag getTag() { this = TTemplatePlaceholderTag(result) }
override BasicBlock getBasicBlock() { none() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
getTag().getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
override string toString() { result = getTag().toString() }
}
/**
* INTERNAL. DO NOT USE.
*

View File

@@ -30,4 +30,5 @@ newtype TNode =
TFunctionReturnNode(Function f) or
TExceptionalFunctionReturnNode(Function f) or
TExceptionalInvocationReturnNode(InvokeExpr e) or
TGlobalAccessPathRoot()
TGlobalAccessPathRoot() or
TTemplatePlaceholderTag(Templating::TemplatePlaceholderTag tag)

View File

@@ -48,4 +48,23 @@ module Templating {
string getDelimiterMatchingRegexpWithPrefix(string prefix) {
result = "(?s)" + prefix + "(" + concat("\\Q" + getADelimiter() + "\\E", "|") + ").*"
}
/** A placeholder tag for a templating engine. */
class TemplatePlaceholderTag extends @template_placeholder_tag, Locatable {
override Location getLocation() { hasLocation(this, result) }
override string toString() { template_placeholder_tag_info(this, _, result) }
/** Gets the full text of the template tag, including delimiters. */
string getRawText() { template_placeholder_tag_info(this, _, result) }
/** Gets the enclosing HTML element, attribute, or file. */
Locatable getParent() { template_placeholder_tag_info(this, result, _) }
/** Gets a data flow node representing the value plugged into this placeholder. */
DataFlow::TemplatePlaceholderTagNode asDataFlowNode() { result.getTag() = this }
/** Gets the top-level containing the template expression to be inserted at this placeholder. */
Angular2::TemplateTopLevel getInnerTopLevel() { toplevel_parent_xml_node(result, this) }
}
}

View File

@@ -132,7 +132,7 @@ is_nodejs (int tl: @toplevel ref);
is_es2015_module (int tl: @toplevel ref);
is_closure_module (int tl: @toplevel ref);
@xml_node_with_code = @xmlelement | @xmlattribute;
@xml_node_with_code = @xmlelement | @xmlattribute | @template_placeholder_tag;
toplevel_parent_xml_node(
unique int toplevel: @toplevel ref,
int xmlnode: @xml_node_with_code ref);
@@ -424,6 +424,14 @@ generated_code_expr_info(
varchar(900) body: string ref
);
@template_placeholder_tag_parent = @xmlelement | @xmlattribute | @file;
template_placeholder_tag_info(
unique int node: @template_placeholder_tag,
int parentNode: @template_placeholder_tag_parent ref,
varchar(900) raw: string ref
);
// scopes
scopes (unique int id: @scope,
int kind: int ref);
@@ -987,7 +995,8 @@ case @json_value.kind of
| @jsdoc | @jsdoc_type_expr | @jsdoc_tag
| @yaml_locatable
| @xmllocatable
| @configLocatable;
| @configLocatable
| @template_placeholder_tag;
hasLocation (unique int locatable: @locatable ref,
int location: @location ref);