Commit 0309686b authored by fpizlo@apple.com's avatar fpizlo@apple.com

Stores to local captured variables should be intercepted

https://bugs.webkit.org/show_bug.cgi?id=124883

Source/JavaScriptCore: 

Reviewed by Mark Hahnenberg.
        
Previously, in bytecode, you could assign to a captured variable just as you would
assign to any other kind of variable. This complicates closure variable constant
inference because we don't have any place where we can intercept stores to captured
variables in the LLInt.
        
This patch institutes a policy that only certain instructions can store to captured
variables. If you interpret those instructions and you are required to notifyWrite()
then you need to check if the relevant variable is captured. Those instructions are
tracked in CodeBlock.cpp's VerifyCapturedDef. The main one is simply op_captured_mov.
In the future, we'll probably modify those instructions to have a pointer directly to
the VariableWatchpointSet; but for now we just introduce the captured instructions as
placeholders.
        
In order to validate that the placeholders are inserted correctly, this patch improves
the CodeBlock validation to be able to inspect every def in the bytecode. To do that,
this patch refactors the liveness analysis' use/def calculator to be reusable; it now
takes a functor for each use or def.
        
In the process of refactoring the liveness analysis, I noticed that op_enter was
claiming to def all callee registers. That's wrong; it only defs the non-temporary
variables. Making that change revealed preexisting bugs in the liveness analysis, since
now the validator would pick up cases where the bytecode claimed to use a temporary and
the def calculator never noticed the definition (or the converse - where the bytecode
was actually not using a temporary but the liveness analysis thought that it was a
use). This patch fixes a few of those bugs.

* GNUmakefile.list.am:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/BytecodeLivenessAnalysis.cpp:
(JSC::stepOverInstruction):
* bytecode/BytecodeUseDef.h: Added.
(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpBytecode):
(JSC::CodeBlock::isCaptured):
(JSC::CodeBlock::validate):
* bytecode/CodeBlock.h:
* bytecode/Opcode.h:
(JSC::padOpcodeName):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::BytecodeGenerator):
(JSC::BytecodeGenerator::resolveCallee):
(JSC::BytecodeGenerator::emitMove):
(JSC::BytecodeGenerator::isCaptured):
(JSC::BytecodeGenerator::local):
(JSC::BytecodeGenerator::constLocal):
(JSC::BytecodeGenerator::emitNewFunction):
(JSC::BytecodeGenerator::emitLazyNewFunction):
(JSC::BytecodeGenerator::emitNewFunctionInternal):
* bytecompiler/BytecodeGenerator.h:
(JSC::Local::Local):
(JSC::Local::isCaptured):
(JSC::Local::captureMode):
(JSC::BytecodeGenerator::captureMode):
(JSC::BytecodeGenerator::emitNode):
(JSC::BytecodeGenerator::pushOptimisedForIn):
* bytecompiler/NodesCodegen.cpp:
(JSC::PostfixNode::emitResolve):
(JSC::PrefixNode::emitResolve):
(JSC::ReadModifyResolveNode::emitBytecode):
(JSC::AssignResolveNode::emitBytecode):
(JSC::ConstDeclNode::emitCodeSingle):
(JSC::ForInNode::emitBytecode):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* runtime/SymbolTable.h:
(JSC::SymbolTable::isCaptured):

LayoutTests: 

Reviewed by Mark Hahnenberg.

* js/regress/captured-assignments-expected.txt: Added.
* js/regress/captured-assignments.html: Added.
* js/regress/script-tests/captured-assignments.js: Added.



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@159943 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent a4ea0663
2013-12-01 Filip Pizlo <fpizlo@apple.com>
Stores to local captured variables should be intercepted
https://bugs.webkit.org/show_bug.cgi?id=124883
Reviewed by Mark Hahnenberg.
* js/regress/captured-assignments-expected.txt: Added.
* js/regress/captured-assignments.html: Added.
* js/regress/script-tests/captured-assignments.js: Added.
2013-12-02 Lauro Neto <lauro.neto@openbossa.org> 2013-12-02 Lauro Neto <lauro.neto@openbossa.org>
[MediaStream] Update layout tests to newer spec. [MediaStream] Update layout tests to newer spec.
......
JSRegress/captured-assignments
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS no exception thrown
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<script src="../../resources/js-test-pre.js"></script>
</head>
<body>
<script src="resources/regress-pre.js"></script>
<script src="script-tests/captured-assignments.js"></script>
<script src="resources/regress-post.js"></script>
<script src="../../resources/js-test-post.js"></script>
</body>
</html>
function foo() {
var x = 0;
++x;
for (var x in [1, 2, 3]) {
}
x = 1;
x++;
var y = x++;
var z = y++;
z = x++;
x += 2;
z -= 1;
x *= 2;
z <<= 1;
x |= 5;
return function() { return x++ + z; }
}
for (var i = 0; i < 100; ++i) {
if (foo()() != 17)
throw "Error";
}
2013-12-01 Filip Pizlo <fpizlo@apple.com>
Stores to local captured variables should be intercepted
https://bugs.webkit.org/show_bug.cgi?id=124883
Reviewed by Mark Hahnenberg.
Previously, in bytecode, you could assign to a captured variable just as you would
assign to any other kind of variable. This complicates closure variable constant
inference because we don't have any place where we can intercept stores to captured
variables in the LLInt.
This patch institutes a policy that only certain instructions can store to captured
variables. If you interpret those instructions and you are required to notifyWrite()
then you need to check if the relevant variable is captured. Those instructions are
tracked in CodeBlock.cpp's VerifyCapturedDef. The main one is simply op_captured_mov.
In the future, we'll probably modify those instructions to have a pointer directly to
the VariableWatchpointSet; but for now we just introduce the captured instructions as
placeholders.
In order to validate that the placeholders are inserted correctly, this patch improves
the CodeBlock validation to be able to inspect every def in the bytecode. To do that,
this patch refactors the liveness analysis' use/def calculator to be reusable; it now
takes a functor for each use or def.
In the process of refactoring the liveness analysis, I noticed that op_enter was
claiming to def all callee registers. That's wrong; it only defs the non-temporary
variables. Making that change revealed preexisting bugs in the liveness analysis, since
now the validator would pick up cases where the bytecode claimed to use a temporary and
the def calculator never noticed the definition (or the converse - where the bytecode
was actually not using a temporary but the liveness analysis thought that it was a
use). This patch fixes a few of those bugs.
* GNUmakefile.list.am:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/BytecodeLivenessAnalysis.cpp:
(JSC::stepOverInstruction):
* bytecode/BytecodeUseDef.h: Added.
(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpBytecode):
(JSC::CodeBlock::isCaptured):
(JSC::CodeBlock::validate):
* bytecode/CodeBlock.h:
* bytecode/Opcode.h:
(JSC::padOpcodeName):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::BytecodeGenerator):
(JSC::BytecodeGenerator::resolveCallee):
(JSC::BytecodeGenerator::emitMove):
(JSC::BytecodeGenerator::isCaptured):
(JSC::BytecodeGenerator::local):
(JSC::BytecodeGenerator::constLocal):
(JSC::BytecodeGenerator::emitNewFunction):
(JSC::BytecodeGenerator::emitLazyNewFunction):
(JSC::BytecodeGenerator::emitNewFunctionInternal):
* bytecompiler/BytecodeGenerator.h:
(JSC::Local::Local):
(JSC::Local::isCaptured):
(JSC::Local::captureMode):
(JSC::BytecodeGenerator::captureMode):
(JSC::BytecodeGenerator::emitNode):
(JSC::BytecodeGenerator::pushOptimisedForIn):
* bytecompiler/NodesCodegen.cpp:
(JSC::PostfixNode::emitResolve):
(JSC::PrefixNode::emitResolve):
(JSC::ReadModifyResolveNode::emitBytecode):
(JSC::AssignResolveNode::emitBytecode):
(JSC::ConstDeclNode::emitCodeSingle):
(JSC::ForInNode::emitBytecode):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* runtime/SymbolTable.h:
(JSC::SymbolTable::isCaptured):
2013-12-02 Filip Pizlo <fpizlo@apple.com> 2013-12-02 Filip Pizlo <fpizlo@apple.com>
Instead of watchpointing activation allocation, we should watchpoint entry into functions that have captured variables Instead of watchpointing activation allocation, we should watchpoint entry into functions that have captured variables
......
...@@ -102,10 +102,11 @@ javascriptcore_sources += \ ...@@ -102,10 +102,11 @@ javascriptcore_sources += \
Source/JavaScriptCore/bytecode/ByValInfo.h \ Source/JavaScriptCore/bytecode/ByValInfo.h \
Source/JavaScriptCore/bytecode/BytecodeBasicBlock.cpp \ Source/JavaScriptCore/bytecode/BytecodeBasicBlock.cpp \
Source/JavaScriptCore/bytecode/BytecodeBasicBlock.h \ Source/JavaScriptCore/bytecode/BytecodeBasicBlock.h \
Source/JavaScriptCore/bytecode/BytecodeConventions.h \
Source/JavaScriptCore/bytecode/BytecodeLivenessAnalysis.cpp \ Source/JavaScriptCore/bytecode/BytecodeLivenessAnalysis.cpp \
Source/JavaScriptCore/bytecode/BytecodeLivenessAnalysis.h \ Source/JavaScriptCore/bytecode/BytecodeLivenessAnalysis.h \
Source/JavaScriptCore/bytecode/BytecodeLivenessAnalysisInlines.h \ Source/JavaScriptCore/bytecode/BytecodeLivenessAnalysisInlines.h \
Source/JavaScriptCore/bytecode/BytecodeConventions.h \ Source/JavaScriptCore/bytecode/BytecodeUseDef.h \
Source/JavaScriptCore/bytecode/CallLinkInfo.cpp \ Source/JavaScriptCore/bytecode/CallLinkInfo.cpp \
Source/JavaScriptCore/bytecode/CallLinkInfo.h \ Source/JavaScriptCore/bytecode/CallLinkInfo.h \
Source/JavaScriptCore/bytecode/CallLinkStatus.cpp \ Source/JavaScriptCore/bytecode/CallLinkStatus.cpp \
......
...@@ -751,6 +751,7 @@ ...@@ -751,6 +751,7 @@
<ClInclude Include="..\bytecode\BytecodeBasicBlock.h" /> <ClInclude Include="..\bytecode\BytecodeBasicBlock.h" />
<ClInclude Include="..\bytecode\BytecodeLivenessAnalysis.h" /> <ClInclude Include="..\bytecode\BytecodeLivenessAnalysis.h" />
<ClInclude Include="..\bytecode\BytecodeLivenessAnalysisInline.h" /> <ClInclude Include="..\bytecode\BytecodeLivenessAnalysisInline.h" />
<ClInclude Include="..\bytecode\BytecodeUseDef.h" />
<ClInclude Include="..\bytecode\CallLinkInfo.h" /> <ClInclude Include="..\bytecode\CallLinkInfo.h" />
<ClInclude Include="..\bytecode\CallLinkStatus.h" /> <ClInclude Include="..\bytecode\CallLinkStatus.h" />
<ClInclude Include="..\bytecode\CallReturnOffsetToBytecodeOffset.h" /> <ClInclude Include="..\bytecode\CallReturnOffsetToBytecodeOffset.h" />
......
...@@ -289,6 +289,7 @@ ...@@ -289,6 +289,7 @@
0F8335B71639C1E6001443B5 /* ArrayAllocationProfile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F8335B41639C1E3001443B5 /* ArrayAllocationProfile.cpp */; }; 0F8335B71639C1E6001443B5 /* ArrayAllocationProfile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F8335B41639C1E3001443B5 /* ArrayAllocationProfile.cpp */; };
0F8335B81639C1EA001443B5 /* ArrayAllocationProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F8335B51639C1E3001443B5 /* ArrayAllocationProfile.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0F8335B81639C1EA001443B5 /* ArrayAllocationProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F8335B51639C1E3001443B5 /* ArrayAllocationProfile.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F8364B7164B0C110053329A /* DFGBranchDirection.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F8364B5164B0C0E0053329A /* DFGBranchDirection.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0F8364B7164B0C110053329A /* DFGBranchDirection.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F8364B5164B0C0E0053329A /* DFGBranchDirection.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F885E111849A3BE00F1E3FA /* BytecodeUseDef.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F885E101849A3BE00F1E3FA /* BytecodeUseDef.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F8F2B95172E04A0007DBDA5 /* FTLLink.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F8F2B93172E049E007DBDA5 /* FTLLink.cpp */; }; 0F8F2B95172E04A0007DBDA5 /* FTLLink.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F8F2B93172E049E007DBDA5 /* FTLLink.cpp */; };
0F8F2B96172E04A3007DBDA5 /* FTLLink.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F8F2B94172E049E007DBDA5 /* FTLLink.h */; settings = {ATTRIBUTES = (Private, ); }; }; 0F8F2B96172E04A3007DBDA5 /* FTLLink.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F8F2B94172E049E007DBDA5 /* FTLLink.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F8F2B99172F04FF007DBDA5 /* DFGDesiredIdentifiers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F8F2B97172F04FD007DBDA5 /* DFGDesiredIdentifiers.cpp */; }; 0F8F2B99172F04FF007DBDA5 /* DFGDesiredIdentifiers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F8F2B97172F04FD007DBDA5 /* DFGDesiredIdentifiers.cpp */; };
...@@ -1583,6 +1584,7 @@ ...@@ -1583,6 +1584,7 @@
0F8335B51639C1E3001443B5 /* ArrayAllocationProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArrayAllocationProfile.h; sourceTree = "<group>"; }; 0F8335B51639C1E3001443B5 /* ArrayAllocationProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArrayAllocationProfile.h; sourceTree = "<group>"; };
0F8364B5164B0C0E0053329A /* DFGBranchDirection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGBranchDirection.h; path = dfg/DFGBranchDirection.h; sourceTree = "<group>"; }; 0F8364B5164B0C0E0053329A /* DFGBranchDirection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGBranchDirection.h; path = dfg/DFGBranchDirection.h; sourceTree = "<group>"; };
0F85A31E16AB76AE0077571E /* DFGVariadicFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGVariadicFunction.h; path = dfg/DFGVariadicFunction.h; sourceTree = "<group>"; }; 0F85A31E16AB76AE0077571E /* DFGVariadicFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGVariadicFunction.h; path = dfg/DFGVariadicFunction.h; sourceTree = "<group>"; };
0F885E101849A3BE00F1E3FA /* BytecodeUseDef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BytecodeUseDef.h; sourceTree = "<group>"; };
0F8F2B93172E049E007DBDA5 /* FTLLink.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = FTLLink.cpp; path = ftl/FTLLink.cpp; sourceTree = "<group>"; }; 0F8F2B93172E049E007DBDA5 /* FTLLink.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = FTLLink.cpp; path = ftl/FTLLink.cpp; sourceTree = "<group>"; };
0F8F2B94172E049E007DBDA5 /* FTLLink.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FTLLink.h; path = ftl/FTLLink.h; sourceTree = "<group>"; }; 0F8F2B94172E049E007DBDA5 /* FTLLink.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FTLLink.h; path = ftl/FTLLink.h; sourceTree = "<group>"; };
0F8F2B97172F04FD007DBDA5 /* DFGDesiredIdentifiers.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = DFGDesiredIdentifiers.cpp; path = dfg/DFGDesiredIdentifiers.cpp; sourceTree = "<group>"; }; 0F8F2B97172F04FD007DBDA5 /* DFGDesiredIdentifiers.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = DFGDesiredIdentifiers.cpp; path = dfg/DFGDesiredIdentifiers.cpp; sourceTree = "<group>"; };
...@@ -4058,6 +4060,7 @@ ...@@ -4058,6 +4060,7 @@
C2FCAE0E17A9C24E0034C735 /* BytecodeLivenessAnalysis.cpp */, C2FCAE0E17A9C24E0034C735 /* BytecodeLivenessAnalysis.cpp */,
C2FCAE0F17A9C24E0034C735 /* BytecodeLivenessAnalysis.h */, C2FCAE0F17A9C24E0034C735 /* BytecodeLivenessAnalysis.h */,
0F666EBE183566F900D017F1 /* BytecodeLivenessAnalysisInlines.h */, 0F666EBE183566F900D017F1 /* BytecodeLivenessAnalysisInlines.h */,
0F885E101849A3BE00F1E3FA /* BytecodeUseDef.h */,
0F8023E91613832300A0BA45 /* ByValInfo.h */, 0F8023E91613832300A0BA45 /* ByValInfo.h */,
0F0B83AE14BCF71400885B4F /* CallLinkInfo.cpp */, 0F0B83AE14BCF71400885B4F /* CallLinkInfo.cpp */,
0F0B83AF14BCF71400885B4F /* CallLinkInfo.h */, 0F0B83AF14BCF71400885B4F /* CallLinkInfo.h */,
...@@ -4269,6 +4272,7 @@ ...@@ -4269,6 +4272,7 @@
BC3135640F302FA3003DFD3A /* DebuggerActivation.h in Headers */, BC3135640F302FA3003DFD3A /* DebuggerActivation.h in Headers */,
BC18C3FB0E16F5CD00B34460 /* DebuggerCallFrame.h in Headers */, BC18C3FB0E16F5CD00B34460 /* DebuggerCallFrame.h in Headers */,
0F136D4D174AD69E0075B354 /* DeferGC.h in Headers */, 0F136D4D174AD69E0075B354 /* DeferGC.h in Headers */,
0F885E111849A3BE00F1E3FA /* BytecodeUseDef.h in Headers */,
0FC712DF17CD877C008CC93C /* DeferredCompilationCallback.h in Headers */, 0FC712DF17CD877C008CC93C /* DeferredCompilationCallback.h in Headers */,
A77A423E17A0BBFD00A8DB81 /* DFGAbstractHeap.h in Headers */, A77A423E17A0BBFD00A8DB81 /* DFGAbstractHeap.h in Headers */,
A704D90317A0BAA8006BA554 /* DFGAbstractInterpreter.h in Headers */, A704D90317A0BAA8006BA554 /* DFGAbstractInterpreter.h in Headers */,
......
This diff is collapsed.
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include "CodeBlock.h" #include "CodeBlock.h"
#include "BytecodeGenerator.h" #include "BytecodeGenerator.h"
#include "BytecodeUseDef.h"
#include "CallLinkStatus.h" #include "CallLinkStatus.h"
#include "DFGCapabilities.h" #include "DFGCapabilities.h"
#include "DFGCommon.h" #include "DFGCommon.h"
...@@ -762,6 +763,13 @@ void CodeBlock::dumpBytecode(PrintStream& out, ExecState* exec, const Instructio ...@@ -762,6 +763,13 @@ void CodeBlock::dumpBytecode(PrintStream& out, ExecState* exec, const Instructio
out.printf("%s, %s", registerName(r0).data(), registerName(r1).data()); out.printf("%s, %s", registerName(r0).data(), registerName(r1).data());
break; break;
} }
case op_captured_mov: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
printLocationAndOp(out, exec, location, it, "captured_mov");
out.printf("%s, %s", registerName(r0).data(), registerName(r1).data());
break;
}
case op_not: { case op_not: {
printUnaryOp(out, exec, location, it, "not"); printUnaryOp(out, exec, location, it, "not");
break; break;
...@@ -1213,6 +1221,14 @@ void CodeBlock::dumpBytecode(PrintStream& out, ExecState* exec, const Instructio ...@@ -1213,6 +1221,14 @@ void CodeBlock::dumpBytecode(PrintStream& out, ExecState* exec, const Instructio
out.printf("%s, f%d, %s", registerName(r0).data(), f0, shouldCheck ? "<Checked>" : "<Unchecked>"); out.printf("%s, f%d, %s", registerName(r0).data(), f0, shouldCheck ? "<Checked>" : "<Unchecked>");
break; break;
} }
case op_new_captured_func: {
int r0 = (++it)->u.operand;
int f0 = (++it)->u.operand;
int shouldCheck = (++it)->u.operand;
printLocationAndOp(out, exec, location, it, "new_captured_func");
out.printf("%s, f%d, %s", registerName(r0).data(), f0, shouldCheck ? "<Checked>" : "<Unchecked>");
break;
}
case op_new_func_exp: { case op_new_func_exp: {
int r0 = (++it)->u.operand; int r0 = (++it)->u.operand;
int f0 = (++it)->u.operand; int f0 = (++it)->u.operand;
...@@ -2472,8 +2488,7 @@ bool CodeBlock::isCaptured(VirtualRegister operand, InlineCallFrame* inlineCallF ...@@ -2472,8 +2488,7 @@ bool CodeBlock::isCaptured(VirtualRegister operand, InlineCallFrame* inlineCallF
if (!symbolTable()) if (!symbolTable())
return false; return false;
return operand.offset() <= symbolTable()->captureStart() return symbolTable()->isCaptured(operand.offset());
&& operand.offset() > symbolTable()->captureEnd();
} }
int CodeBlock::framePointerOffsetToGetActivationRegisters(int machineCaptureStart) int CodeBlock::framePointerOffsetToGetActivationRegisters(int machineCaptureStart)
...@@ -3430,6 +3445,48 @@ String CodeBlock::nameForRegister(VirtualRegister virtualRegister) ...@@ -3430,6 +3445,48 @@ String CodeBlock::nameForRegister(VirtualRegister virtualRegister)
return ""; return "";
} }
namespace {
struct VerifyCapturedDef {
void operator()(CodeBlock* codeBlock, Instruction* instruction, OpcodeID opcodeID, int operand)
{
unsigned bytecodeOffset = instruction - codeBlock->instructions().begin();
if (codeBlock->isConstantRegisterIndex(operand)) {
codeBlock->beginValidationDidFail();
dataLog(" At bc#", bytecodeOffset, " encountered a definition of a constant.\n");
codeBlock->endValidationDidFail();
return;
}
switch (opcodeID) {
case op_enter:
case op_captured_mov:
case op_init_lazy_reg:
case op_create_arguments:
case op_new_captured_func:
return;
default:
break;
}
VirtualRegister virtualReg(operand);
if (!virtualReg.isLocal())
return;
if (codeBlock->captureCount() && codeBlock->symbolTable()->isCaptured(operand)) {
codeBlock->beginValidationDidFail();
dataLog(" At bc#", bytecodeOffset, " encountered invalid assignment to captured variable loc", virtualReg.toLocal(), ".\n");
codeBlock->endValidationDidFail();
return;
}
return;
}
};
} // anonymous namespace
void CodeBlock::validate() void CodeBlock::validate()
{ {
BytecodeLivenessAnalysis liveness(this); // Compute directly from scratch so it doesn't effect CodeBlock footprint. BytecodeLivenessAnalysis liveness(this); // Compute directly from scratch so it doesn't effect CodeBlock footprint.
...@@ -3467,6 +3524,16 @@ void CodeBlock::validate() ...@@ -3467,6 +3524,16 @@ void CodeBlock::validate()
} }
} }
} }
for (unsigned bytecodeOffset = 0; bytecodeOffset < instructions().size();) {
Instruction* currentInstruction = instructions().begin() + bytecodeOffset;
OpcodeID opcodeID = m_vm->interpreter->getOpcodeID(currentInstruction->u.opcode);
VerifyCapturedDef verifyCapturedDef;
computeDefsForBytecodeOffset(this, bytecodeOffset, verifyCapturedDef);
bytecodeOffset += opcodeLength(opcodeID);
}
} }
void CodeBlock::beginValidationDidFail() void CodeBlock::beginValidationDidFail()
......
...@@ -930,7 +930,12 @@ public: ...@@ -930,7 +930,12 @@ public:
bool m_allTransitionsHaveBeenMarked; // Initialized and used on every GC. bool m_allTransitionsHaveBeenMarked; // Initialized and used on every GC.
bool m_didFailFTLCompilation; bool m_didFailFTLCompilation;
// Internal methods for use by validation code. It would be private if it wasn't
// for the fact that we use it from anonymous namespaces.
void beginValidationDidFail();
NO_RETURN_DUE_TO_CRASH void endValidationDidFail();
protected: protected:
virtual void visitWeakReferences(SlotVisitor&) OVERRIDE; virtual void visitWeakReferences(SlotVisitor&) OVERRIDE;
virtual void finalizeUnconditionally() OVERRIDE; virtual void finalizeUnconditionally() OVERRIDE;
...@@ -1031,9 +1036,6 @@ private: ...@@ -1031,9 +1036,6 @@ private:
m_rareData = adoptPtr(new RareData); m_rareData = adoptPtr(new RareData);
} }
void beginValidationDidFail();
NO_RETURN_DUE_TO_CRASH void endValidationDidFail();
#if ENABLE(JIT) #if ENABLE(JIT)
void resetStubInternal(RepatchBuffer&, StructureStubInfo&); void resetStubInternal(RepatchBuffer&, StructureStubInfo&);
void resetStubDuringGCInternal(RepatchBuffer&, StructureStubInfo&); void resetStubDuringGCInternal(RepatchBuffer&, StructureStubInfo&);
......
...@@ -55,6 +55,7 @@ namespace JSC { ...@@ -55,6 +55,7 @@ namespace JSC {
macro(op_new_array_buffer, 5) \ macro(op_new_array_buffer, 5) \
macro(op_new_regexp, 3) \ macro(op_new_regexp, 3) \
macro(op_mov, 3) \ macro(op_mov, 3) \
macro(op_captured_mov, 3) \
\ \
macro(op_not, 3) \ macro(op_not, 3) \
macro(op_eq, 4) \ macro(op_eq, 4) \
...@@ -154,6 +155,7 @@ namespace JSC { ...@@ -154,6 +155,7 @@ namespace JSC {
macro(op_switch_string, 4) \ macro(op_switch_string, 4) \
\ \
macro(op_new_func, 4) \ macro(op_new_func, 4) \
macro(op_new_captured_func, 4) \
macro(op_new_func_exp, 3) \ macro(op_new_func_exp, 3) \
macro(op_call, 8) /* has value profiling */ \ macro(op_call, 8) /* has value profiling */ \
macro(op_call_eval, 8) /* has value profiling */ \ macro(op_call_eval, 8) /* has value profiling */ \
......
...@@ -307,7 +307,7 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionBodyNode* functionBody, Unl ...@@ -307,7 +307,7 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionBodyNode* functionBody, Unl
instructions().append(m_activationRegister->index()); instructions().append(m_activationRegister->index());
} }
m_functions.add(ident.impl()); m_functions.add(ident.impl());
emitNewFunction(addVar(ident, false), function); emitNewFunction(addVar(ident, false), IsCaptured, function);
} }
} }
for (size_t i = 0; i < varStack.size(); ++i) { for (size_t i = 0; i < varStack.size(); ++i) {
...@@ -335,7 +335,7 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionBodyNode* functionBody, Unl ...@@ -335,7 +335,7 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionBodyNode* functionBody, Unl
// Don't lazily create functions that override the name 'arguments' // Don't lazily create functions that override the name 'arguments'
// as this would complicate lazy instantiation of actual arguments. // as this would complicate lazy instantiation of actual arguments.
if (!canLazilyCreateFunctions || ident == propertyNames().arguments) if (!canLazilyCreateFunctions || ident == propertyNames().arguments)
emitNewFunction(reg.get(), function); emitNewFunction(reg.get(), NotCaptured, function);
else { else {
emitInitLazyRegister(reg.get()); emitInitLazyRegister(reg.get());
m_lazyFunctions.set(reg->virtualRegister().toLocal(), function); m_lazyFunctions.set(reg->virtualRegister().toLocal(), function);
...@@ -475,7 +475,7 @@ RegisterID* BytecodeGenerator::resolveCallee(FunctionBodyNode* functionBodyNode) ...@@ -475,7 +475,7 @@ RegisterID* BytecodeGenerator::resolveCallee(FunctionBodyNode* functionBodyNode)
return &m_calleeRegister; return &m_calleeRegister;
// Move the callee into the captured section of the stack. // Move the callee into the captured section of the stack.
return emitMove(addVar(), &m_calleeRegister); return emitMove(addVar(), IsCaptured, &m_calleeRegister);
} }
void BytecodeGenerator::addCallee(FunctionBodyNode* functionBodyNode, RegisterID* calleeRegister) void BytecodeGenerator::addCallee(FunctionBodyNode* functionBodyNode, RegisterID* calleeRegister)
...@@ -1000,16 +1000,21 @@ unsigned BytecodeGenerator::addRegExp(RegExp* r) ...@@ -1000,16 +1000,21 @@ unsigned BytecodeGenerator::addRegExp(RegExp* r)
return m_codeBlock->addRegExp(r); return m_codeBlock->addRegExp(r);
} }
RegisterID* BytecodeGenerator::emitMove(RegisterID* dst, RegisterID* src) RegisterID* BytecodeGenerator::emitMove(RegisterID* dst, CaptureMode captureMode, RegisterID* src)
{ {
m_staticPropertyAnalyzer.mov(dst->index(), src->index()); m_staticPropertyAnalyzer.mov(dst->index(), src->index());
emitOpcode(op_mov); emitOpcode(captureMode == IsCaptured ? op_captured_mov : op_mov);
instructions().append(dst->index()); instructions().append(dst->index());
instructions().append(src->index()); instructions().append(src->index());
return dst; return dst;
} }
RegisterID* BytecodeGenerator::emitMove(RegisterID* dst, RegisterID* src)
{
return emitMove(dst, captureMode(dst->index()), src);
}
RegisterID* BytecodeGenerator::emitUnaryOp(OpcodeID opcodeID, RegisterID* dst, RegisterID* src) RegisterID* BytecodeGenerator::emitUnaryOp(OpcodeID opcodeID, RegisterID* dst, RegisterID* src)
{ {
emitOpcode(opcodeID); emitOpcode(opcodeID);
...@@ -1160,11 +1165,16 @@ RegisterID* BytecodeGenerator::emitLoadGlobalObject(RegisterID* dst) ...@@ -1160,11 +1165,16 @@ RegisterID* BytecodeGenerator::emitLoadGlobalObject(RegisterID* dst)
return m_globalObjectRegister; return m_globalObjectRegister;
} }
bool BytecodeGenerator::isCaptured(int operand)
{
return m_symbolTable && m_symbolTable->isCaptured(operand);
}
Local BytecodeGenerator::local(const Identifier& property) Local BytecodeGenerator::local(const Identifier& property)
{ {
if (property == propertyNames().thisIdentifier) if (property == propertyNames().thisIdentifier)
return Local(thisRegister(), ReadOnly); return Local(thisRegister(), ReadOnly, NotCaptured);
if (property == propertyNames().arguments) if (property == propertyNames().arguments)
createArgumentsIfNecessary(); createArgumentsIfNecessary();
...@@ -1176,7 +1186,7 @@ Local BytecodeGenerator::local(const Identifier& property) ...@@ -1176,7 +1186,7 @@ Local BytecodeGenerator::local(const Identifier& property)
return Local(); return Local();
RegisterID* local = createLazyRegisterIfNecessary(&registerFor(entry.getIndex())); RegisterID* local = createLazyRegisterIfNecessary(&registerFor(entry.getIndex()));
return Local(local, entry.getAttributes()); return Local(local, entry.getAttributes(), captureMode(local->index()));
} }
Local BytecodeGenerator::constLocal(const Identifier& property) Local BytecodeGenerator::constLocal(const Identifier& property)
...@@ -1189,7 +1199,7 @@ Local BytecodeGenerator::constLocal(const Identifier& property) ...@@ -1189,7 +1199,7 @@ Local BytecodeGenerator::constLocal(const Identifier& property)
return Local(); return Local();
RegisterID* local = createLazyRegisterIfNecessary(&registerFor(entry.getIndex())); RegisterID* local = createLazyRegisterIfNecessary(&registerFor(entry.getIndex()));
return Local(local, entry.getAttributes()); return Local(local, entry.getAttributes(), captureMode(local->index()));
} }
void BytecodeGenerator::emitCheckHasInstance(RegisterID* dst, RegisterID* value, RegisterID* base, Label* target) void BytecodeGenerator::emitCheckHasInstance(RegisterID* dst, RegisterID* value, RegisterID* base, Label* target)
...@@ -1550,9 +1560,9 @@ RegisterID* BytecodeGenerator::emitNewArray(RegisterID* dst, ElementNode* elemen ...@@ -1550,9 +1560,9 @@ RegisterID* BytecodeGenerator::emitNewArray(RegisterID* dst, ElementNode* elemen
return dst; return dst;
} }
RegisterID* BytecodeGenerator::emitNewFunction(RegisterID* dst, FunctionBodyNode* function) RegisterID* BytecodeGenerator::emitNewFunction(RegisterID* dst, CaptureMode captureMode, FunctionBodyNode* function)
{ {
return emitNewFunctionInternal(dst, m_codeBlock->addFunctionDecl(makeFunction(function)), false); return emitNewFunctionInternal(dst, captureMode, m_codeBlock->addFunctionDecl(makeFunction(function)), false);
} }
RegisterID* BytecodeGenerator::emitLazyNewFunction(RegisterID* dst, FunctionBodyNode* function) RegisterID* BytecodeGenerator::emitLazyNewFunction(RegisterID* dst, FunctionBodyNode* function)
...@@ -1560,13 +1570,13 @@ RegisterID* BytecodeGenerator::emitLazyNewFunction(RegisterID* dst, FunctionBody ...@@ -1560,13 +1570,13 @@ RegisterID* BytecodeGenerator::emitLazyNewFunction(RegisterID* dst, FunctionBody
FunctionOffsetMap::AddResult ptr = m_functionOffsets.add(function, 0); FunctionOffsetMap::AddResult ptr = m_functionOffsets.add(function, 0);
if (ptr.isNewEntry) if (ptr.isNewEntry)
ptr.iterator->value = m_codeBlock->addFunctionDecl(makeFunction(function)); ptr.iterator->value = m_codeBlock->addFunctionDecl(makeFunction(function));
return emitNewFunctionInternal(dst, ptr.iterator->value, true); return emitNewFunctionInternal(dst, NotCaptured, ptr.iterator->value, true);
} }
RegisterID* BytecodeGenerator::emitNewFunctionInternal(RegisterID* dst, unsigned index, bool doNullCheck) RegisterID* BytecodeGenerator::emitNewFunctionInternal(RegisterID* dst, CaptureMode captureMode, unsigned index, bool doNullCheck)
{ {
createActivationIfNecessary(); createActivationIfNecessary();
emitOpcode(op_new_func); emitOpcode(captureMode == IsCaptured ? op_new_captured_func : op_new_func);
instructions().append(dst->index()); instructions().append(dst->index());
instructions().append(index); instructions().append(index);
instructions().append(doNullCheck); instructions().append(doNullCheck);
......
...@@ -114,6 +114,11 @@ namespace JSC { ...@@ -114,6 +114,11 @@ namespace JSC {
TryData* tryData; TryData* tryData;
}; };
enum CaptureMode {
NotCaptured,
IsCaptured
};
class Local { class Local {
public: public:
Local() Local()
...@@ -122,9 +127,10 @@ namespace JSC { ...@@ -122,9 +127,10 @@ namespace JSC {
{ {
} }
Local(RegisterID* local, unsigned attributes) Local(RegisterID* local, unsigned attributes, CaptureMode captureMode)
: m_local(local) : m_local(local)
, m_attributes(attributes) , m_attributes