Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (180517 => 180518)
--- trunk/Source/_javascript_Core/ChangeLog 2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/_javascript_Core/ChangeLog 2015-02-23 22:10:51 UTC (rev 180518)
@@ -1,3 +1,102 @@
+2015-02-23 Saam Barati <[email protected]>
+
+ 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 "if (cond) foo();", the whitespace preceding "foo()" is not part of
+ the "foo()" basic block, but instead is part of the "if (cond) " 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<LexerType>::parseBlockStatement):
+ (JSC::Parser<LexerType>::parseStatement):
+ (JSC::Parser<LexerType>::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):
+
2015-02-23 Matthew Mirman <[email protected]>
r9 is volatile on ARMv7 for iOS 3 and up.
Modified: trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp (180517 => 180518)
--- trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp 2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp 2015-02-23 22:10:51 UTC (rev 180518)
@@ -4052,7 +4052,7 @@
if (i + 1 < offsetsLength) {
size_t endIdx = bytecodeOffsets[i + 1];
RELEASE_ASSERT(vm()->interpreter->getOpcodeID(instructions[endIdx].u.opcode) == op_profile_control_flow);
- basicBlockEndOffset = instructions[endIdx + 1].u.operand;
+ basicBlockEndOffset = instructions[endIdx + 1].u.operand - 1;
} else {
basicBlockEndOffset = m_sourceOffset + m_ownerExecutable->source().length() - 1; // Offset before the closing brace.
basicBlockStartOffset = std::min(basicBlockStartOffset, basicBlockEndOffset); // Some start offsets may be at the closing brace, ensure it is the offset before.
Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp (180517 => 180518)
--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp 2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp 2015-02-23 22:10:51 UTC (rev 180518)
@@ -338,7 +338,7 @@
m_symbolTable->setCaptureEnd(virtualRegisterForLocal(codeBlock->m_numVars).offset());
- bool canLazilyCreateFunctions = !functionNode->needsActivationForMoreThanVariables() && !m_shouldEmitDebugHooks && !m_vm->typeProfiler();
+ bool canLazilyCreateFunctions = !functionNode->needsActivationForMoreThanVariables() && !m_shouldEmitDebugHooks && !m_vm->typeProfiler() && !m_vm->controlFlowProfiler();
m_firstLazyFunction = codeBlock->m_numVars;
if (!shouldCaptureAllTheThings) {
for (size_t i = 0; i < functionStack.size(); ++i) {
Modified: trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp (180517 => 180518)
--- trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp 2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp 2015-02-23 22:10:51 UTC (rev 180518)
@@ -1444,12 +1444,12 @@
generator.emitJump(afterElse.get());
generator.emitLabel(beforeElse.get());
- generator.emitProfileControlFlow(m_expr2->startOffset());
+ generator.emitProfileControlFlow(m_expr1->endOffset() + 1);
generator.emitNode(newDst.get(), m_expr2);
generator.emitLabel(afterElse.get());
- generator.emitProfileControlFlow(m_expr2->endOffset());
+ generator.emitProfileControlFlow(m_expr2->endOffset() + 1);
return newDst.get();
}
@@ -1897,12 +1897,13 @@
generator.emitLabel(beforeElse.get());
if (m_elseBlock) {
- generator.emitProfileControlFlow(m_ifBlock->endOffset());
+ generator.emitProfileControlFlow(m_ifBlock->endOffset() + (m_ifBlock->isBlock() ? 1 : 0));
generator.emitNode(dst, m_elseBlock);
}
generator.emitLabel(afterElse.get());
- generator.emitProfileControlFlow(m_elseBlock ? m_elseBlock->endOffset() : m_ifBlock->endOffset());
+ StatementNode* endingBlock = m_elseBlock ? m_elseBlock : m_ifBlock;
+ generator.emitProfileControlFlow(endingBlock->endOffset() + (endingBlock->isBlock() ? 1 : 0));
}
// ------------------------------ DoWhileNode ----------------------------------
@@ -1948,7 +1949,7 @@
generator.emitLabel(scope->breakTarget());
- generator.emitProfileControlFlow(endOffset());
+ generator.emitProfileControlFlow(m_statement->endOffset() + (m_statement->isBlock() ? 1 : 0));
}
// ------------------------------ ForNode --------------------------------------
@@ -1983,7 +1984,7 @@
generator.emitJump(topOfLoop.get());
generator.emitLabel(scope->breakTarget());
- generator.emitProfileControlFlow(endOffset());
+ generator.emitProfileControlFlow(m_statement->endOffset() + (m_statement->isBlock() ? 1 : 0));
}
// ------------------------------ ForInNode ------------------------------------
@@ -2100,6 +2101,8 @@
generator.emitNode(base.get(), m_expr);
RefPtr<RegisterID> local = this->tryGetBoundLocal(generator);
+ int profilerStartOffset = m_statement->startOffset();
+ int profilerEndOffset = m_statement->endOffset() + (m_statement->isBlock() ? 1 : 0);
// Indexed property loop.
{
LabelScopePtr scope = generator.newLabelScope(LabelScope::Loop);
@@ -2121,13 +2124,13 @@
generator.emitToIndexString(propertyName.get(), i.get());
this->emitLoopHeader(generator, propertyName.get());
- generator.emitProfileControlFlow(m_statement->startOffset());
+ generator.emitProfileControlFlow(profilerStartOffset);
generator.pushIndexedForInScope(local.get(), i.get());
generator.emitNode(dst, m_statement);
generator.popIndexedForInScope(local.get());
- generator.emitProfileControlFlow(m_statement->endOffset());
+ generator.emitProfileControlFlow(profilerEndOffset);
generator.emitLabel(scope->continueTarget());
generator.emitInc(i.get());
@@ -2159,13 +2162,13 @@
this->emitLoopHeader(generator, propertyName.get());
- generator.emitProfileControlFlow(m_statement->startOffset());
+ generator.emitProfileControlFlow(profilerStartOffset);
generator.pushStructureForInScope(local.get(), i.get(), propertyName.get(), structureEnumerator.get());
generator.emitNode(dst, m_statement);
generator.popStructureForInScope(local.get());
- generator.emitProfileControlFlow(m_statement->endOffset());
+ generator.emitProfileControlFlow(profilerEndOffset);
generator.emitLabel(scope->continueTarget());
generator.emitInc(i.get());
@@ -2196,7 +2199,7 @@
this->emitLoopHeader(generator, propertyName.get());
- generator.emitProfileControlFlow(m_statement->startOffset());
+ generator.emitProfileControlFlow(profilerStartOffset);
generator.emitNode(dst, m_statement);
@@ -2217,7 +2220,7 @@
generator.emitDebugHook(WillExecuteStatement, firstLine(), startOffset(), lineStartOffset());
generator.emitLabel(end.get());
- generator.emitProfileControlFlow(m_statement->endOffset());
+ generator.emitProfileControlFlow(profilerEndOffset);
}
void ForInNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
@@ -2285,7 +2288,7 @@
generator.emitNode(dst, m_statement);
};
generator.emitEnumeration(this, m_expr, extractor);
- generator.emitProfileControlFlow(m_statement->endOffset());
+ generator.emitProfileControlFlow(m_statement->endOffset() + (m_statement->isBlock() ? 1 : 0));
}
// ------------------------------ ContinueNode ---------------------------------
@@ -2631,7 +2634,7 @@
}
generator.emitPushCatchScope(generator.scopeRegister(), m_exceptionIdent, exceptionRegister.get(), DontDelete);
- generator.emitProfileControlFlow(m_tryBlock->endOffset());
+ generator.emitProfileControlFlow(m_tryBlock->endOffset() + 1);
generator.emitNode(dst, m_catchBlock);
generator.emitPopScope(generator.scopeRegister());
generator.emitLabel(catchEndLabel.get());
@@ -2644,12 +2647,12 @@
RefPtr<Label> finallyEndLabel = generator.newLabel();
- int finallyStartOffset = m_catchBlock ? m_catchBlock->endOffset() : m_tryBlock->endOffset();
+ int finallyStartOffset = m_catchBlock ? m_catchBlock->endOffset() + 1 : m_tryBlock->endOffset() + 1;
// Normal path: run the finally code, and jump to the end.
generator.emitProfileControlFlow(finallyStartOffset);
generator.emitNode(dst, m_finallyBlock);
- generator.emitProfileControlFlow(m_finallyBlock->endOffset());
+ generator.emitProfileControlFlow(m_finallyBlock->endOffset() + 1);
generator.emitJump(finallyEndLabel.get());
// Uncaught exception path: invoke the finally block, then re-throw the exception.
@@ -2659,9 +2662,9 @@
generator.emitThrow(tempExceptionRegister.get());
generator.emitLabel(finallyEndLabel.get());
- generator.emitProfileControlFlow(m_finallyBlock->endOffset());
+ generator.emitProfileControlFlow(m_finallyBlock->endOffset() + 1);
} else
- generator.emitProfileControlFlow(m_catchBlock->endOffset());
+ generator.emitProfileControlFlow(m_catchBlock->endOffset() + 1);
}
Modified: trunk/Source/_javascript_Core/jsc.cpp (180517 => 180518)
--- trunk/Source/_javascript_Core/jsc.cpp 2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/_javascript_Core/jsc.cpp 2015-02-23 22:10:51 UTC (rev 180518)
@@ -1127,6 +1127,7 @@
RELEASE_ASSERT(exec->argument(1).isString());
String substring = exec->argument(1).getString(exec);
String sourceCodeText = executable->source().toString();
+ RELEASE_ASSERT(sourceCodeText.contains(substring));
int offset = sourceCodeText.find(substring) + executable->source().startOffset();
bool hasExecuted = exec->vm().controlFlowProfiler()->hasBasicBlockAtTextOffsetBeenExecuted(offset, executable->sourceID(), exec->vm());
Modified: trunk/Source/_javascript_Core/parser/ASTBuilder.h (180517 => 180518)
--- trunk/Source/_javascript_Core/parser/ASTBuilder.h 2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/_javascript_Core/parser/ASTBuilder.h 2015-02-23 22:10:51 UTC (rev 180518)
@@ -713,6 +713,11 @@
{
node->setStartOffset(offset);
}
+
+ void setStartOffset(Node* node, int offset)
+ {
+ node->setStartOffset(offset);
+ }
private:
struct Scope {
Modified: trunk/Source/_javascript_Core/parser/Nodes.h (180517 => 180518)
--- trunk/Source/_javascript_Core/parser/Nodes.h 2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/_javascript_Core/parser/Nodes.h 2015-02-23 22:10:51 UTC (rev 180518)
@@ -132,6 +132,7 @@
int lineStartOffset() const { return m_position.lineStartOffset; }
const JSTextPosition& position() const { return m_position; }
void setEndOffset(int offset) { m_endOffset = offset; }
+ void setStartOffset(int offset) { m_position.offset = offset; }
protected:
JSTextPosition m_position;
Modified: trunk/Source/_javascript_Core/parser/Parser.cpp (180517 => 180518)
--- trunk/Source/_javascript_Core/parser/Parser.cpp 2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/_javascript_Core/parser/Parser.cpp 2015-02-23 22:10:51 UTC (rev 180518)
@@ -1115,21 +1115,24 @@
{
ASSERT(match(OPENBRACE));
JSTokenLocation location(tokenLocation());
+ int startOffset = m_token.m_data.offset;
int start = tokenLine();
next();
if (match(CLOSEBRACE)) {
- unsigned endOffset = m_lexer->currentOffset();
+ int endOffset = m_token.m_data.offset;
next();
TreeStatement result = context.createBlockStatement(location, 0, start, m_lastTokenEndPosition.line);
+ context.setStartOffset(result, startOffset);
context.setEndOffset(result, endOffset);
return result;
}
TreeSourceElements subtree = parseSourceElements(context, DontCheckForStrictMode);
failIfFalse(subtree, "Cannot parse the body of the block statement");
matchOrFail(CLOSEBRACE, "Expected a closing '}' at the end of a block statement");
- unsigned endOffset = m_lexer->currentOffset();
+ int endOffset = m_token.m_data.offset;
next();
TreeStatement result = context.createBlockStatement(location, subtree, start, m_lastTokenEndPosition.line);
+ context.setStartOffset(result, startOffset);
context.setEndOffset(result, endOffset);
return result;
}
@@ -1143,9 +1146,11 @@
int nonTrivialExpressionCount = 0;
failIfStackOverflow();
TreeStatement result = 0;
+ bool shouldSetEndOffset = true;
switch (m_token.m_type) {
case OPENBRACE:
result = parseBlockStatement(context);
+ shouldSetEndOffset = false;
break;
case VAR:
result = parseVarDeclaration(context);
@@ -1228,7 +1233,7 @@
break;
}
- if (result)
+ if (result && shouldSetEndOffset)
context.setEndOffset(result, m_lastTokenEndPosition.offset);
return result;
}
@@ -2312,6 +2317,7 @@
TreeExpression base = 0;
JSTextPosition expressionStart = tokenStartPosition();
int newCount = 0;
+ JSTokenLocation startLocation = tokenLocation();
JSTokenLocation location;
while (match(NEW)) {
next();
@@ -2350,7 +2356,7 @@
JSTextPosition expressionEnd = lastTokenEndPosition();
TreeArguments arguments = parseArguments(context, AllowSpread);
failIfFalse(arguments, "Cannot parse call arguments");
- base = context.makeFunctionCallNode(location, base, arguments, expressionStart, expressionEnd, lastTokenEndPosition());
+ base = context.makeFunctionCallNode(startLocation, base, arguments, expressionStart, expressionEnd, lastTokenEndPosition());
}
m_nonLHSCount = nonLHSCount;
break;
Modified: trunk/Source/_javascript_Core/runtime/BasicBlockLocation.cpp (180517 => 180518)
--- trunk/Source/_javascript_Core/runtime/BasicBlockLocation.cpp 2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/_javascript_Core/runtime/BasicBlockLocation.cpp 2015-02-23 22:10:51 UTC (rev 180518)
@@ -62,8 +62,8 @@
minIdx = idx;
}
}
- result.append(Gap(nextRangeStart, minGap.first));
- nextRangeStart = minGap.second;
+ result.append(Gap(nextRangeStart, minGap.first - 1));
+ nextRangeStart = minGap.second + 1;
gaps.remove(minIdx);
}
Modified: trunk/Source/_javascript_Core/runtime/ControlFlowProfiler.cpp (180517 => 180518)
--- trunk/Source/_javascript_Core/runtime/ControlFlowProfiler.cpp 2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/_javascript_Core/runtime/ControlFlowProfiler.cpp 2015-02-23 22:10:51 UTC (rev 180518)
@@ -91,7 +91,7 @@
BasicBlockRange range;
range.m_hasExecuted = std::get<0>(functionRange);
range.m_startOffset = static_cast<int>(std::get<1>(functionRange));
- range.m_endOffset = static_cast<int>(std::get<2>(functionRange) + 1);
+ range.m_endOffset = static_cast<int>(std::get<2>(functionRange));
result.append(range);
}
Added: trunk/Source/_javascript_Core/tests/controlFlowProfiler/brace-location.js (0 => 180518)
--- trunk/Source/_javascript_Core/tests/controlFlowProfiler/brace-location.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/controlFlowProfiler/brace-location.js 2015-02-23 22:10:51 UTC (rev 180518)
@@ -0,0 +1,159 @@
+load("./driver/driver.js");
+
+function foo() {}
+function bar() {}
+function baz() {}
+
+function testIf(x) {
+ if (x < 10) { foo(); } else if (x < 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, "{ foo", ShouldHaveExecuted);
+// In this case, it will test the basic block that contains the ' ' character.
+checkBasicBlock(testIf, " foo", ShouldHaveExecuted);
+checkBasicBlock(testIf, "} else if", ShouldHaveExecuted);
+checkBasicBlock(testIf, "else if", ShouldNotHaveExecuted);
+checkBasicBlock(testIf, "{ bar", ShouldNotHaveExecuted);
+checkBasicBlock(testIf, " bar", ShouldNotHaveExecuted);
+checkBasicBlock(testIf, "else {", ShouldNotHaveExecuted);
+checkBasicBlock(testIf, "{ baz", ShouldNotHaveExecuted);
+checkBasicBlock(testIf, " baz", ShouldNotHaveExecuted);
+testIf(21);
+checkBasicBlock(testIf, "else if (x < 20)", ShouldHaveExecuted);
+checkBasicBlock(testIf, "{ bar", ShouldNotHaveExecuted);
+checkBasicBlock(testIf, " bar", ShouldNotHaveExecuted);
+checkBasicBlock(testIf, "else {", ShouldHaveExecuted);
+checkBasicBlock(testIf, "{ baz", ShouldHaveExecuted);
+checkBasicBlock(testIf, " baz", ShouldHaveExecuted);
+testIf(11);
+checkBasicBlock(testIf, "{ bar", ShouldHaveExecuted);
+checkBasicBlock(testIf, " bar", ShouldHaveExecuted);
+
+function testForRegular(x) {
+ for (var i = 0; i < x; i++) { foo(); } bar();
+}
+testForRegular(0);
+checkBasicBlock(testForRegular, "{ foo", ShouldNotHaveExecuted);
+checkBasicBlock(testForRegular, "} bar", ShouldNotHaveExecuted);
+checkBasicBlock(testForRegular, " bar", ShouldHaveExecuted);
+testForRegular(1);
+checkBasicBlock(testForRegular, "{ foo", ShouldHaveExecuted);
+checkBasicBlock(testForRegular, "} bar", ShouldHaveExecuted);
+
+function testForIn(x) {
+ for (var i in x) { foo(); } bar();
+}
+testForIn({});
+checkBasicBlock(testForIn, "{ foo", ShouldNotHaveExecuted);
+checkBasicBlock(testForIn, "} bar", ShouldNotHaveExecuted);
+checkBasicBlock(testForIn, " bar", ShouldHaveExecuted);
+testForIn({foo: 20});
+checkBasicBlock(testForIn, "{ foo", ShouldHaveExecuted);
+checkBasicBlock(testForIn, "} bar", ShouldHaveExecuted);
+
+function testForOf(x) {
+ for (var i of x) { foo(); } bar();
+}
+testForOf([]);
+checkBasicBlock(testForOf, "{ foo", ShouldNotHaveExecuted);
+checkBasicBlock(testForOf, " foo", ShouldNotHaveExecuted);
+checkBasicBlock(testForOf, "} bar", ShouldNotHaveExecuted);
+checkBasicBlock(testForOf, " bar", ShouldHaveExecuted);
+testForOf([20]);
+checkBasicBlock(testForOf, "{ foo", ShouldHaveExecuted);
+checkBasicBlock(testForOf, "} bar", ShouldHaveExecuted);
+
+function testWhile(x) {
+ var i = 0; while (i++ < x) { foo(); } bar();
+}
+testWhile(0);
+checkBasicBlock(testWhile, "{ foo", ShouldNotHaveExecuted);
+checkBasicBlock(testWhile, " foo", ShouldNotHaveExecuted);
+checkBasicBlock(testWhile, "} bar", ShouldNotHaveExecuted);
+checkBasicBlock(testWhile, " bar", ShouldHaveExecuted);
+testWhile(1);
+checkBasicBlock(testWhile, "{ foo", ShouldHaveExecuted);
+checkBasicBlock(testWhile, "} bar", ShouldHaveExecuted);
+
+
+// No braces tests.
+
+function testIfNoBraces(x) {
+ if (x < 10) foo(); else if (x < 20) bar(); else baz();
+}
+testIfNoBraces(9);
+checkBasicBlock(testIfNoBraces, "foo", ShouldHaveExecuted);
+checkBasicBlock(testIfNoBraces, " foo", ShouldHaveExecuted);
+checkBasicBlock(testIfNoBraces, "; else if", ShouldHaveExecuted);
+checkBasicBlock(testIfNoBraces, " else if", ShouldNotHaveExecuted);
+checkBasicBlock(testIfNoBraces, " bar", ShouldNotHaveExecuted);
+checkBasicBlock(testIfNoBraces, "bar", ShouldNotHaveExecuted);
+checkBasicBlock(testIfNoBraces, "else baz", ShouldNotHaveExecuted);
+checkBasicBlock(testIfNoBraces, "baz", ShouldNotHaveExecuted);
+testIfNoBraces(21);
+checkBasicBlock(testIfNoBraces, "else baz", ShouldHaveExecuted);
+checkBasicBlock(testIfNoBraces, "baz", ShouldHaveExecuted);
+checkBasicBlock(testIfNoBraces, "; else baz", ShouldNotHaveExecuted);
+checkBasicBlock(testIfNoBraces, "else if (x < 20)", 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_ "bar()".
+checkBasicBlock(testIfNoBraces, " bar", ShouldHaveExecuted);
+checkBasicBlock(testIfNoBraces, "bar", ShouldNotHaveExecuted);
+testIfNoBraces(11);
+checkBasicBlock(testIfNoBraces, " bar", ShouldHaveExecuted);
+checkBasicBlock(testIfNoBraces, "bar", ShouldHaveExecuted);
+
+function testForRegularNoBraces(x) {
+ for (var i = 0; i < x; i++) foo(); bar();
+}
+testForRegularNoBraces(0);
+checkBasicBlock(testForRegularNoBraces, " foo", ShouldHaveExecuted);
+checkBasicBlock(testForRegularNoBraces, "foo", ShouldNotHaveExecuted);
+checkBasicBlock(testForRegularNoBraces, "; bar", ShouldNotHaveExecuted);
+checkBasicBlock(testForRegularNoBraces, " bar", ShouldHaveExecuted);
+testForRegularNoBraces(1);
+checkBasicBlock(testForRegularNoBraces, " foo", ShouldHaveExecuted);
+checkBasicBlock(testForRegularNoBraces, "foo", ShouldHaveExecuted);
+checkBasicBlock(testForRegularNoBraces, " bar", ShouldHaveExecuted);
+
+function testForInNoBraces(x) {
+ for (var i in x) foo(); bar();
+}
+testForInNoBraces({});
+checkBasicBlock(testForInNoBraces, " foo", ShouldHaveExecuted);
+checkBasicBlock(testForInNoBraces, "foo", ShouldNotHaveExecuted);
+checkBasicBlock(testForInNoBraces, "; bar", ShouldNotHaveExecuted);
+checkBasicBlock(testForInNoBraces, " bar", ShouldHaveExecuted);
+testForInNoBraces({foo: 20});
+checkBasicBlock(testForInNoBraces, " foo", ShouldHaveExecuted);
+checkBasicBlock(testForInNoBraces, "foo", ShouldHaveExecuted);
+checkBasicBlock(testForInNoBraces, "; bar", ShouldHaveExecuted);
+
+function testForOfNoBraces(x) {
+ for (var i of x) foo(); bar();
+}
+testForOfNoBraces([]);
+checkBasicBlock(testForOfNoBraces, " foo", ShouldHaveExecuted);
+checkBasicBlock(testForOfNoBraces, "foo", ShouldNotHaveExecuted);
+checkBasicBlock(testForOfNoBraces, "; bar", ShouldNotHaveExecuted);
+checkBasicBlock(testForOfNoBraces, " bar", ShouldHaveExecuted);
+testForOfNoBraces([20]);
+checkBasicBlock(testForOfNoBraces, " foo", ShouldHaveExecuted);
+checkBasicBlock(testForOfNoBraces, "foo", ShouldHaveExecuted);
+checkBasicBlock(testForOfNoBraces, "; bar", ShouldHaveExecuted);
+
+function testWhileNoBraces(x) {
+ var i = 0; while (i++ < x) foo(); bar();
+}
+testWhileNoBraces(0);
+checkBasicBlock(testWhileNoBraces, " foo", ShouldHaveExecuted);
+checkBasicBlock(testWhileNoBraces, "foo", ShouldNotHaveExecuted);
+checkBasicBlock(testWhileNoBraces, "; bar", ShouldNotHaveExecuted);
+checkBasicBlock(testWhileNoBraces, " bar", ShouldHaveExecuted);
+testWhileNoBraces(1);
+checkBasicBlock(testWhileNoBraces, " foo", ShouldHaveExecuted);
+checkBasicBlock(testWhileNoBraces, "foo", ShouldHaveExecuted);
+checkBasicBlock(testWhileNoBraces, "; bar", ShouldHaveExecuted);
Added: trunk/Source/_javascript_Core/tests/controlFlowProfiler/conditional-_expression_.js (0 => 180518)
--- trunk/Source/_javascript_Core/tests/controlFlowProfiler/conditional-_expression_.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/controlFlowProfiler/conditional-_expression_.js 2015-02-23 22:10:51 UTC (rev 180518)
@@ -0,0 +1,44 @@
+load("./driver/driver.js");
+
+function foo(){ }
+function bar(){ }
+function baz(){ }
+
+function testConditionalBasic(x) {
+ return x ? 10 : 20;
+}
+
+
+testConditionalBasic(false);
+checkBasicBlock(testConditionalBasic, "x", ShouldHaveExecuted);
+checkBasicBlock(testConditionalBasic, "20", ShouldHaveExecuted);
+checkBasicBlock(testConditionalBasic, "10", ShouldNotHaveExecuted);
+
+testConditionalBasic(true);
+checkBasicBlock(testConditionalBasic, "10", ShouldHaveExecuted);
+
+
+function testConditionalFunctionCall(x, y) {
+ x ? y ? foo()
+ : baz()
+ : bar()
+}
+testConditionalFunctionCall(false, false);
+checkBasicBlock(testConditionalFunctionCall, "x", ShouldHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, "? y", ShouldHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, "bar", ShouldHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, ": bar", ShouldHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, "y", ShouldNotHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, "? foo", ShouldNotHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, "foo", ShouldNotHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, "baz", ShouldNotHaveExecuted);
+
+testConditionalFunctionCall(true, false);
+checkBasicBlock(testConditionalFunctionCall, "y", ShouldHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, "? foo", ShouldHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, ": baz", ShouldHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, "baz", ShouldHaveExecuted);
+checkBasicBlock(testConditionalFunctionCall, "foo", ShouldNotHaveExecuted);
+
+testConditionalFunctionCall(true, true);
+checkBasicBlock(testConditionalFunctionCall, "foo", ShouldHaveExecuted);
Modified: trunk/Source/_javascript_Core/tests/controlFlowProfiler/driver/driver.js (180517 => 180518)
--- trunk/Source/_javascript_Core/tests/controlFlowProfiler/driver/driver.js 2015-02-23 22:08:45 UTC (rev 180517)
+++ trunk/Source/_javascript_Core/tests/controlFlowProfiler/driver/driver.js 2015-02-23 22:10:51 UTC (rev 180518)
@@ -2,3 +2,13 @@
if (!condition)
throw new Error(reason);
}
+
+var ShouldHaveExecuted = true;
+var ShouldNotHaveExecuted = false;
+
+function checkBasicBlock(func, expr, expectation) {
+ if (expectation === ShouldHaveExecuted)
+ assert(hasBasicBlockExecuted(func, expr, "should have executed"));
+ else
+ assert(!hasBasicBlockExecuted(func, expr, "should not have executed"));
+}