<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[180518] trunk/Source/JavaScriptCore</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/180518">180518</a></dd>
<dt>Author</dt> <dd>saambarati1@gmail.com</dd>
<dt>Date</dt> <dd>2015-02-23 14:10:51 -0800 (Mon, 23 Feb 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>Adjust the ranges of basic block statements in JSC's control flow profiler to be mutually exclusive
https://bugs.webkit.org/show_bug.cgi?id=141095

Reviewed by Mark Lam.

Suppose the control flow of a program forms basic block A with successor block
B. A's end offset will be the *same* as B's start offset in the current architecture
of the control flow profiler. This makes reasoning about the text offsets of
the control flow profiler unsound. To make reasoning about offsets sound, all
basic block ranges should be mutually exclusive.  All calls to emitProfileControlFlow
now pass in the *start* of a basic block as the text offset argument. This simplifies
all calls to emitProfileControlFlow because the previous implementation had a
lot of edge cases for getting the desired basic block text boundaries.

This patch also ensures that the basic block boundary of a block statement
is the exactly the block's open and close brace offsets (inclusive). For example,
in if/for/while statements. This also has the consequence that for statements
like &quot;if (cond) foo();&quot;, the whitespace preceding &quot;foo()&quot; is not part of
the &quot;foo()&quot; basic block, but instead is part of the &quot;if (cond) &quot; basic block.
This is okay because these text offsets aren't meant to be human readable.
Instead, they reflect the text offsets of JSC's AST nodes. The Web Inspector
is the only client of this API and user of these text offsets and it is
not negatively effected by this new behavior.

* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::insertBasicBlockBoundariesForControlFlowProfiler):
When computing basic block boundaries in CodeBlock, we ensure that every
block's end offset is one less than its successor's start offset to
maintain that boundaries' ranges should be mutually exclusive.

* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::BytecodeGenerator):
Because the control flow profiler needs to know which functions
have executed, we can't lazily create functions. This was a bug
from before that was hidden because the Type Profiler was always
enabled when the control flow profiler was enabled when profiling
was turned on from the Web Inspector. But, JSC allows for Control
Flow profiling to be turned on without Type Profiling, so we need
to ensure the Control Flow profiler has all the data it needs.

* bytecompiler/NodesCodegen.cpp:
(JSC::ConditionalNode::emitBytecode):
(JSC::IfElseNode::emitBytecode):
(JSC::WhileNode::emitBytecode):
(JSC::ForNode::emitBytecode):
(JSC::ForInNode::emitMultiLoopBytecode):
(JSC::ForOfNode::emitBytecode):
(JSC::TryNode::emitBytecode):
* jsc.cpp:
(functionHasBasicBlockExecuted):
We now assert that the substring argument is indeed a substring
of the function argument's text because subtle bugs could be
introduced otherwise.

* parser/ASTBuilder.h:
(JSC::ASTBuilder::setStartOffset):
* parser/Nodes.h:
(JSC::Node::setStartOffset):
* parser/Parser.cpp:
(JSC::Parser&lt;LexerType&gt;::parseBlockStatement):
(JSC::Parser&lt;LexerType&gt;::parseStatement):
(JSC::Parser&lt;LexerType&gt;::parseMemberExpression):
For the various function call AST nodes, their m_position member
variable is now the start of the entire function call expression
and not at the start of the open paren of the arguments list.

* runtime/BasicBlockLocation.cpp:
(JSC::BasicBlockLocation::getExecutedRanges):
* runtime/ControlFlowProfiler.cpp:
(JSC::ControlFlowProfiler::getBasicBlocksForSourceID):
Function ranges inserted as gaps should follow the same criteria
that the bytecode generator uses to ensure that basic blocks
start and end offsets are mutually exclusive.

* tests/controlFlowProfiler/brace-location.js: Added.
(foo):
(bar):
(baz):
(testIf):
(testForRegular):
(testForIn):
(testForOf):
(testWhile):
(testIfNoBraces):
(testForRegularNoBraces):
(testForInNoBraces):
(testForOfNoBraces):
(testWhileNoBraces):
* tests/controlFlowProfiler/conditional-expression.js: Added.
(foo):
(bar):
(baz):
(testConditionalBasic):
(testConditionalFunctionCall):
* tests/controlFlowProfiler/driver/driver.js:
(checkBasicBlock):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCorebytecodeCodeBlockcpp">trunk/Source/JavaScriptCore/bytecode/CodeBlock.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCorebytecompilerBytecodeGeneratorcpp">trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCorebytecompilerNodesCodegencpp">trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCorejsccpp">trunk/Source/JavaScriptCore/jsc.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreparserASTBuilderh">trunk/Source/JavaScriptCore/parser/ASTBuilder.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreparserNodesh">trunk/Source/JavaScriptCore/parser/Nodes.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreparserParsercpp">trunk/Source/JavaScriptCore/parser/Parser.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeBasicBlockLocationcpp">trunk/Source/JavaScriptCore/runtime/BasicBlockLocation.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeControlFlowProfilercpp">trunk/Source/JavaScriptCore/runtime/ControlFlowProfiler.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoretestscontrolFlowProfilerdriverdriverjs">trunk/Source/JavaScriptCore/tests/controlFlowProfiler/driver/driver.js</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoretestscontrolFlowProfilerbracelocationjs">trunk/Source/JavaScriptCore/tests/controlFlowProfiler/brace-location.js</a></li>
<li><a href="#trunkSourceJavaScriptCoretestscontrolFlowProfilerconditionalexpressionjs">trunk/Source/JavaScriptCore/tests/controlFlowProfiler/conditional-expression.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (180517 => 180518)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/JavaScriptCore/ChangeLog        2015-02-23 22:10:51 UTC (rev 180518)
</span><span class="lines">@@ -1,3 +1,102 @@
</span><ins>+2015-02-23  Saam Barati  &lt;saambarati1@gmail.com&gt;
+
+        Adjust the ranges of basic block statements in JSC's control flow profiler to be mutually exclusive
+        https://bugs.webkit.org/show_bug.cgi?id=141095
+
+        Reviewed by Mark Lam.
+
+        Suppose the control flow of a program forms basic block A with successor block
+        B. A's end offset will be the *same* as B's start offset in the current architecture 
+        of the control flow profiler. This makes reasoning about the text offsets of
+        the control flow profiler unsound. To make reasoning about offsets sound, all 
+        basic block ranges should be mutually exclusive.  All calls to emitProfileControlFlow 
+        now pass in the *start* of a basic block as the text offset argument. This simplifies 
+        all calls to emitProfileControlFlow because the previous implementation had a
+        lot of edge cases for getting the desired basic block text boundaries.
+
+        This patch also ensures that the basic block boundary of a block statement 
+        is the exactly the block's open and close brace offsets (inclusive). For example,
+        in if/for/while statements. This also has the consequence that for statements 
+        like &quot;if (cond) foo();&quot;, the whitespace preceding &quot;foo()&quot; is not part of 
+        the &quot;foo()&quot; basic block, but instead is part of the &quot;if (cond) &quot; basic block. 
+        This is okay because these text offsets aren't meant to be human readable.
+        Instead, they reflect the text offsets of JSC's AST nodes. The Web Inspector 
+        is the only client of this API and user of these text offsets and it is 
+        not negatively effected by this new behavior.
+
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::insertBasicBlockBoundariesForControlFlowProfiler):
+        When computing basic block boundaries in CodeBlock, we ensure that every
+        block's end offset is one less than its successor's start offset to
+        maintain that boundaries' ranges should be mutually exclusive.
+
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::BytecodeGenerator):
+        Because the control flow profiler needs to know which functions
+        have executed, we can't lazily create functions. This was a bug 
+        from before that was hidden because the Type Profiler was always 
+        enabled when the control flow profiler was enabled when profiling 
+        was turned on from the Web Inspector. But, JSC allows for Control 
+        Flow profiling to be turned on without Type Profiling, so we need 
+        to ensure the Control Flow profiler has all the data it needs.
+
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::ConditionalNode::emitBytecode):
+        (JSC::IfElseNode::emitBytecode):
+        (JSC::WhileNode::emitBytecode):
+        (JSC::ForNode::emitBytecode):
+        (JSC::ForInNode::emitMultiLoopBytecode):
+        (JSC::ForOfNode::emitBytecode):
+        (JSC::TryNode::emitBytecode):
+        * jsc.cpp:
+        (functionHasBasicBlockExecuted):
+        We now assert that the substring argument is indeed a substring
+        of the function argument's text because subtle bugs could be
+        introduced otherwise.
+
+        * parser/ASTBuilder.h:
+        (JSC::ASTBuilder::setStartOffset):
+        * parser/Nodes.h:
+        (JSC::Node::setStartOffset):
+        * parser/Parser.cpp:
+        (JSC::Parser&lt;LexerType&gt;::parseBlockStatement):
+        (JSC::Parser&lt;LexerType&gt;::parseStatement):
+        (JSC::Parser&lt;LexerType&gt;::parseMemberExpression):
+        For the various function call AST nodes, their m_position member 
+        variable is now the start of the entire function call expression 
+        and not at the start of the open paren of the arguments list.
+
+        * runtime/BasicBlockLocation.cpp:
+        (JSC::BasicBlockLocation::getExecutedRanges):
+        * runtime/ControlFlowProfiler.cpp:
+        (JSC::ControlFlowProfiler::getBasicBlocksForSourceID):
+        Function ranges inserted as gaps should follow the same criteria
+        that the bytecode generator uses to ensure that basic blocks
+        start and end offsets are mutually exclusive.
+
+        * tests/controlFlowProfiler/brace-location.js: Added.
+        (foo):
+        (bar):
+        (baz):
+        (testIf):
+        (testForRegular):
+        (testForIn):
+        (testForOf):
+        (testWhile):
+        (testIfNoBraces):
+        (testForRegularNoBraces):
+        (testForInNoBraces):
+        (testForOfNoBraces):
+        (testWhileNoBraces):
+        * tests/controlFlowProfiler/conditional-expression.js: Added.
+        (foo):
+        (bar):
+        (baz):
+        (testConditionalBasic):
+        (testConditionalFunctionCall):
+        * tests/controlFlowProfiler/driver/driver.js:
+        (checkBasicBlock):
+
</ins><span class="cx"> 2015-02-23  Matthew Mirman  &lt;mmirman@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         r9 is volatile on ARMv7 for iOS 3 and up. 
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorebytecodeCodeBlockcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/bytecode/CodeBlock.cpp (180517 => 180518)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/bytecode/CodeBlock.cpp        2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/JavaScriptCore/bytecode/CodeBlock.cpp        2015-02-23 22:10:51 UTC (rev 180518)
</span><span class="lines">@@ -4052,7 +4052,7 @@
</span><span class="cx">         if (i + 1 &lt; offsetsLength) {
</span><span class="cx">             size_t endIdx = bytecodeOffsets[i + 1];
</span><span class="cx">             RELEASE_ASSERT(vm()-&gt;interpreter-&gt;getOpcodeID(instructions[endIdx].u.opcode) == op_profile_control_flow);
</span><del>-            basicBlockEndOffset = instructions[endIdx + 1].u.operand;
</del><ins>+            basicBlockEndOffset = instructions[endIdx + 1].u.operand - 1;
</ins><span class="cx">         } else {
</span><span class="cx">             basicBlockEndOffset = m_sourceOffset + m_ownerExecutable-&gt;source().length() - 1; // Offset before the closing brace.
</span><span class="cx">             basicBlockStartOffset = std::min(basicBlockStartOffset, basicBlockEndOffset); // Some start offsets may be at the closing brace, ensure it is the offset before.
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorebytecompilerBytecodeGeneratorcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp (180517 => 180518)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp        2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp        2015-02-23 22:10:51 UTC (rev 180518)
</span><span class="lines">@@ -338,7 +338,7 @@
</span><span class="cx"> 
</span><span class="cx">     m_symbolTable-&gt;setCaptureEnd(virtualRegisterForLocal(codeBlock-&gt;m_numVars).offset());
</span><span class="cx"> 
</span><del>-    bool canLazilyCreateFunctions = !functionNode-&gt;needsActivationForMoreThanVariables() &amp;&amp; !m_shouldEmitDebugHooks &amp;&amp; !m_vm-&gt;typeProfiler();
</del><ins>+    bool canLazilyCreateFunctions = !functionNode-&gt;needsActivationForMoreThanVariables() &amp;&amp; !m_shouldEmitDebugHooks &amp;&amp; !m_vm-&gt;typeProfiler() &amp;&amp; !m_vm-&gt;controlFlowProfiler();
</ins><span class="cx">     m_firstLazyFunction = codeBlock-&gt;m_numVars;
</span><span class="cx">     if (!shouldCaptureAllTheThings) {
</span><span class="cx">         for (size_t i = 0; i &lt; functionStack.size(); ++i) {
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorebytecompilerNodesCodegencpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp (180517 => 180518)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp        2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp        2015-02-23 22:10:51 UTC (rev 180518)
</span><span class="lines">@@ -1444,12 +1444,12 @@
</span><span class="cx">     generator.emitJump(afterElse.get());
</span><span class="cx"> 
</span><span class="cx">     generator.emitLabel(beforeElse.get());
</span><del>-    generator.emitProfileControlFlow(m_expr2-&gt;startOffset());
</del><ins>+    generator.emitProfileControlFlow(m_expr1-&gt;endOffset() + 1);
</ins><span class="cx">     generator.emitNode(newDst.get(), m_expr2);
</span><span class="cx"> 
</span><span class="cx">     generator.emitLabel(afterElse.get());
</span><span class="cx"> 
</span><del>-    generator.emitProfileControlFlow(m_expr2-&gt;endOffset());
</del><ins>+    generator.emitProfileControlFlow(m_expr2-&gt;endOffset() + 1);
</ins><span class="cx"> 
</span><span class="cx">     return newDst.get();
</span><span class="cx"> }
</span><span class="lines">@@ -1897,12 +1897,13 @@
</span><span class="cx">     generator.emitLabel(beforeElse.get());
</span><span class="cx"> 
</span><span class="cx">     if (m_elseBlock) {
</span><del>-        generator.emitProfileControlFlow(m_ifBlock-&gt;endOffset());
</del><ins>+        generator.emitProfileControlFlow(m_ifBlock-&gt;endOffset() + (m_ifBlock-&gt;isBlock() ? 1 : 0));
</ins><span class="cx">         generator.emitNode(dst, m_elseBlock);
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     generator.emitLabel(afterElse.get());
</span><del>-    generator.emitProfileControlFlow(m_elseBlock ? m_elseBlock-&gt;endOffset() : m_ifBlock-&gt;endOffset());
</del><ins>+    StatementNode* endingBlock = m_elseBlock ? m_elseBlock : m_ifBlock;
+    generator.emitProfileControlFlow(endingBlock-&gt;endOffset() + (endingBlock-&gt;isBlock() ? 1 : 0));
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> // ------------------------------ DoWhileNode ----------------------------------
</span><span class="lines">@@ -1948,7 +1949,7 @@
</span><span class="cx"> 
</span><span class="cx">     generator.emitLabel(scope-&gt;breakTarget());
</span><span class="cx"> 
</span><del>-    generator.emitProfileControlFlow(endOffset());
</del><ins>+    generator.emitProfileControlFlow(m_statement-&gt;endOffset() + (m_statement-&gt;isBlock() ? 1 : 0));
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> // ------------------------------ ForNode --------------------------------------
</span><span class="lines">@@ -1983,7 +1984,7 @@
</span><span class="cx">         generator.emitJump(topOfLoop.get());
</span><span class="cx"> 
</span><span class="cx">     generator.emitLabel(scope-&gt;breakTarget());
</span><del>-    generator.emitProfileControlFlow(endOffset());
</del><ins>+    generator.emitProfileControlFlow(m_statement-&gt;endOffset() + (m_statement-&gt;isBlock() ? 1 : 0));
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> // ------------------------------ ForInNode ------------------------------------
</span><span class="lines">@@ -2100,6 +2101,8 @@
</span><span class="cx">     generator.emitNode(base.get(), m_expr);
</span><span class="cx">     RefPtr&lt;RegisterID&gt; local = this-&gt;tryGetBoundLocal(generator);
</span><span class="cx"> 
</span><ins>+    int profilerStartOffset = m_statement-&gt;startOffset();
+    int profilerEndOffset = m_statement-&gt;endOffset() + (m_statement-&gt;isBlock() ? 1 : 0);
</ins><span class="cx">     // Indexed property loop.
</span><span class="cx">     {
</span><span class="cx">         LabelScopePtr scope = generator.newLabelScope(LabelScope::Loop);
</span><span class="lines">@@ -2121,13 +2124,13 @@
</span><span class="cx">         generator.emitToIndexString(propertyName.get(), i.get());
</span><span class="cx">         this-&gt;emitLoopHeader(generator, propertyName.get());
</span><span class="cx"> 
</span><del>-        generator.emitProfileControlFlow(m_statement-&gt;startOffset());
</del><ins>+        generator.emitProfileControlFlow(profilerStartOffset);
</ins><span class="cx"> 
</span><span class="cx">         generator.pushIndexedForInScope(local.get(), i.get());
</span><span class="cx">         generator.emitNode(dst, m_statement);
</span><span class="cx">         generator.popIndexedForInScope(local.get());
</span><span class="cx"> 
</span><del>-        generator.emitProfileControlFlow(m_statement-&gt;endOffset());
</del><ins>+        generator.emitProfileControlFlow(profilerEndOffset);
</ins><span class="cx"> 
</span><span class="cx">         generator.emitLabel(scope-&gt;continueTarget());
</span><span class="cx">         generator.emitInc(i.get());
</span><span class="lines">@@ -2159,13 +2162,13 @@
</span><span class="cx"> 
</span><span class="cx">         this-&gt;emitLoopHeader(generator, propertyName.get());
</span><span class="cx"> 
</span><del>-        generator.emitProfileControlFlow(m_statement-&gt;startOffset());
</del><ins>+        generator.emitProfileControlFlow(profilerStartOffset);
</ins><span class="cx"> 
</span><span class="cx">         generator.pushStructureForInScope(local.get(), i.get(), propertyName.get(), structureEnumerator.get());
</span><span class="cx">         generator.emitNode(dst, m_statement);
</span><span class="cx">         generator.popStructureForInScope(local.get());
</span><span class="cx"> 
</span><del>-        generator.emitProfileControlFlow(m_statement-&gt;endOffset());
</del><ins>+        generator.emitProfileControlFlow(profilerEndOffset);
</ins><span class="cx"> 
</span><span class="cx">         generator.emitLabel(scope-&gt;continueTarget());
</span><span class="cx">         generator.emitInc(i.get());
</span><span class="lines">@@ -2196,7 +2199,7 @@
</span><span class="cx"> 
</span><span class="cx">         this-&gt;emitLoopHeader(generator, propertyName.get());
</span><span class="cx"> 
</span><del>-        generator.emitProfileControlFlow(m_statement-&gt;startOffset());
</del><ins>+        generator.emitProfileControlFlow(profilerStartOffset);
</ins><span class="cx"> 
</span><span class="cx">         generator.emitNode(dst, m_statement);
</span><span class="cx"> 
</span><span class="lines">@@ -2217,7 +2220,7 @@
</span><span class="cx"> 
</span><span class="cx">     generator.emitDebugHook(WillExecuteStatement, firstLine(), startOffset(), lineStartOffset());
</span><span class="cx">     generator.emitLabel(end.get());
</span><del>-    generator.emitProfileControlFlow(m_statement-&gt;endOffset());
</del><ins>+    generator.emitProfileControlFlow(profilerEndOffset);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void ForInNode::emitBytecode(BytecodeGenerator&amp; generator, RegisterID* dst)
</span><span class="lines">@@ -2285,7 +2288,7 @@
</span><span class="cx">         generator.emitNode(dst, m_statement);
</span><span class="cx">     };
</span><span class="cx">     generator.emitEnumeration(this, m_expr, extractor);
</span><del>-    generator.emitProfileControlFlow(m_statement-&gt;endOffset());
</del><ins>+    generator.emitProfileControlFlow(m_statement-&gt;endOffset() + (m_statement-&gt;isBlock() ? 1 : 0));
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> // ------------------------------ ContinueNode ---------------------------------
</span><span class="lines">@@ -2631,7 +2634,7 @@
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         generator.emitPushCatchScope(generator.scopeRegister(), m_exceptionIdent, exceptionRegister.get(), DontDelete);
</span><del>-        generator.emitProfileControlFlow(m_tryBlock-&gt;endOffset());
</del><ins>+        generator.emitProfileControlFlow(m_tryBlock-&gt;endOffset() + 1);
</ins><span class="cx">         generator.emitNode(dst, m_catchBlock);
</span><span class="cx">         generator.emitPopScope(generator.scopeRegister());
</span><span class="cx">         generator.emitLabel(catchEndLabel.get());
</span><span class="lines">@@ -2644,12 +2647,12 @@
</span><span class="cx"> 
</span><span class="cx">         RefPtr&lt;Label&gt; finallyEndLabel = generator.newLabel();
</span><span class="cx"> 
</span><del>-        int finallyStartOffset = m_catchBlock ? m_catchBlock-&gt;endOffset() : m_tryBlock-&gt;endOffset();
</del><ins>+        int finallyStartOffset = m_catchBlock ? m_catchBlock-&gt;endOffset() + 1 : m_tryBlock-&gt;endOffset() + 1;
</ins><span class="cx"> 
</span><span class="cx">         // Normal path: run the finally code, and jump to the end.
</span><span class="cx">         generator.emitProfileControlFlow(finallyStartOffset);
</span><span class="cx">         generator.emitNode(dst, m_finallyBlock);
</span><del>-        generator.emitProfileControlFlow(m_finallyBlock-&gt;endOffset());
</del><ins>+        generator.emitProfileControlFlow(m_finallyBlock-&gt;endOffset() + 1);
</ins><span class="cx">         generator.emitJump(finallyEndLabel.get());
</span><span class="cx"> 
</span><span class="cx">         // Uncaught exception path: invoke the finally block, then re-throw the exception.
</span><span class="lines">@@ -2659,9 +2662,9 @@
</span><span class="cx">         generator.emitThrow(tempExceptionRegister.get());
</span><span class="cx"> 
</span><span class="cx">         generator.emitLabel(finallyEndLabel.get());
</span><del>-        generator.emitProfileControlFlow(m_finallyBlock-&gt;endOffset());
</del><ins>+        generator.emitProfileControlFlow(m_finallyBlock-&gt;endOffset() + 1);
</ins><span class="cx">     } else
</span><del>-        generator.emitProfileControlFlow(m_catchBlock-&gt;endOffset());
</del><ins>+        generator.emitProfileControlFlow(m_catchBlock-&gt;endOffset() + 1);
</ins><span class="cx"> 
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorejsccpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/jsc.cpp (180517 => 180518)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/jsc.cpp        2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/JavaScriptCore/jsc.cpp        2015-02-23 22:10:51 UTC (rev 180518)
</span><span class="lines">@@ -1127,6 +1127,7 @@
</span><span class="cx">     RELEASE_ASSERT(exec-&gt;argument(1).isString());
</span><span class="cx">     String substring = exec-&gt;argument(1).getString(exec);
</span><span class="cx">     String sourceCodeText = executable-&gt;source().toString();
</span><ins>+    RELEASE_ASSERT(sourceCodeText.contains(substring));
</ins><span class="cx">     int offset = sourceCodeText.find(substring) + executable-&gt;source().startOffset();
</span><span class="cx">     
</span><span class="cx">     bool hasExecuted = exec-&gt;vm().controlFlowProfiler()-&gt;hasBasicBlockAtTextOffsetBeenExecuted(offset, executable-&gt;sourceID(), exec-&gt;vm());
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreparserASTBuilderh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/parser/ASTBuilder.h (180517 => 180518)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/parser/ASTBuilder.h        2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/JavaScriptCore/parser/ASTBuilder.h        2015-02-23 22:10:51 UTC (rev 180518)
</span><span class="lines">@@ -713,6 +713,11 @@
</span><span class="cx">     {
</span><span class="cx">         node-&gt;setStartOffset(offset);
</span><span class="cx">     }
</span><ins>+
+    void setStartOffset(Node* node, int offset)
+    {
+        node-&gt;setStartOffset(offset);
+    }
</ins><span class="cx">     
</span><span class="cx"> private:
</span><span class="cx">     struct Scope {
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreparserNodesh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/parser/Nodes.h (180517 => 180518)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/parser/Nodes.h        2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/JavaScriptCore/parser/Nodes.h        2015-02-23 22:10:51 UTC (rev 180518)
</span><span class="lines">@@ -132,6 +132,7 @@
</span><span class="cx">         int lineStartOffset() const { return m_position.lineStartOffset; }
</span><span class="cx">         const JSTextPosition&amp; position() const { return m_position; }
</span><span class="cx">         void setEndOffset(int offset) { m_endOffset = offset; }
</span><ins>+        void setStartOffset(int offset) { m_position.offset = offset; }
</ins><span class="cx"> 
</span><span class="cx">     protected:
</span><span class="cx">         JSTextPosition m_position;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreparserParsercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/parser/Parser.cpp (180517 => 180518)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/parser/Parser.cpp        2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/JavaScriptCore/parser/Parser.cpp        2015-02-23 22:10:51 UTC (rev 180518)
</span><span class="lines">@@ -1115,21 +1115,24 @@
</span><span class="cx"> {
</span><span class="cx">     ASSERT(match(OPENBRACE));
</span><span class="cx">     JSTokenLocation location(tokenLocation());
</span><ins>+    int startOffset = m_token.m_data.offset;
</ins><span class="cx">     int start = tokenLine();
</span><span class="cx">     next();
</span><span class="cx">     if (match(CLOSEBRACE)) {
</span><del>-        unsigned endOffset = m_lexer-&gt;currentOffset();
</del><ins>+        int endOffset = m_token.m_data.offset;
</ins><span class="cx">         next();
</span><span class="cx">         TreeStatement result = context.createBlockStatement(location, 0, start, m_lastTokenEndPosition.line);
</span><ins>+        context.setStartOffset(result, startOffset);
</ins><span class="cx">         context.setEndOffset(result, endOffset);
</span><span class="cx">         return result;
</span><span class="cx">     }
</span><span class="cx">     TreeSourceElements subtree = parseSourceElements(context, DontCheckForStrictMode);
</span><span class="cx">     failIfFalse(subtree, &quot;Cannot parse the body of the block statement&quot;);
</span><span class="cx">     matchOrFail(CLOSEBRACE, &quot;Expected a closing '}' at the end of a block statement&quot;);
</span><del>-    unsigned endOffset = m_lexer-&gt;currentOffset();
</del><ins>+    int endOffset = m_token.m_data.offset;
</ins><span class="cx">     next();
</span><span class="cx">     TreeStatement result = context.createBlockStatement(location, subtree, start, m_lastTokenEndPosition.line);
</span><ins>+    context.setStartOffset(result, startOffset);
</ins><span class="cx">     context.setEndOffset(result, endOffset);
</span><span class="cx">     return result;
</span><span class="cx"> }
</span><span class="lines">@@ -1143,9 +1146,11 @@
</span><span class="cx">     int nonTrivialExpressionCount = 0;
</span><span class="cx">     failIfStackOverflow();
</span><span class="cx">     TreeStatement result = 0;
</span><ins>+    bool shouldSetEndOffset = true;
</ins><span class="cx">     switch (m_token.m_type) {
</span><span class="cx">     case OPENBRACE:
</span><span class="cx">         result = parseBlockStatement(context);
</span><ins>+        shouldSetEndOffset = false;
</ins><span class="cx">         break;
</span><span class="cx">     case VAR:
</span><span class="cx">         result = parseVarDeclaration(context);
</span><span class="lines">@@ -1228,7 +1233,7 @@
</span><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (result)
</del><ins>+    if (result &amp;&amp; shouldSetEndOffset)
</ins><span class="cx">         context.setEndOffset(result, m_lastTokenEndPosition.offset);
</span><span class="cx">     return result;
</span><span class="cx"> }
</span><span class="lines">@@ -2312,6 +2317,7 @@
</span><span class="cx">     TreeExpression base = 0;
</span><span class="cx">     JSTextPosition expressionStart = tokenStartPosition();
</span><span class="cx">     int newCount = 0;
</span><ins>+    JSTokenLocation startLocation = tokenLocation();
</ins><span class="cx">     JSTokenLocation location;
</span><span class="cx">     while (match(NEW)) {
</span><span class="cx">         next();
</span><span class="lines">@@ -2350,7 +2356,7 @@
</span><span class="cx">                 JSTextPosition expressionEnd = lastTokenEndPosition();
</span><span class="cx">                 TreeArguments arguments = parseArguments(context, AllowSpread);
</span><span class="cx">                 failIfFalse(arguments, &quot;Cannot parse call arguments&quot;);
</span><del>-                base = context.makeFunctionCallNode(location, base, arguments, expressionStart, expressionEnd, lastTokenEndPosition());
</del><ins>+                base = context.makeFunctionCallNode(startLocation, base, arguments, expressionStart, expressionEnd, lastTokenEndPosition());
</ins><span class="cx">             }
</span><span class="cx">             m_nonLHSCount = nonLHSCount;
</span><span class="cx">             break;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeBasicBlockLocationcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/BasicBlockLocation.cpp (180517 => 180518)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/BasicBlockLocation.cpp        2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/JavaScriptCore/runtime/BasicBlockLocation.cpp        2015-02-23 22:10:51 UTC (rev 180518)
</span><span class="lines">@@ -62,8 +62,8 @@
</span><span class="cx">                 minIdx = idx;
</span><span class="cx">             }
</span><span class="cx">         }
</span><del>-        result.append(Gap(nextRangeStart, minGap.first));
-        nextRangeStart = minGap.second;
</del><ins>+        result.append(Gap(nextRangeStart, minGap.first - 1));
+        nextRangeStart = minGap.second + 1;
</ins><span class="cx">         gaps.remove(minIdx);
</span><span class="cx">     }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeControlFlowProfilercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/ControlFlowProfiler.cpp (180517 => 180518)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/ControlFlowProfiler.cpp        2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/JavaScriptCore/runtime/ControlFlowProfiler.cpp        2015-02-23 22:10:51 UTC (rev 180518)
</span><span class="lines">@@ -91,7 +91,7 @@
</span><span class="cx">         BasicBlockRange range;
</span><span class="cx">         range.m_hasExecuted = std::get&lt;0&gt;(functionRange);
</span><span class="cx">         range.m_startOffset = static_cast&lt;int&gt;(std::get&lt;1&gt;(functionRange));
</span><del>-        range.m_endOffset = static_cast&lt;int&gt;(std::get&lt;2&gt;(functionRange) + 1);
</del><ins>+        range.m_endOffset = static_cast&lt;int&gt;(std::get&lt;2&gt;(functionRange));
</ins><span class="cx">         result.append(range);
</span><span class="cx">     }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretestscontrolFlowProfilerbracelocationjs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/controlFlowProfiler/brace-location.js (0 => 180518)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/controlFlowProfiler/brace-location.js                                (rev 0)
+++ trunk/Source/JavaScriptCore/tests/controlFlowProfiler/brace-location.js        2015-02-23 22:10:51 UTC (rev 180518)
</span><span class="lines">@@ -0,0 +1,159 @@
</span><ins>+load(&quot;./driver/driver.js&quot;);
+
+function foo() {}
+function bar() {}
+function baz() {}
+
+function testIf(x) {
+    if (x &lt; 10) { foo(); } else if (x &lt; 20) { bar(); } else { baz() }
+}
+testIf(9);
+// Note, the check will be against the basic block that contains the first matched character. 
+// So in this following case, the block that contains the '{'.
+checkBasicBlock(testIf, &quot;{ foo&quot;, ShouldHaveExecuted);
+// In this case, it will test the basic block that contains the ' ' character.
+checkBasicBlock(testIf, &quot; foo&quot;, ShouldHaveExecuted);
+checkBasicBlock(testIf, &quot;} else if&quot;, ShouldHaveExecuted);
+checkBasicBlock(testIf, &quot;else if&quot;, ShouldNotHaveExecuted);
+checkBasicBlock(testIf, &quot;{ bar&quot;, ShouldNotHaveExecuted);
+checkBasicBlock(testIf, &quot; bar&quot;, ShouldNotHaveExecuted);
+checkBasicBlock(testIf, &quot;else {&quot;, ShouldNotHaveExecuted);
+checkBasicBlock(testIf, &quot;{ baz&quot;, ShouldNotHaveExecuted);
+checkBasicBlock(testIf, &quot; baz&quot;, ShouldNotHaveExecuted);
+testIf(21);
+checkBasicBlock(testIf, &quot;else if (x &lt; 20)&quot;, ShouldHaveExecuted); 
+checkBasicBlock(testIf, &quot;{ bar&quot;, ShouldNotHaveExecuted); 
+checkBasicBlock(testIf, &quot; bar&quot;, ShouldNotHaveExecuted); 
+checkBasicBlock(testIf, &quot;else {&quot;, ShouldHaveExecuted); 
+checkBasicBlock(testIf, &quot;{ baz&quot;, ShouldHaveExecuted); 
+checkBasicBlock(testIf, &quot; baz&quot;, ShouldHaveExecuted); 
+testIf(11);
+checkBasicBlock(testIf, &quot;{ bar&quot;, ShouldHaveExecuted); 
+checkBasicBlock(testIf, &quot; bar&quot;, ShouldHaveExecuted); 
+
+function testForRegular(x) {
+    for (var i = 0; i &lt; x; i++) { foo(); } bar();
+}
+testForRegular(0);
+checkBasicBlock(testForRegular, &quot;{ foo&quot;, ShouldNotHaveExecuted); 
+checkBasicBlock(testForRegular, &quot;} bar&quot;, ShouldNotHaveExecuted); 
+checkBasicBlock(testForRegular, &quot; bar&quot;, ShouldHaveExecuted); 
+testForRegular(1);
+checkBasicBlock(testForRegular, &quot;{ foo&quot;, ShouldHaveExecuted); 
+checkBasicBlock(testForRegular, &quot;} bar&quot;, ShouldHaveExecuted); 
+
+function testForIn(x) {
+    for (var i in x) { foo(); } bar();
+}
+testForIn({});
+checkBasicBlock(testForIn, &quot;{ foo&quot;, ShouldNotHaveExecuted); 
+checkBasicBlock(testForIn, &quot;} bar&quot;, ShouldNotHaveExecuted); 
+checkBasicBlock(testForIn, &quot; bar&quot;, ShouldHaveExecuted); 
+testForIn({foo: 20});
+checkBasicBlock(testForIn, &quot;{ foo&quot;, ShouldHaveExecuted); 
+checkBasicBlock(testForIn, &quot;} bar&quot;, ShouldHaveExecuted); 
+
+function testForOf(x) {
+    for (var i of x) { foo(); } bar();
+}
+testForOf([]);
+checkBasicBlock(testForOf, &quot;{ foo&quot;, ShouldNotHaveExecuted); 
+checkBasicBlock(testForOf, &quot; foo&quot;, ShouldNotHaveExecuted); 
+checkBasicBlock(testForOf, &quot;} bar&quot;, ShouldNotHaveExecuted); 
+checkBasicBlock(testForOf, &quot; bar&quot;, ShouldHaveExecuted); 
+testForOf([20]);
+checkBasicBlock(testForOf, &quot;{ foo&quot;, ShouldHaveExecuted); 
+checkBasicBlock(testForOf, &quot;} bar&quot;, ShouldHaveExecuted); 
+
+function testWhile(x) {
+    var i = 0; while (i++ &lt; x) { foo(); } bar();
+}
+testWhile(0);
+checkBasicBlock(testWhile, &quot;{ foo&quot;, ShouldNotHaveExecuted); 
+checkBasicBlock(testWhile, &quot; foo&quot;, ShouldNotHaveExecuted); 
+checkBasicBlock(testWhile, &quot;} bar&quot;, ShouldNotHaveExecuted); 
+checkBasicBlock(testWhile, &quot; bar&quot;, ShouldHaveExecuted); 
+testWhile(1);
+checkBasicBlock(testWhile, &quot;{ foo&quot;, ShouldHaveExecuted); 
+checkBasicBlock(testWhile, &quot;} bar&quot;, ShouldHaveExecuted); 
+
+
+// No braces tests.
+
+function testIfNoBraces(x) {
+    if (x &lt; 10) foo(); else if (x &lt; 20) bar(); else baz();
+}
+testIfNoBraces(9);
+checkBasicBlock(testIfNoBraces, &quot;foo&quot;, ShouldHaveExecuted);
+checkBasicBlock(testIfNoBraces, &quot; foo&quot;, ShouldHaveExecuted);
+checkBasicBlock(testIfNoBraces, &quot;; else if&quot;, ShouldHaveExecuted);
+checkBasicBlock(testIfNoBraces, &quot; else if&quot;, ShouldNotHaveExecuted);
+checkBasicBlock(testIfNoBraces, &quot; bar&quot;, ShouldNotHaveExecuted); 
+checkBasicBlock(testIfNoBraces, &quot;bar&quot;, ShouldNotHaveExecuted); 
+checkBasicBlock(testIfNoBraces, &quot;else baz&quot;, ShouldNotHaveExecuted); 
+checkBasicBlock(testIfNoBraces, &quot;baz&quot;, ShouldNotHaveExecuted); 
+testIfNoBraces(21);
+checkBasicBlock(testIfNoBraces, &quot;else baz&quot;, ShouldHaveExecuted); 
+checkBasicBlock(testIfNoBraces, &quot;baz&quot;, ShouldHaveExecuted); 
+checkBasicBlock(testIfNoBraces, &quot;; else baz&quot;, ShouldNotHaveExecuted); 
+checkBasicBlock(testIfNoBraces, &quot;else if (x &lt; 20)&quot;, ShouldHaveExecuted);
+// Note that the whitespace preceding bar is part of the previous basic block.
+// An if statement's if-true basic block text offset begins at the start offset
+// of the if-true block, in this case, just the expression &quot;bar()&quot;.
+checkBasicBlock(testIfNoBraces, &quot; bar&quot;, ShouldHaveExecuted); 
+checkBasicBlock(testIfNoBraces, &quot;bar&quot;, ShouldNotHaveExecuted); 
+testIfNoBraces(11);
+checkBasicBlock(testIfNoBraces, &quot; bar&quot;, ShouldHaveExecuted); 
+checkBasicBlock(testIfNoBraces, &quot;bar&quot;, ShouldHaveExecuted); 
+
+function testForRegularNoBraces(x) {
+    for (var i = 0; i &lt; x; i++) foo(); bar();
+}
+testForRegularNoBraces(0);
+checkBasicBlock(testForRegularNoBraces, &quot; foo&quot;, ShouldHaveExecuted); 
+checkBasicBlock(testForRegularNoBraces, &quot;foo&quot;, ShouldNotHaveExecuted); 
+checkBasicBlock(testForRegularNoBraces, &quot;; bar&quot;, ShouldNotHaveExecuted); 
+checkBasicBlock(testForRegularNoBraces, &quot; bar&quot;, ShouldHaveExecuted); 
+testForRegularNoBraces(1);
+checkBasicBlock(testForRegularNoBraces, &quot; foo&quot;, ShouldHaveExecuted); 
+checkBasicBlock(testForRegularNoBraces, &quot;foo&quot;, ShouldHaveExecuted); 
+checkBasicBlock(testForRegularNoBraces, &quot; bar&quot;, ShouldHaveExecuted); 
+
+function testForInNoBraces(x) {
+    for (var i in x) foo(); bar();
+}
+testForInNoBraces({});
+checkBasicBlock(testForInNoBraces, &quot; foo&quot;, ShouldHaveExecuted); 
+checkBasicBlock(testForInNoBraces, &quot;foo&quot;, ShouldNotHaveExecuted); 
+checkBasicBlock(testForInNoBraces, &quot;; bar&quot;, ShouldNotHaveExecuted); 
+checkBasicBlock(testForInNoBraces, &quot; bar&quot;, ShouldHaveExecuted); 
+testForInNoBraces({foo: 20});
+checkBasicBlock(testForInNoBraces, &quot; foo&quot;, ShouldHaveExecuted); 
+checkBasicBlock(testForInNoBraces, &quot;foo&quot;, ShouldHaveExecuted); 
+checkBasicBlock(testForInNoBraces, &quot;; bar&quot;, ShouldHaveExecuted); 
+
+function testForOfNoBraces(x) {
+    for (var i of x) foo(); bar();
+}
+testForOfNoBraces([]);
+checkBasicBlock(testForOfNoBraces, &quot; foo&quot;, ShouldHaveExecuted); 
+checkBasicBlock(testForOfNoBraces, &quot;foo&quot;, ShouldNotHaveExecuted); 
+checkBasicBlock(testForOfNoBraces, &quot;; bar&quot;, ShouldNotHaveExecuted); 
+checkBasicBlock(testForOfNoBraces, &quot; bar&quot;, ShouldHaveExecuted); 
+testForOfNoBraces([20]);
+checkBasicBlock(testForOfNoBraces, &quot; foo&quot;, ShouldHaveExecuted); 
+checkBasicBlock(testForOfNoBraces, &quot;foo&quot;, ShouldHaveExecuted); 
+checkBasicBlock(testForOfNoBraces, &quot;; bar&quot;, ShouldHaveExecuted); 
+
+function testWhileNoBraces(x) {
+    var i = 0; while (i++ &lt; x) foo(); bar();
+}
+testWhileNoBraces(0);
+checkBasicBlock(testWhileNoBraces, &quot; foo&quot;, ShouldHaveExecuted); 
+checkBasicBlock(testWhileNoBraces, &quot;foo&quot;, ShouldNotHaveExecuted); 
+checkBasicBlock(testWhileNoBraces, &quot;; bar&quot;, ShouldNotHaveExecuted); 
+checkBasicBlock(testWhileNoBraces, &quot; bar&quot;, ShouldHaveExecuted); 
+testWhileNoBraces(1);
+checkBasicBlock(testWhileNoBraces, &quot; foo&quot;, ShouldHaveExecuted); 
+checkBasicBlock(testWhileNoBraces, &quot;foo&quot;, ShouldHaveExecuted); 
+checkBasicBlock(testWhileNoBraces, &quot;; bar&quot;, ShouldHaveExecuted); 
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoretestscontrolFlowProfilerconditionalexpressionjs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/controlFlowProfiler/conditional-expression.js (0 => 180518)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/controlFlowProfiler/conditional-expression.js                                (rev 0)
+++ trunk/Source/JavaScriptCore/tests/controlFlowProfiler/conditional-expression.js        2015-02-23 22:10:51 UTC (rev 180518)
</span><span class="lines">@@ -0,0 +1,44 @@
</span><ins>+load(&quot;./driver/driver.js&quot;);
+
+function foo(){ }
+function bar(){ }
+function baz(){ }
+
+function testConditionalBasic(x) {
+    return x ? 10 : 20;
+}
+
+
+testConditionalBasic(false);
+checkBasicBlock(testConditionalBasic, &quot;x&quot;, ShouldHaveExecuted);
+checkBasicBlock(testConditionalBasic, &quot;20&quot;, ShouldHaveExecuted);
+checkBasicBlock(testConditionalBasic, &quot;10&quot;, ShouldNotHaveExecuted);
+
+testConditionalBasic(true);
+checkBasicBlock(testConditionalBasic, &quot;10&quot;, ShouldHaveExecuted);
+
+
+function testConditionalFunctionCall(x, y) {
+    x ? y ? foo() 
+        : baz() 
+        : bar()
+}
+testConditionalFunctionCall(false, false);
+checkBasicBlock(testConditionalFunctionCall, &quot;x&quot;, ShouldHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, &quot;? y&quot;, ShouldHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, &quot;bar&quot;, ShouldHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, &quot;: bar&quot;, ShouldHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, &quot;y&quot;, ShouldNotHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, &quot;? foo&quot;, ShouldNotHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, &quot;foo&quot;, ShouldNotHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, &quot;baz&quot;, ShouldNotHaveExecuted);
+
+testConditionalFunctionCall(true, false);
+checkBasicBlock(testConditionalFunctionCall, &quot;y&quot;, ShouldHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, &quot;? foo&quot;, ShouldHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, &quot;: baz&quot;, ShouldHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, &quot;baz&quot;, ShouldHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, &quot;foo&quot;, ShouldNotHaveExecuted);
+
+testConditionalFunctionCall(true, true);
+checkBasicBlock(testConditionalFunctionCall, &quot;foo&quot;, ShouldHaveExecuted);
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoretestscontrolFlowProfilerdriverdriverjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/tests/controlFlowProfiler/driver/driver.js (180517 => 180518)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/controlFlowProfiler/driver/driver.js        2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/JavaScriptCore/tests/controlFlowProfiler/driver/driver.js        2015-02-23 22:10:51 UTC (rev 180518)
</span><span class="lines">@@ -2,3 +2,13 @@
</span><span class="cx">     if (!condition)
</span><span class="cx">         throw new Error(reason);
</span><span class="cx"> }
</span><ins>+
+var ShouldHaveExecuted = true;
+var ShouldNotHaveExecuted = false;
+
+function checkBasicBlock(func, expr, expectation) {
+    if (expectation === ShouldHaveExecuted)
+        assert(hasBasicBlockExecuted(func, expr, &quot;should have executed&quot;));
+    else
+        assert(!hasBasicBlockExecuted(func, expr, &quot;should not have executed&quot;));
+}
</ins></span></pre>
</div>
</div>

</body>
</html>