Implement try/catch in the DFG.
authorsbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 18 Sep 2015 23:37:42 +0000 (23:37 +0000)
committersbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 18 Sep 2015 23:37:42 +0000 (23:37 +0000)
https://bugs.webkit.org/show_bug.cgi?id=147374

Reviewed by Filip Pizlo.

Source/JavaScriptCore:

This patch implements try/catch inside the DFG JIT.
It also prevents tier up to the FTL for any functions
that have an op_catch in them that are DFG compiled.

This patch accomplishes implementing try/catch inside
the DFG by OSR exiting to op_catch when an exception is thrown.
We can OSR exit from an exception inside the DFG in two ways:
1) We have a JS call (can also be via implicit getter/setter in GetById/PutById)
2) We have an exception when returing from a callOperation

In the case of (1), we get to the OSR exit from genericUnwind because
the exception was thrown in a child call frame. This means these
OSR exits must act as defacto op_catches (even though we will still OSR
exit to a baseline op_catch). That means they must restore the stack pointer
and call frame.

In the case of (2), we can skip genericUnwind because we know the exception
check will take us to a particular OSR exit. Instead, we link these
exception checks as jumps to a particular OSR exit.

Both types of OSR exits will exit into op_catch inside the baseline JIT.
Because they exit to op_catch, these OSR exits must set callFrameForCatch
to the proper call frame pointer.

We "handle" all exceptions inside the machine frame of the DFG code
block. This means the machine code block is responsible for "catching"
exceptions of any inlined frames' try/catch. OSR exit will then exit to
the proper baseline CodeBlock after reifying the inlined frames
(DFG::OSRExit::m_codeOrigin corresponds to the op_catch we will exit to).
Also, genericUnwind will never consult an inlined call frame's CodeBlock to
see if they can catch the exception because they can't. We always unwind to the
next machine code block frame. The DFG CodeBlock changes how the exception
handler table is keyed: it is now keyed by CallSiteIndex for DFG code blocks.

So, when consulting call sites that throw, we keep track of the CallSiteIndex,
and the HandlerInfo for the corresponding baseline exception handler for
that particular CallSiteIndex (if an exception at that call site will be caught).
Then, when we're inside DFG::JITCompiler::link(), we install new HandlerInfo's
inside the DFG CodeBlock and key it by the corresponding CallSiteIndex.
(The CodeBlock only has HandlerInfos for the OSR exits that are to be arrived
at from genericUnwind).

Also, each OSR exit will know if it acting as an exception handler, and
whether or not it will be arrived at from genericUnwind. When we know we
will arrive at an OSR exit from genericUnwind, we set the corresponding
HandlerInfo's nativeCode CodeLocationLabel field to be the OSR exit.

This patch also introduces a new Phase inside the DFG that ensures
that DFG CodeBlocks that handle exceptions take the necessary
steps to keep live variables at "op_catch" live according the
OSR exit value recovery machinery. We accomplish this by flushing
all live op_catch variables to the stack when inside a "try" block.

* CMakeLists.txt:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters:
* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::handlerForBytecodeOffset):
(JSC::CodeBlock::handlerForIndex):
* bytecode/CodeBlock.h:
(JSC::CodeBlock::clearExceptionHandlers):
(JSC::CodeBlock::appendExceptionHandler):
* bytecode/PreciseJumpTargets.cpp:
(JSC::computePreciseJumpTargets):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::getLocal):
(JSC::DFG::ByteCodeParser::setLocal):
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* dfg/DFGCommonData.cpp:
(JSC::DFG::CommonData::addCodeOrigin):
(JSC::DFG::CommonData::lastCallSite):
(JSC::DFG::CommonData::shrinkToFit):
* dfg/DFGCommonData.h:
* dfg/DFGGraph.h:
* dfg/DFGJITCompiler.cpp:
(JSC::DFG::JITCompiler::linkOSRExits):
(JSC::DFG::JITCompiler::link):
(JSC::DFG::JITCompiler::compile):
(JSC::DFG::JITCompiler::noticeOSREntry):
(JSC::DFG::JITCompiler::appendExceptionHandlingOSRExit):
(JSC::DFG::JITCompiler::willCatchExceptionInMachineFrame):
(JSC::DFG::JITCompiler::exceptionCheck):
(JSC::DFG::JITCompiler::recordCallSiteAndGenerateExceptionHandlingOSRExitIfNeeded):
* dfg/DFGJITCompiler.h:
(JSC::DFG::JITCompiler::emitStoreCodeOrigin):
(JSC::DFG::JITCompiler::emitStoreCallSiteIndex):
(JSC::DFG::JITCompiler::appendCall):
(JSC::DFG::JITCompiler::exceptionCheckWithCallFrameRollback):
(JSC::DFG::JITCompiler::blockHeads):
(JSC::DFG::JITCompiler::exceptionCheck): Deleted.
* dfg/DFGLiveCatchVariablePreservationPhase.cpp: Added.
(JSC::DFG::FlushLiveCatchVariablesInsertionPhase::FlushLiveCatchVariablesInsertionPhase):
(JSC::DFG::FlushLiveCatchVariablesInsertionPhase::run):
(JSC::DFG::FlushLiveCatchVariablesInsertionPhase::willCatchException):
(JSC::DFG::FlushLiveCatchVariablesInsertionPhase::handleBlock):
(JSC::DFG::FlushLiveCatchVariablesInsertionPhase::newVariableAccessData):
(JSC::DFG::performLiveCatchVariablePreservationPhase):
* dfg/DFGLiveCatchVariablePreservationPhase.h: Added.
* dfg/DFGOSRExit.cpp:
(JSC::DFG::OSRExit::OSRExit):
(JSC::DFG::OSRExit::setPatchableCodeOffset):
* dfg/DFGOSRExit.h:
(JSC::DFG::OSRExit::considerAddingAsFrequentExitSite):
* dfg/DFGOSRExitCompiler.cpp:
* dfg/DFGOSRExitCompiler32_64.cpp:
(JSC::DFG::OSRExitCompiler::compileExit):
* dfg/DFGOSRExitCompiler64.cpp:
(JSC::DFG::OSRExitCompiler::compileExit):
* dfg/DFGOSRExitCompilerCommon.cpp:
(JSC::DFG::osrWriteBarrier):
(JSC::DFG::adjustAndJumpToTarget):
* dfg/DFGOSRExitCompilerCommon.h:
* dfg/DFGPlan.cpp:
(JSC::DFG::Plan::compileInThreadImpl):
* dfg/DFGSlowPathGenerator.h:
(JSC::DFG::SlowPathGenerator::SlowPathGenerator):
(JSC::DFG::SlowPathGenerator::~SlowPathGenerator):
(JSC::DFG::SlowPathGenerator::generate):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::cachedGetById):
(JSC::DFG::SpeculativeJIT::cachedPutById):
(JSC::DFG::SpeculativeJIT::emitCall):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::cachedGetById):
(JSC::DFG::SpeculativeJIT::cachedPutById):
(JSC::DFG::SpeculativeJIT::emitCall):
* dfg/DFGTierUpCheckInjectionPhase.cpp:
(JSC::DFG::TierUpCheckInjectionPhase::run):
* ftl/FTLOSRExitCompiler.cpp:
(JSC::FTL::compileStub):
* interpreter/Interpreter.cpp:
(JSC::GetCatchHandlerFunctor::operator()):
(JSC::UnwindFunctor::operator()):
* interpreter/StackVisitor.cpp:
(JSC::StackVisitor::gotoNextFrame):
(JSC::StackVisitor::unwindToMachineCodeBlockFrame):
(JSC::StackVisitor::readFrame):
* interpreter/StackVisitor.h:
(JSC::StackVisitor::operator*):
(JSC::StackVisitor::operator->):
* jit/AssemblyHelpers.cpp:
(JSC::AssemblyHelpers::emitExceptionCheck):
(JSC::AssemblyHelpers::emitNonPatchableExceptionCheck):
(JSC::AssemblyHelpers::emitStoreStructureWithTypeInfo):
* jit/AssemblyHelpers.h:
(JSC::AssemblyHelpers::emitCount):
* jit/JITExceptions.cpp:
(JSC::genericUnwind):
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_catch):
* jit/JITOpcodes32_64.cpp:
(JSC::JIT::emit_op_catch):
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* runtime/VM.cpp:
(JSC::VM::VM):
* runtime/VM.h:
(JSC::VM::clearException):
(JSC::VM::clearLastException):
(JSC::VM::addressOfCallFrameForCatch):
(JSC::VM::exception):
(JSC::VM::addressOfException):
* tests/stress/dfg-exception-try-catch-in-constructor-with-inlined-throw.js: Added.
(f):
(bar):
(Foo):
* tests/stress/es6-for-of-loop-exception.js: Added.
(assert):
(shouldThrowInvalidConstAssignment):
(baz):
(foo):
* tests/stress/exception-dfg-inlined-frame-not-strict-equal.js: Added.
(assert):
(o.valueOf):
(o.toString):
(read):
(bar):
(foo):
* tests/stress/exception-dfg-not-strict-equal.js: Added.
(foo):
(o.valueOf):
(o.toString):
(assert):
(shouldDoSomethingInFinally):
(catch):
* tests/stress/exception-dfg-operation-read-value.js: Added.
(assert):
(o.valueOf):
(o.toString):
(read):
(foo):
* tests/stress/exception-dfg-throw-from-catch-block.js: Added.
(assert):
(baz):
(bar):
(foo):

LayoutTests:

* js/regress/raytrace-with-empty-try-catch-expected.txt: Added.
* js/regress/raytrace-with-empty-try-catch.html: Added.
* js/regress/raytrace-with-try-catch-expected.txt: Added.
* js/regress/raytrace-with-try-catch.html: Added.
* js/regress/script-tests/raytrace-with-empty-try-catch.js: Added.
(createVector):
(sqrLengthVector):
(lengthVector):
(addVector):
(subVector):
(scaleVector):
(normaliseVector):
(add):
(sub):
(scalev):
(dot):
(scale):
(cross):
(normalise):
(transformMatrix):
(invertMatrix):
(Triangle):
(Triangle.prototype.intersect):
(Scene):
(Scene.prototype.intersect):
(Scene.prototype.blocked):
(Camera):
(Camera.prototype.generateRayPair):
(renderRows):
(Camera.prototype.render):
(raytraceScene.floorShader):
(raytraceScene):
(arrayToCanvasCommands):
* js/regress/script-tests/raytrace-with-try-catch.js: Added.
(randomException):
(createVector):
(sqrLengthVector):
(lengthVector):
(addVector):
(subVector):
(scaleVector):
(normaliseVector):
(add):
(sub):
(scalev):
(dot):
(scale):
(cross):
(normalise):
(transformMatrix):
(invertMatrix):
(Triangle):
(Triangle.prototype.intersect):
(Scene):
(Scene.prototype.intersect):
(Scene.prototype.blocked):
(Camera):
(Camera.prototype.generateRayPair):
(renderRows):
(Camera.prototype.render):
(raytraceScene.floorShader):
(raytraceScene):
(arrayToCanvasCommands):
* js/regress/script-tests/v8-raytrace-with-empty-try-catch.js: Added.
(Class.create):
(Object.extend):
(Flog.RayTracer.Color.prototype.initialize):
(Flog.RayTracer.Color.prototype.add):
(Flog.RayTracer.Color.prototype.addScalar):
(Flog.RayTracer.Color.prototype.subtract):
(Flog.RayTracer.Color.prototype.multiply):
(Flog.RayTracer.Color.prototype.multiplyScalar):
(Flog.RayTracer.Color.prototype.divideFactor):
(Flog.RayTracer.Color.prototype.limit):
(Flog.RayTracer.Color.prototype.distance):
(Flog.RayTracer.Color.prototype.blend):
(Flog.RayTracer.Color.prototype.brightness):
(Flog.RayTracer.Color.prototype.toString):
(Flog.RayTracer.Light.prototype.initialize):
(Flog.RayTracer.Light.prototype.toString):
(Flog.RayTracer.Vector.prototype.initialize):
(Flog.RayTracer.Vector.prototype.copy):
(Flog.RayTracer.Vector.prototype.normalize):
(Flog.RayTracer.Vector.prototype.magnitude):
(Flog.RayTracer.Vector.prototype.cross):
(Flog.RayTracer.Vector.prototype.dot):
(Flog.RayTracer.Vector.prototype.add):
(Flog.RayTracer.Vector.prototype.subtract):
(Flog.RayTracer.Vector.prototype.multiplyVector):
(Flog.RayTracer.Vector.prototype.multiplyScalar):
(Flog.RayTracer.Vector.prototype.toString):
(Flog.RayTracer.Ray.prototype.initialize):
(Flog.RayTracer.Ray.prototype.toString):
(Flog.RayTracer.Scene.prototype.initialize):
(Flog.RayTracer.Material.BaseMaterial.prototype.initialize):
(Flog.RayTracer.Material.BaseMaterial.prototype.getColor):
(Flog.RayTracer.Material.BaseMaterial.prototype.wrapUp):
(Flog.RayTracer.Material.BaseMaterial.prototype.toString):
(Flog.RayTracer.Material.Solid.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.initialize):
(Flog.RayTracer.Material.Solid.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.getColor):
(Flog.RayTracer.Material.Solid.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.toString):
(Flog.RayTracer.Material.Solid.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial):
(Flog.RayTracer.Material.Chessboard.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.initialize):
(Flog.RayTracer.Material.Chessboard.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.getColor):
(Flog.RayTracer.Material.Chessboard.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.toString):
(Flog.RayTracer.Material.Chessboard.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial):
(Flog.RayTracer.Shape.Sphere.prototype.initialize):
(Flog.RayTracer.Shape.Sphere.prototype.intersect):
(Flog.RayTracer.Shape.Sphere.prototype.toString):
(Flog.RayTracer.Shape.Plane.prototype.initialize):
(Flog.RayTracer.Shape.Plane.prototype.intersect):
(Flog.RayTracer.Shape.Plane.prototype.toString):
(Flog.RayTracer.IntersectionInfo.prototype.initialize):
(Flog.RayTracer.IntersectionInfo.prototype.toString):
(Flog.RayTracer.Camera.prototype.initialize):
(Flog.RayTracer.Camera.prototype.getRay):
(Flog.RayTracer.Camera.prototype.toString):
(Flog.RayTracer.Background.prototype.initialize):
(Flog.RayTracer.Engine.prototype.initialize):
(Flog.RayTracer.Engine.prototype.setPixel):
(Flog.RayTracer.Engine.prototype.renderScene):
(Flog.RayTracer.Engine.prototype.getPixelColor):
(Flog.RayTracer.Engine.prototype.testIntersection):
(Flog.RayTracer.Engine.prototype.getReflectionRay):
(Flog.RayTracer.Engine.prototype.rayTrace):
(renderScene):
* js/regress/script-tests/v8-raytrace-with-try-catch.js: Added.
(randomException):
(Class.create):
(Object.extend):
(Flog.RayTracer.Color.prototype.initialize):
(Flog.RayTracer.Color.prototype.add):
(Flog.RayTracer.Color.prototype.addScalar):
(Flog.RayTracer.Color.prototype.subtract):
(Flog.RayTracer.Color.prototype.multiply):
(Flog.RayTracer.Color.prototype.multiplyScalar):
(Flog.RayTracer.Color.prototype.divideFactor):
(Flog.RayTracer.Color.prototype.limit):
(Flog.RayTracer.Color.prototype.distance):
(Flog.RayTracer.Color.prototype.blend):
(Flog.RayTracer.Color.prototype.brightness):
(Flog.RayTracer.Color.prototype.toString):
(Flog.RayTracer.Light.prototype.initialize):
(Flog.RayTracer.Light.prototype.toString):
(Flog.RayTracer.Vector.prototype.initialize):
(Flog.RayTracer.Vector.prototype.copy):
(Flog.RayTracer.Vector.prototype.normalize):
(Flog.RayTracer.Vector.prototype.magnitude):
(Flog.RayTracer.Vector.prototype.cross):
(Flog.RayTracer.Vector.prototype.dot):
(Flog.RayTracer.Vector.prototype.add):
(Flog.RayTracer.Vector.prototype.subtract):
(Flog.RayTracer.Vector.prototype.multiplyVector):
(Flog.RayTracer.Vector.prototype.multiplyScalar):
(Flog.RayTracer.Vector.prototype.toString):
(Flog.RayTracer.Ray.prototype.initialize):
(Flog.RayTracer.Ray.prototype.toString):
(Flog.RayTracer.Scene.prototype.initialize):
(Flog.RayTracer.Material.BaseMaterial.prototype.initialize):
(Flog.RayTracer.Material.BaseMaterial.prototype.getColor):
(Flog.RayTracer.Material.BaseMaterial.prototype.wrapUp):
(Flog.RayTracer.Material.BaseMaterial.prototype.toString):
(Flog.RayTracer.Material.Solid.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.initialize):
(Flog.RayTracer.Material.Solid.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.getColor):
(Flog.RayTracer.Material.Solid.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.toString):
(Flog.RayTracer.Material.Solid.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial):
(Flog.RayTracer.Material.Chessboard.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.initialize):
(Flog.RayTracer.Material.Chessboard.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.getColor):
(Flog.RayTracer.Material.Chessboard.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.toString):
(Flog.RayTracer.Material.Chessboard.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial):
(Flog.RayTracer.Shape.Sphere.prototype.initialize):
(Flog.RayTracer.Shape.Sphere.prototype.intersect):
(Flog.RayTracer.Shape.Sphere.prototype.toString):
(Flog.RayTracer.Shape.Plane.prototype.initialize):
(Flog.RayTracer.Shape.Plane.prototype.intersect):
(Flog.RayTracer.Shape.Plane.prototype.toString):
(Flog.RayTracer.IntersectionInfo.prototype.initialize):
(Flog.RayTracer.IntersectionInfo.prototype.toString):
(Flog.RayTracer.Camera.prototype.initialize):
(Flog.RayTracer.Camera.prototype.getRay):
(Flog.RayTracer.Camera.prototype.toString):
(Flog.RayTracer.Background.prototype.initialize):
(Flog.RayTracer.Engine.prototype.initialize):
(Flog.RayTracer.Engine.prototype.setPixel):
(Flog.RayTracer.Engine.prototype.renderScene):
(Flog.RayTracer.Engine.prototype.getPixelColor):
(Flog.RayTracer.Engine.prototype.testIntersection):
(Flog.RayTracer.Engine.prototype.getReflectionRay):
(Flog.RayTracer.Engine.prototype.rayTrace):
(renderScene):
* js/regress/v8-raytrace-with-empty-try-catch-expected.txt: Added.
* js/regress/v8-raytrace-with-empty-try-catch.html: Added.
* js/regress/v8-raytrace-with-try-catch-expected.txt: Added.
* js/regress/v8-raytrace-with-try-catch.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@189995 268f45cc-cd09-0410-ab3c-d52691b4dbfc

62 files changed:
LayoutTests/ChangeLog
LayoutTests/js/regress/raytrace-with-empty-try-catch-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/raytrace-with-empty-try-catch.html [new file with mode: 0644]
LayoutTests/js/regress/raytrace-with-try-catch-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/raytrace-with-try-catch.html [new file with mode: 0644]
LayoutTests/js/regress/script-tests/raytrace-with-empty-try-catch.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/raytrace-with-try-catch.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/v8-raytrace-with-empty-try-catch.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/v8-raytrace-with-try-catch.js [new file with mode: 0644]
LayoutTests/js/regress/v8-raytrace-with-empty-try-catch-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/v8-raytrace-with-empty-try-catch.html [new file with mode: 0644]
LayoutTests/js/regress/v8-raytrace-with-try-catch-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/v8-raytrace-with-try-catch.html [new file with mode: 0644]
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecode/CodeBlock.h
Source/JavaScriptCore/bytecode/PreciseJumpTargets.cpp
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGCapabilities.cpp
Source/JavaScriptCore/dfg/DFGCommonData.cpp
Source/JavaScriptCore/dfg/DFGCommonData.h
Source/JavaScriptCore/dfg/DFGGraph.h
Source/JavaScriptCore/dfg/DFGJITCompiler.cpp
Source/JavaScriptCore/dfg/DFGJITCompiler.h
Source/JavaScriptCore/dfg/DFGLiveCatchVariablePreservationPhase.cpp [new file with mode: 0644]
Source/JavaScriptCore/dfg/DFGLiveCatchVariablePreservationPhase.h [new file with mode: 0644]
Source/JavaScriptCore/dfg/DFGOSRExit.cpp
Source/JavaScriptCore/dfg/DFGOSRExit.h
Source/JavaScriptCore/dfg/DFGOSRExitCompiler.cpp
Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp
Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp
Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp
Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.h
Source/JavaScriptCore/dfg/DFGPlan.cpp
Source/JavaScriptCore/dfg/DFGSlowPathGenerator.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/dfg/DFGTierUpCheckInjectionPhase.cpp
Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp
Source/JavaScriptCore/interpreter/Interpreter.cpp
Source/JavaScriptCore/interpreter/StackVisitor.cpp
Source/JavaScriptCore/interpreter/StackVisitor.h
Source/JavaScriptCore/jit/AssemblyHelpers.cpp
Source/JavaScriptCore/jit/AssemblyHelpers.h
Source/JavaScriptCore/jit/JITExceptions.cpp
Source/JavaScriptCore/jit/JITOpcodes.cpp
Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
Source/JavaScriptCore/runtime/VM.cpp
Source/JavaScriptCore/runtime/VM.h
Source/JavaScriptCore/tests/stress/dfg-exception-try-catch-in-constructor-with-inlined-throw.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/es6-for-of-loop-exception.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/exception-dfg-inlined-frame-not-strict-equal.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/exception-dfg-not-strict-equal.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/exception-dfg-operation-read-value.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/exception-dfg-throw-from-catch-block.js [new file with mode: 0644]

index 6da1c51..56d5289 100644 (file)
@@ -1,3 +1,205 @@
+2015-09-18  Saam barati  <sbarati@apple.com>
+
+        Implement try/catch in the DFG.
+        https://bugs.webkit.org/show_bug.cgi?id=147374
+
+        Reviewed by Filip Pizlo.
+
+        * js/regress/raytrace-with-empty-try-catch-expected.txt: Added.
+        * js/regress/raytrace-with-empty-try-catch.html: Added.
+        * js/regress/raytrace-with-try-catch-expected.txt: Added.
+        * js/regress/raytrace-with-try-catch.html: Added.
+        * js/regress/script-tests/raytrace-with-empty-try-catch.js: Added.
+        (createVector):
+        (sqrLengthVector):
+        (lengthVector):
+        (addVector):
+        (subVector):
+        (scaleVector):
+        (normaliseVector):
+        (add):
+        (sub):
+        (scalev):
+        (dot):
+        (scale):
+        (cross):
+        (normalise):
+        (transformMatrix):
+        (invertMatrix):
+        (Triangle):
+        (Triangle.prototype.intersect):
+        (Scene):
+        (Scene.prototype.intersect):
+        (Scene.prototype.blocked):
+        (Camera):
+        (Camera.prototype.generateRayPair):
+        (renderRows):
+        (Camera.prototype.render):
+        (raytraceScene.floorShader):
+        (raytraceScene):
+        (arrayToCanvasCommands):
+        * js/regress/script-tests/raytrace-with-try-catch.js: Added.
+        (randomException):
+        (createVector):
+        (sqrLengthVector):
+        (lengthVector):
+        (addVector):
+        (subVector):
+        (scaleVector):
+        (normaliseVector):
+        (add):
+        (sub):
+        (scalev):
+        (dot):
+        (scale):
+        (cross):
+        (normalise):
+        (transformMatrix):
+        (invertMatrix):
+        (Triangle):
+        (Triangle.prototype.intersect):
+        (Scene):
+        (Scene.prototype.intersect):
+        (Scene.prototype.blocked):
+        (Camera):
+        (Camera.prototype.generateRayPair):
+        (renderRows):
+        (Camera.prototype.render):
+        (raytraceScene.floorShader):
+        (raytraceScene):
+        (arrayToCanvasCommands):
+        * js/regress/script-tests/v8-raytrace-with-empty-try-catch.js: Added.
+        (Class.create):
+        (Object.extend):
+        (Flog.RayTracer.Color.prototype.initialize):
+        (Flog.RayTracer.Color.prototype.add):
+        (Flog.RayTracer.Color.prototype.addScalar):
+        (Flog.RayTracer.Color.prototype.subtract):
+        (Flog.RayTracer.Color.prototype.multiply):
+        (Flog.RayTracer.Color.prototype.multiplyScalar):
+        (Flog.RayTracer.Color.prototype.divideFactor):
+        (Flog.RayTracer.Color.prototype.limit):
+        (Flog.RayTracer.Color.prototype.distance):
+        (Flog.RayTracer.Color.prototype.blend):
+        (Flog.RayTracer.Color.prototype.brightness):
+        (Flog.RayTracer.Color.prototype.toString):
+        (Flog.RayTracer.Light.prototype.initialize):
+        (Flog.RayTracer.Light.prototype.toString):
+        (Flog.RayTracer.Vector.prototype.initialize):
+        (Flog.RayTracer.Vector.prototype.copy):
+        (Flog.RayTracer.Vector.prototype.normalize):
+        (Flog.RayTracer.Vector.prototype.magnitude):
+        (Flog.RayTracer.Vector.prototype.cross):
+        (Flog.RayTracer.Vector.prototype.dot):
+        (Flog.RayTracer.Vector.prototype.add):
+        (Flog.RayTracer.Vector.prototype.subtract):
+        (Flog.RayTracer.Vector.prototype.multiplyVector):
+        (Flog.RayTracer.Vector.prototype.multiplyScalar):
+        (Flog.RayTracer.Vector.prototype.toString):
+        (Flog.RayTracer.Ray.prototype.initialize):
+        (Flog.RayTracer.Ray.prototype.toString):
+        (Flog.RayTracer.Scene.prototype.initialize):
+        (Flog.RayTracer.Material.BaseMaterial.prototype.initialize):
+        (Flog.RayTracer.Material.BaseMaterial.prototype.getColor):
+        (Flog.RayTracer.Material.BaseMaterial.prototype.wrapUp):
+        (Flog.RayTracer.Material.BaseMaterial.prototype.toString):
+        (Flog.RayTracer.Material.Solid.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.initialize):
+        (Flog.RayTracer.Material.Solid.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.getColor):
+        (Flog.RayTracer.Material.Solid.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.toString):
+        (Flog.RayTracer.Material.Solid.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial):
+        (Flog.RayTracer.Material.Chessboard.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.initialize):
+        (Flog.RayTracer.Material.Chessboard.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.getColor):
+        (Flog.RayTracer.Material.Chessboard.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.toString):
+        (Flog.RayTracer.Material.Chessboard.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial):
+        (Flog.RayTracer.Shape.Sphere.prototype.initialize):
+        (Flog.RayTracer.Shape.Sphere.prototype.intersect):
+        (Flog.RayTracer.Shape.Sphere.prototype.toString):
+        (Flog.RayTracer.Shape.Plane.prototype.initialize):
+        (Flog.RayTracer.Shape.Plane.prototype.intersect):
+        (Flog.RayTracer.Shape.Plane.prototype.toString):
+        (Flog.RayTracer.IntersectionInfo.prototype.initialize):
+        (Flog.RayTracer.IntersectionInfo.prototype.toString):
+        (Flog.RayTracer.Camera.prototype.initialize):
+        (Flog.RayTracer.Camera.prototype.getRay):
+        (Flog.RayTracer.Camera.prototype.toString):
+        (Flog.RayTracer.Background.prototype.initialize):
+        (Flog.RayTracer.Engine.prototype.initialize):
+        (Flog.RayTracer.Engine.prototype.setPixel):
+        (Flog.RayTracer.Engine.prototype.renderScene):
+        (Flog.RayTracer.Engine.prototype.getPixelColor):
+        (Flog.RayTracer.Engine.prototype.testIntersection):
+        (Flog.RayTracer.Engine.prototype.getReflectionRay):
+        (Flog.RayTracer.Engine.prototype.rayTrace):
+        (renderScene):
+        * js/regress/script-tests/v8-raytrace-with-try-catch.js: Added.
+        (randomException):
+        (Class.create):
+        (Object.extend):
+        (Flog.RayTracer.Color.prototype.initialize):
+        (Flog.RayTracer.Color.prototype.add):
+        (Flog.RayTracer.Color.prototype.addScalar):
+        (Flog.RayTracer.Color.prototype.subtract):
+        (Flog.RayTracer.Color.prototype.multiply):
+        (Flog.RayTracer.Color.prototype.multiplyScalar):
+        (Flog.RayTracer.Color.prototype.divideFactor):
+        (Flog.RayTracer.Color.prototype.limit):
+        (Flog.RayTracer.Color.prototype.distance):
+        (Flog.RayTracer.Color.prototype.blend):
+        (Flog.RayTracer.Color.prototype.brightness):
+        (Flog.RayTracer.Color.prototype.toString):
+        (Flog.RayTracer.Light.prototype.initialize):
+        (Flog.RayTracer.Light.prototype.toString):
+        (Flog.RayTracer.Vector.prototype.initialize):
+        (Flog.RayTracer.Vector.prototype.copy):
+        (Flog.RayTracer.Vector.prototype.normalize):
+        (Flog.RayTracer.Vector.prototype.magnitude):
+        (Flog.RayTracer.Vector.prototype.cross):
+        (Flog.RayTracer.Vector.prototype.dot):
+        (Flog.RayTracer.Vector.prototype.add):
+        (Flog.RayTracer.Vector.prototype.subtract):
+        (Flog.RayTracer.Vector.prototype.multiplyVector):
+        (Flog.RayTracer.Vector.prototype.multiplyScalar):
+        (Flog.RayTracer.Vector.prototype.toString):
+        (Flog.RayTracer.Ray.prototype.initialize):
+        (Flog.RayTracer.Ray.prototype.toString):
+        (Flog.RayTracer.Scene.prototype.initialize):
+        (Flog.RayTracer.Material.BaseMaterial.prototype.initialize):
+        (Flog.RayTracer.Material.BaseMaterial.prototype.getColor):
+        (Flog.RayTracer.Material.BaseMaterial.prototype.wrapUp):
+        (Flog.RayTracer.Material.BaseMaterial.prototype.toString):
+        (Flog.RayTracer.Material.Solid.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.initialize):
+        (Flog.RayTracer.Material.Solid.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.getColor):
+        (Flog.RayTracer.Material.Solid.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.toString):
+        (Flog.RayTracer.Material.Solid.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial):
+        (Flog.RayTracer.Material.Chessboard.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.initialize):
+        (Flog.RayTracer.Material.Chessboard.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.getColor):
+        (Flog.RayTracer.Material.Chessboard.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial.toString):
+        (Flog.RayTracer.Material.Chessboard.prototype.Object.extend.new.Flog.RayTracer.Material.BaseMaterial):
+        (Flog.RayTracer.Shape.Sphere.prototype.initialize):
+        (Flog.RayTracer.Shape.Sphere.prototype.intersect):
+        (Flog.RayTracer.Shape.Sphere.prototype.toString):
+        (Flog.RayTracer.Shape.Plane.prototype.initialize):
+        (Flog.RayTracer.Shape.Plane.prototype.intersect):
+        (Flog.RayTracer.Shape.Plane.prototype.toString):
+        (Flog.RayTracer.IntersectionInfo.prototype.initialize):
+        (Flog.RayTracer.IntersectionInfo.prototype.toString):
+        (Flog.RayTracer.Camera.prototype.initialize):
+        (Flog.RayTracer.Camera.prototype.getRay):
+        (Flog.RayTracer.Camera.prototype.toString):
+        (Flog.RayTracer.Background.prototype.initialize):
+        (Flog.RayTracer.Engine.prototype.initialize):
+        (Flog.RayTracer.Engine.prototype.setPixel):
+        (Flog.RayTracer.Engine.prototype.renderScene):
+        (Flog.RayTracer.Engine.prototype.getPixelColor):
+        (Flog.RayTracer.Engine.prototype.testIntersection):
+        (Flog.RayTracer.Engine.prototype.getReflectionRay):
+        (Flog.RayTracer.Engine.prototype.rayTrace):
+        (renderScene):
+        * js/regress/v8-raytrace-with-empty-try-catch-expected.txt: Added.
+        * js/regress/v8-raytrace-with-empty-try-catch.html: Added.
+        * js/regress/v8-raytrace-with-try-catch-expected.txt: Added.
+        * js/regress/v8-raytrace-with-try-catch.html: Added.
+
 2015-09-18  Nan Wang  <n_wang@apple.com>
 
         AX: Implement ARIA 1.1 @aria-current on iOS
diff --git a/LayoutTests/js/regress/raytrace-with-empty-try-catch-expected.txt b/LayoutTests/js/regress/raytrace-with-empty-try-catch-expected.txt
new file mode 100644 (file)
index 0000000..54eee14
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/raytrace-with-empty-try-catch
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/raytrace-with-empty-try-catch.html b/LayoutTests/js/regress/raytrace-with-empty-try-catch.html
new file mode 100644 (file)
index 0000000..5a3164f
--- /dev/null
@@ -0,0 +1,12 @@
+<!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/raytrace-with-empty-try-catch.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/raytrace-with-try-catch-expected.txt b/LayoutTests/js/regress/raytrace-with-try-catch-expected.txt
new file mode 100644 (file)
index 0000000..18845ce
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/raytrace-with-try-catch
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/raytrace-with-try-catch.html b/LayoutTests/js/regress/raytrace-with-try-catch.html
new file mode 100644 (file)
index 0000000..babff48
--- /dev/null
@@ -0,0 +1,12 @@
+<!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/raytrace-with-try-catch.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/script-tests/raytrace-with-empty-try-catch.js b/LayoutTests/js/regress/script-tests/raytrace-with-empty-try-catch.js
new file mode 100644 (file)
index 0000000..81fad17
--- /dev/null
@@ -0,0 +1,528 @@
+/*
+ * Copyright (C) 2007 Apple Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+
+function createVector(x,y,z) {
+    try {
+        return new Array(x,y,z);
+    } catch(e) { }
+}
+
+function sqrLengthVector(self) {
+    try {
+        return self[0] * self[0] + self[1] * self[1] + self[2] * self[2];
+    } catch(e) { }
+}
+
+function lengthVector(self) {
+    try {
+        return Math.sqrt(self[0] * self[0] + self[1] * self[1] + self[2] * self[2]);
+    } catch(e) { }
+}
+
+function addVector(self, v) {
+    try {
+        self[0] += v[0];
+        self[1] += v[1];
+    } catch(e) { }
+
+    self[2] += v[2];
+    return self;
+}
+
+function subVector(self, v) {
+    try {
+        self[0] -= v[0];
+        self[1] -= v[1];
+    } catch(e) { 
+    }
+    self[2] -= v[2];
+    return self;
+}
+
+function scaleVector(self, scale) {
+    try {
+        self[0] *= scale;
+        self[1] *= scale;
+    } catch(e) { }
+    self[2] *= scale;
+    return self;
+}
+
+function normaliseVector(self) {
+    var len = Math.sqrt(self[0] * self[0] + self[1] * self[1] + self[2] * self[2]);
+    try {
+        self[0] /= len;
+        self[1] /= len;
+    } catch(e) { }
+    self[2] /= len;
+    return self;
+}
+
+function add(v1, v2) {
+    try {
+        return new Array(v1[0] + v2[0], v1[1] + v2[1], v1[2] + v2[2]);
+    } catch(e) { }
+}
+
+function sub(v1, v2) {
+    try {
+        return new Array(v1[0] - v2[0], v1[1] - v2[1], v1[2] - v2[2]);
+    } catch(e) { }
+}
+
+function scalev(v1, v2) {
+    try {
+        return new Array(v1[0] * v2[0], v1[1] * v2[1], v1[2] * v2[2]);
+    } catch(e) { }
+}
+
+function dot(v1, v2) {
+    try {
+        return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
+    } catch(e) { }
+}
+
+function scale(v, scale) {
+    try {
+        return [v[0] * scale, v[1] * scale, v[2] * scale];
+    } catch(e) { }
+}
+
+function cross(v1, v2) {
+    try {
+        return [v1[1] * v2[2] - v1[2] * v2[1], 
+                v1[2] * v2[0] - v1[0] * v2[2],
+                v1[0] * v2[1] - v1[1] * v2[0]];
+    } catch(e) { }
+
+}
+
+function normalise(v) {
+    var len = lengthVector(v);
+    try {
+    } catch(e) { }
+    return [v[0] / len, v[1] / len, v[2] / len];
+}
+
+function transformMatrix(self, v) {
+    try {
+        var vals = self;
+        var x  = vals[0] * v[0] + vals[1] * v[1] + vals[2] * v[2] + vals[3];
+        var y  = vals[4] * v[0] + vals[5] * v[1] + vals[6] * v[2] + vals[7];
+    } catch(e) { }
+    var z  = vals[8] * v[0] + vals[9] * v[1] + vals[10] * v[2] + vals[11];
+    return [x, y, z];
+}
+
+function invertMatrix(self) {
+    try {
+        var temp = new Array(16);
+        var tx = -self[3];
+        var ty = -self[7];
+        var tz = -self[11];
+        for (h = 0; h < 3; h++) 
+            for (v = 0; v < 3; v++) 
+                temp[h + v * 4] = self[v + h * 4];
+    } catch(e) { }
+    try {
+        for (i = 0; i < 11; i++)
+            self[i] = temp[i];
+        self[3] = tx * self[0] + ty * self[1] + tz * self[2];
+        self[7] = tx * self[4] + ty * self[5] + tz * self[6];
+        self[11] = tx * self[8] + ty * self[9] + tz * self[10];
+    } catch(e) { }
+    return self;
+}
+
+
+// Triangle intersection using barycentric coord method
+function Triangle(p1, p2, p3) {
+    try {
+        var edge1 = sub(p3, p1);
+        var edge2 = sub(p2, p1);
+        var normal = cross(edge1, edge2);
+        if (Math.abs(normal[0]) > Math.abs(normal[1]))
+            if (Math.abs(normal[0]) > Math.abs(normal[2]))
+                this.axis = 0; 
+            else 
+                this.axis = 2;
+        else
+            if (Math.abs(normal[1]) > Math.abs(normal[2])) 
+                this.axis = 1;
+            else 
+                this.axis = 2;
+        var u = (this.axis + 1) % 3;
+        var v = (this.axis + 2) % 3;
+        var u1 = edge1[u];
+        var v1 = edge1[v];
+        
+        var u2 = edge2[u];
+        var v2 = edge2[v];
+        this.normal = normalise(normal);
+        this.nu = normal[u] / normal[this.axis];
+        this.nv = normal[v] / normal[this.axis];
+        this.nd = dot(normal, p1) / normal[this.axis];
+        var det = u1 * v2 - v1 * u2;
+        this.eu = p1[u];
+        this.ev = p1[v]; 
+        this.nu1 = u1 / det;
+        this.nv1 = -v1 / det;
+        this.nu2 = v2 / det;
+        this.nv2 = -u2 / det; 
+        this.material = [0.7, 0.7, 0.7];
+    } catch(e) { }
+}
+
+Triangle.prototype.intersect = function(orig, dir, near, far) {
+    try {
+        var u = (this.axis + 1) % 3;
+        var v = (this.axis + 2) % 3;
+        var d = dir[this.axis] + this.nu * dir[u] + this.nv * dir[v];
+        var t = (this.nd - orig[this.axis] - this.nu * orig[u] - this.nv * orig[v]) / d;
+    } catch(e) { }
+    if (t < near || t > far)
+        return null;
+    try {
+        var Pu = orig[u] + t * dir[u] - this.eu;
+        var Pv = orig[v] + t * dir[v] - this.ev;
+        var a2 = Pv * this.nu1 + Pu * this.nv1;
+    } catch(e) { }
+    if (a2 < 0) 
+        return null;
+    try {
+        var a3 = Pu * this.nu2 + Pv * this.nv2;
+    } catch(e) { }
+    if (a3 < 0) 
+        return null;
+
+    if ((a2 + a3) > 1) 
+        return null;
+    return t;
+}
+
+function Scene(a_triangles) {
+    try {
+        this.triangles = a_triangles;
+        this.lights = [];
+        this.ambient = [0,0,0];
+        this.background = [0.8,0.8,1];
+    } catch(e) { }
+}
+var zero = new Array(0,0,0);
+
+Scene.prototype.intersect = function(origin, dir, near, far) {
+    var closest = null;
+    for (i = 0; i < this.triangles.length; i++) {
+        try {
+            var triangle = this.triangles[i];   
+            var d = triangle.intersect(origin, dir, near, far);
+            if (d == null || d > far || d < near)
+                continue;
+            far = d;
+            closest = triangle;
+        } catch(e) { }
+    }
+    
+    if (!closest)
+        return [this.background[0],this.background[1],this.background[2]];
+        
+    try {
+        var normal = closest.normal;
+        var hit = add(origin, scale(dir, far)); 
+        if (dot(dir, normal) > 0)
+            normal = [-normal[0], -normal[1], -normal[2]];
+        
+        var colour = null;
+        if (closest.shader) {
+            colour = closest.shader(closest, hit, dir);
+        } else {
+            colour = closest.material;
+        }
+    } catch(e) { }
+    
+    // do reflection
+    var reflected = null;
+    if (colour.reflection > 0.001) {
+        try {
+            var reflection = addVector(scale(normal, -2*dot(dir, normal)), dir);
+            reflected = this.intersect(hit, reflection, 0.0001, 1000000);
+        } catch(e) { }
+        if (colour.reflection >= 0.999999)
+            return reflected;
+    }
+    
+    var l = [this.ambient[0], this.ambient[1], this.ambient[2]];
+    for (var i = 0; i < this.lights.length; i++) {
+        try {
+            var light = this.lights[i];
+            var toLight = sub(light, hit);
+            var distance = lengthVector(toLight);
+            scaleVector(toLight, 1.0/distance);
+            distance -= 0.0001;
+            if (this.blocked(hit, toLight, distance))
+                continue;
+            var nl = dot(normal, toLight);
+            if (nl > 0)
+                addVector(l, scale(light.colour, nl));
+        } catch(e) { }
+    }
+    try {
+        l = scalev(l, colour);
+        if (reflected) {
+            l = addVector(scaleVector(l, 1 - colour.reflection), scaleVector(reflected, colour.reflection));
+        }
+    } catch(e) { }
+    return l;
+}
+
+Scene.prototype.blocked = function(O, D, far) {
+    var near = 0.0001;
+    var closest = null;
+    for (i = 0; i < this.triangles.length; i++) {
+        try {
+            var triangle = this.triangles[i];   
+            var d = triangle.intersect(O, D, near, far);
+        } catch(e) { }
+        if (d == null || d > far || d < near)
+            continue;
+        return true;
+    }
+    
+    return false;
+}
+
+
+// this camera code is from notes i made ages ago, it is from *somewhere* -- i cannot remember where
+// that somewhere is
+function Camera(origin, lookat, up) {
+    try {
+        var zaxis = normaliseVector(subVector(lookat, origin));
+        var xaxis = normaliseVector(cross(up, zaxis));
+        var yaxis = normaliseVector(cross(xaxis, subVector([0,0,0], zaxis)));
+        var m = new Array(16);
+        m[0] = xaxis[0]; m[1] = xaxis[1]; m[2] = xaxis[2];
+        m[4] = yaxis[0]; m[5] = yaxis[1]; m[6] = yaxis[2];
+        m[8] = zaxis[0]; m[9] = zaxis[1]; m[10] = zaxis[2];
+        invertMatrix(m);
+        m[3] = 0; m[7] = 0; m[11] = 0;
+        this.origin = origin;
+        this.directions = new Array(4);
+        this.directions[0] = normalise([-0.7,  0.7, 1]);
+        this.directions[1] = normalise([ 0.7,  0.7, 1]);
+        this.directions[2] = normalise([ 0.7, -0.7, 1]);
+        this.directions[3] = normalise([-0.7, -0.7, 1]);
+        this.directions[0] = transformMatrix(m, this.directions[0]);
+        this.directions[1] = transformMatrix(m, this.directions[1]);
+        this.directions[2] = transformMatrix(m, this.directions[2]);
+        this.directions[3] = transformMatrix(m, this.directions[3]);
+
+    } catch(e) { }
+}
+
+Camera.prototype.generateRayPair = function(y) {
+    try {
+        rays = new Array(new Object(), new Object());
+        rays[0].origin = this.origin;
+        rays[1].origin = this.origin;
+        rays[0].dir = addVector(scale(this.directions[0], y), scale(this.directions[3], 1 - y));
+        rays[1].dir = addVector(scale(this.directions[1], y), scale(this.directions[2], 1 - y));
+    } catch(e) { }
+    return rays;
+}
+
+function renderRows(camera, scene, pixels, width, height, starty, stopy) {
+    for (var y = starty; y < stopy; y++) {
+        var rays = camera.generateRayPair(y / height);
+        for (var x = 0; x < width; x++) {
+            try {
+                var xp = x / width;
+                var origin = addVector(scale(rays[0].origin, xp), scale(rays[1].origin, 1 - xp));
+                var dir = normaliseVector(addVector(scale(rays[0].dir, xp), scale(rays[1].dir, 1 - xp)));
+                var l = scene.intersect(origin, dir);
+                pixels[y][x] = l;
+            } catch(e) { }
+        }
+    }
+}
+
+Camera.prototype.render = function(scene, pixels, width, height) {
+    try {
+        var cam = this;
+        var row = 0;
+        renderRows(cam, scene, pixels, width, height, 0, height);
+    } catch(e) { }
+}
+
+
+
+function raytraceScene()
+{
+    try {
+        var startDate = new Date().getTime();
+        var numTriangles = 2 * 6;
+        var triangles = new Array();//numTriangles);
+        var tfl = createVector(-10,  10, -10);
+        var tfr = createVector( 10,  10, -10);
+        var tbl = createVector(-10,  10,  10);
+        var tbr = createVector( 10,  10,  10);
+        var bfl = createVector(-10, -10, -10);
+        var bfr = createVector( 10, -10, -10);
+        var bbl = createVector(-10, -10,  10);
+        var bbr = createVector( 10, -10,  10);
+        
+        // cube!!!
+        // front
+        var i = 0;
+        
+        triangles[i++] = new Triangle(tfl, tfr, bfr);
+        triangles[i++] = new Triangle(tfl, bfr, bfl);
+        // back
+        triangles[i++] = new Triangle(tbl, tbr, bbr);
+        triangles[i++] = new Triangle(tbl, bbr, bbl);
+        //        triangles[i-1].material = [0.7,0.2,0.2];
+        //            triangles[i-1].material.reflection = 0.8;
+        // left
+        triangles[i++] = new Triangle(tbl, tfl, bbl);
+        //            triangles[i-1].reflection = 0.6;
+        triangles[i++] = new Triangle(tfl, bfl, bbl);
+        //            triangles[i-1].reflection = 0.6;
+        // right
+        triangles[i++] = new Triangle(tbr, tfr, bbr);
+        triangles[i++] = new Triangle(tfr, bfr, bbr);
+        // top
+        triangles[i++] = new Triangle(tbl, tbr, tfr);
+        triangles[i++] = new Triangle(tbl, tfr, tfl);
+        // bottom
+        triangles[i++] = new Triangle(bbl, bbr, bfr);
+        triangles[i++] = new Triangle(bbl, bfr, bfl);
+        
+        //Floor!!!!
+        var green = createVector(0.0, 0.4, 0.0);
+        var grey = createVector(0.4, 0.4, 0.4);
+        grey.reflection = 1.0;
+
+    } catch(e) { }
+    var floorShader = function(tri, pos, view) {
+        try {
+            var x = ((pos[0]/32) % 2 + 2) % 2;
+            var z = ((pos[2]/32 + 0.3) % 2 + 2) % 2;
+        } catch(e) { }
+        try {
+            if (x < 1 != z < 1) {
+                //in the real world we use the fresnel term...
+                //    var angle = 1-dot(view, tri.normal);
+                //   angle *= angle;
+                //  angle *= angle;
+                // angle *= angle;
+                //grey.reflection = angle;
+                return grey;
+            } else 
+                return green;
+        } catch(e) { }
+    }
+    var ffl = createVector(-1000, -30, -1000);
+    var ffr = createVector( 1000, -30, -1000);
+    var fbl = createVector(-1000, -30,  1000);
+    var fbr = createVector( 1000, -30,  1000);
+    triangles[i++] = new Triangle(fbl, fbr, ffr);
+    triangles[i-1].shader = floorShader;
+    triangles[i++] = new Triangle(fbl, ffr, ffl);
+    triangles[i-1].shader = floorShader;
+    
+    var _scene = new Scene(triangles);
+    _scene.lights[0] = createVector(20, 38, -22);
+    _scene.lights[0].colour = createVector(0.7, 0.3, 0.3);
+    _scene.lights[1] = createVector(-23, 40, 17);
+    _scene.lights[1].colour = createVector(0.7, 0.3, 0.3);
+    _scene.lights[2] = createVector(23, 20, 17);
+    _scene.lights[2].colour = createVector(0.7, 0.7, 0.7);
+    _scene.ambient = createVector(0.1, 0.1, 0.1);
+    //  _scene.background = createVector(0.7, 0.7, 1.0);
+    
+    var size = 30;
+    var pixels = new Array();
+    for (var y = 0; y < size; y++) {
+        try {
+            pixels[y] = new Array();
+        } catch(e) { }
+        for (var x = 0; x < size; x++) {
+            try {
+                pixels[y][x] = 0;
+            } catch(e) { }
+        }
+    }
+
+    try {
+        var _camera = new Camera(createVector(-40, 40, 40), createVector(0, 0, 0), createVector(0, 1, 0));
+        _camera.render(_scene, pixels, size, size);
+    } catch(e){}
+
+    return pixels;
+}
+
+function arrayToCanvasCommands(pixels)
+{
+    var s = '<canvas id="renderCanvas" width="30px" height="30px"></canvas><scr' + 'ipt>\nvar pixels = [';
+    var size = 30;
+    for (var y = 0; y < size; y++) {
+        s += "[";
+        for (var x = 0; x < size; x++) {
+            s += "[" + pixels[y][x] + "],";
+        }
+        s+= "],";
+    }
+    s += '];\n    var canvas = document.getElementById("renderCanvas").getContext("2d");\n\
+\n\
+\n\
+    var size = 30;\n\
+    canvas.fillStyle = "red";\n\
+    canvas.fillRect(0, 0, size, size);\n\
+    canvas.scale(1, -1);\n\
+    canvas.translate(0, -size);\n\
+\n\
+    if (!canvas.setFillColor)\n\
+        canvas.setFillColor = function(r, g, b, a) {\n\
+            this.fillStyle = "rgb("+[Math.floor(r * 255), Math.floor(g * 255), Math.floor(b * 255)]+")";\n\
+    }\n\
+\n\
+for (var y = 0; y < size; y++) {\n\
+  for (var x = 0; x < size; x++) {\n\
+    var l = pixels[y][x];\n\
+    canvas.setFillColor(l[0], l[1], l[2], 1);\n\
+    canvas.fillRect(x, y, 1, 1);\n\
+  }\n\
+}</scr' + 'ipt>';
+
+    return s;
+}
+
+testOutput = arrayToCanvasCommands(raytraceScene());
+
+var expectedLength = 20970;
+
+if (testOutput.length != expectedLength)
+    throw "Error: bad result: expected length " + expectedLength + " but got " + testOutput.length;
+
diff --git a/LayoutTests/js/regress/script-tests/raytrace-with-try-catch.js b/LayoutTests/js/regress/script-tests/raytrace-with-try-catch.js
new file mode 100644 (file)
index 0000000..5768ebb
--- /dev/null
@@ -0,0 +1,565 @@
+/*
+ * Copyright (C) 2007 Apple Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+let __exceptionCounter = 0;
+function randomException() {
+    __exceptionCounter++;
+    if (__exceptionCounter % 500 === 0) {
+        throw new Error("rando");
+    }
+}
+noInline(randomException);
+
+
+function createVector(x,y,z) {
+    try {
+        return new Array(x,y,z);
+    } catch(e) { }
+}
+
+function sqrLengthVector(self) {
+    try {
+        return self[0] * self[0] + self[1] * self[1] + self[2] * self[2];
+    } catch(e) { }
+}
+
+function lengthVector(self) {
+    try {
+        return Math.sqrt(self[0] * self[0] + self[1] * self[1] + self[2] * self[2]);
+    } catch(e) { }
+}
+
+function addVector(self, v) {
+    try {
+        self[0] += v[0];
+        self[1] += v[1];
+        randomException();
+    } catch(e) { }
+
+    self[2] += v[2];
+    return self;
+}
+
+function subVector(self, v) {
+    try {
+        self[0] -= v[0];
+        self[1] -= v[1];
+        randomException();
+    } catch(e) { 
+    }
+    self[2] -= v[2];
+    return self;
+}
+
+function scaleVector(self, scale) {
+    try {
+        self[0] *= scale;
+        self[1] *= scale;
+        randomException();
+    } catch(e) { }
+    self[2] *= scale;
+    return self;
+}
+
+function normaliseVector(self) {
+    var len = Math.sqrt(self[0] * self[0] + self[1] * self[1] + self[2] * self[2]);
+    try {
+        self[0] /= len;
+        self[1] /= len;
+        randomException();
+    } catch(e) { }
+    self[2] /= len;
+    return self;
+}
+
+function add(v1, v2) {
+    try {
+        return new Array(v1[0] + v2[0], v1[1] + v2[1], v1[2] + v2[2]);
+    } catch(e) { }
+}
+
+function sub(v1, v2) {
+    try {
+        return new Array(v1[0] - v2[0], v1[1] - v2[1], v1[2] - v2[2]);
+    } catch(e) { }
+}
+
+function scalev(v1, v2) {
+    try {
+        return new Array(v1[0] * v2[0], v1[1] * v2[1], v1[2] * v2[2]);
+    } catch(e) { }
+}
+
+function dot(v1, v2) {
+    try {
+        return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
+    } catch(e) { }
+}
+
+function scale(v, scale) {
+    try {
+        return [v[0] * scale, v[1] * scale, v[2] * scale];
+    } catch(e) { }
+}
+
+function cross(v1, v2) {
+    try {
+        return [v1[1] * v2[2] - v1[2] * v2[1], 
+                v1[2] * v2[0] - v1[0] * v2[2],
+                v1[0] * v2[1] - v1[1] * v2[0]];
+    } catch(e) { }
+
+}
+
+function normalise(v) {
+    var len = lengthVector(v);
+    try {
+        randomException();
+    } catch(e) { }
+    return [v[0] / len, v[1] / len, v[2] / len];
+}
+
+function transformMatrix(self, v) {
+    try {
+        var vals = self;
+        var x  = vals[0] * v[0] + vals[1] * v[1] + vals[2] * v[2] + vals[3];
+        var y  = vals[4] * v[0] + vals[5] * v[1] + vals[6] * v[2] + vals[7];
+        randomException();
+    } catch(e) { }
+    var z  = vals[8] * v[0] + vals[9] * v[1] + vals[10] * v[2] + vals[11];
+    return [x, y, z];
+}
+
+function invertMatrix(self) {
+    try {
+        var temp = new Array(16);
+        var tx = -self[3];
+        var ty = -self[7];
+        var tz = -self[11];
+        for (h = 0; h < 3; h++) 
+            for (v = 0; v < 3; v++) 
+                temp[h + v * 4] = self[v + h * 4];
+        randomException();
+    } catch(e) { }
+    try {
+        for (i = 0; i < 11; i++)
+            self[i] = temp[i];
+        self[3] = tx * self[0] + ty * self[1] + tz * self[2];
+        self[7] = tx * self[4] + ty * self[5] + tz * self[6];
+        self[11] = tx * self[8] + ty * self[9] + tz * self[10];
+        randomException();
+    } catch(e) { }
+    return self;
+}
+
+
+// Triangle intersection using barycentric coord method
+function Triangle(p1, p2, p3) {
+    try {
+        var edge1 = sub(p3, p1);
+        var edge2 = sub(p2, p1);
+        var normal = cross(edge1, edge2);
+        if (Math.abs(normal[0]) > Math.abs(normal[1]))
+            if (Math.abs(normal[0]) > Math.abs(normal[2]))
+                this.axis = 0; 
+            else 
+                this.axis = 2;
+        else
+            if (Math.abs(normal[1]) > Math.abs(normal[2])) 
+                this.axis = 1;
+            else 
+                this.axis = 2;
+        var u = (this.axis + 1) % 3;
+        var v = (this.axis + 2) % 3;
+        var u1 = edge1[u];
+        var v1 = edge1[v];
+        
+        var u2 = edge2[u];
+        var v2 = edge2[v];
+        this.normal = normalise(normal);
+        this.nu = normal[u] / normal[this.axis];
+        this.nv = normal[v] / normal[this.axis];
+        this.nd = dot(normal, p1) / normal[this.axis];
+        var det = u1 * v2 - v1 * u2;
+        this.eu = p1[u];
+        this.ev = p1[v]; 
+        this.nu1 = u1 / det;
+        this.nv1 = -v1 / det;
+        this.nu2 = v2 / det;
+        this.nv2 = -u2 / det; 
+        this.material = [0.7, 0.7, 0.7];
+        randomException();
+    } catch(e) { }
+}
+
+Triangle.prototype.intersect = function(orig, dir, near, far) {
+    try {
+        var u = (this.axis + 1) % 3;
+        var v = (this.axis + 2) % 3;
+        var d = dir[this.axis] + this.nu * dir[u] + this.nv * dir[v];
+        var t = (this.nd - orig[this.axis] - this.nu * orig[u] - this.nv * orig[v]) / d;
+        randomException();
+    } catch(e) { }
+    if (t < near || t > far)
+        return null;
+    try {
+        var Pu = orig[u] + t * dir[u] - this.eu;
+        var Pv = orig[v] + t * dir[v] - this.ev;
+        var a2 = Pv * this.nu1 + Pu * this.nv1;
+        randomException();
+    } catch(e) { }
+    if (a2 < 0) 
+        return null;
+    try {
+        var a3 = Pu * this.nu2 + Pv * this.nv2;
+        randomException();
+    } catch(e) { }
+    if (a3 < 0) 
+        return null;
+
+    if ((a2 + a3) > 1) 
+        return null;
+    return t;
+}
+
+function Scene(a_triangles) {
+    try {
+        this.triangles = a_triangles;
+        this.lights = [];
+        this.ambient = [0,0,0];
+        this.background = [0.8,0.8,1];
+        randomException();
+    } catch(e) { }
+}
+var zero = new Array(0,0,0);
+
+Scene.prototype.intersect = function(origin, dir, near, far) {
+    var closest = null;
+    for (i = 0; i < this.triangles.length; i++) {
+        try {
+            var triangle = this.triangles[i];   
+            var d = triangle.intersect(origin, dir, near, far);
+            if (d == null || d > far || d < near)
+                continue;
+            far = d;
+            closest = triangle;
+            randomException();
+        } catch(e) { }
+    }
+    
+    if (!closest)
+        return [this.background[0],this.background[1],this.background[2]];
+        
+    try {
+        var normal = closest.normal;
+        var hit = add(origin, scale(dir, far)); 
+        if (dot(dir, normal) > 0)
+            normal = [-normal[0], -normal[1], -normal[2]];
+        
+        var colour = null;
+        if (closest.shader) {
+            colour = closest.shader(closest, hit, dir);
+        } else {
+            colour = closest.material;
+        }
+        randomException();
+    } catch(e) { }
+    
+    // do reflection
+    var reflected = null;
+    if (colour.reflection > 0.001) {
+        try {
+            var reflection = addVector(scale(normal, -2*dot(dir, normal)), dir);
+            reflected = this.intersect(hit, reflection, 0.0001, 1000000);
+            randomException();
+        } catch(e) { }
+        if (colour.reflection >= 0.999999)
+            return reflected;
+    }
+    
+    var l = [this.ambient[0], this.ambient[1], this.ambient[2]];
+    for (var i = 0; i < this.lights.length; i++) {
+        try {
+            var light = this.lights[i];
+            var toLight = sub(light, hit);
+            var distance = lengthVector(toLight);
+            scaleVector(toLight, 1.0/distance);
+            distance -= 0.0001;
+            if (this.blocked(hit, toLight, distance))
+                continue;
+            var nl = dot(normal, toLight);
+            if (nl > 0)
+                addVector(l, scale(light.colour, nl));
+            randomException();
+        } catch(e) { }
+    }
+    try {
+        l = scalev(l, colour);
+        if (reflected) {
+            l = addVector(scaleVector(l, 1 - colour.reflection), scaleVector(reflected, colour.reflection));
+        }
+        randomException();
+    } catch(e) { }
+    return l;
+}
+
+Scene.prototype.blocked = function(O, D, far) {
+    var near = 0.0001;
+    var closest = null;
+    for (i = 0; i < this.triangles.length; i++) {
+        try {
+            var triangle = this.triangles[i];   
+            var d = triangle.intersect(O, D, near, far);
+            randomException();
+        } catch(e) { }
+        if (d == null || d > far || d < near)
+            continue;
+        return true;
+    }
+    
+    return false;
+}
+
+
+// this camera code is from notes i made ages ago, it is from *somewhere* -- i cannot remember where
+// that somewhere is
+function Camera(origin, lookat, up) {
+    try {
+        var zaxis = normaliseVector(subVector(lookat, origin));
+        var xaxis = normaliseVector(cross(up, zaxis));
+        var yaxis = normaliseVector(cross(xaxis, subVector([0,0,0], zaxis)));
+        var m = new Array(16);
+        m[0] = xaxis[0]; m[1] = xaxis[1]; m[2] = xaxis[2];
+        m[4] = yaxis[0]; m[5] = yaxis[1]; m[6] = yaxis[2];
+        m[8] = zaxis[0]; m[9] = zaxis[1]; m[10] = zaxis[2];
+        invertMatrix(m);
+        m[3] = 0; m[7] = 0; m[11] = 0;
+        this.origin = origin;
+        this.directions = new Array(4);
+        this.directions[0] = normalise([-0.7,  0.7, 1]);
+        this.directions[1] = normalise([ 0.7,  0.7, 1]);
+        this.directions[2] = normalise([ 0.7, -0.7, 1]);
+        this.directions[3] = normalise([-0.7, -0.7, 1]);
+        this.directions[0] = transformMatrix(m, this.directions[0]);
+        this.directions[1] = transformMatrix(m, this.directions[1]);
+        this.directions[2] = transformMatrix(m, this.directions[2]);
+        this.directions[3] = transformMatrix(m, this.directions[3]);
+
+        randomException();
+    } catch(e) { }
+}
+
+Camera.prototype.generateRayPair = function(y) {
+    try {
+        rays = new Array(new Object(), new Object());
+        rays[0].origin = this.origin;
+        rays[1].origin = this.origin;
+        rays[0].dir = addVector(scale(this.directions[0], y), scale(this.directions[3], 1 - y));
+        rays[1].dir = addVector(scale(this.directions[1], y), scale(this.directions[2], 1 - y));
+        randomException();
+    } catch(e) { }
+    return rays;
+}
+
+function renderRows(camera, scene, pixels, width, height, starty, stopy) {
+    for (var y = starty; y < stopy; y++) {
+        var rays = camera.generateRayPair(y / height);
+        for (var x = 0; x < width; x++) {
+            try {
+                var xp = x / width;
+                var origin = addVector(scale(rays[0].origin, xp), scale(rays[1].origin, 1 - xp));
+                var dir = normaliseVector(addVector(scale(rays[0].dir, xp), scale(rays[1].dir, 1 - xp)));
+                var l = scene.intersect(origin, dir);
+                pixels[y][x] = l;
+                randomException();
+            } catch(e) { }
+        }
+    }
+}
+
+Camera.prototype.render = function(scene, pixels, width, height) {
+    try {
+        var cam = this;
+        var row = 0;
+        renderRows(cam, scene, pixels, width, height, 0, height);
+        randomException();
+    } catch(e) { }
+}
+
+
+
+function raytraceScene()
+{
+    try {
+        var startDate = new Date().getTime();
+        var numTriangles = 2 * 6;
+        var triangles = new Array();//numTriangles);
+        var tfl = createVector(-10,  10, -10);
+        var tfr = createVector( 10,  10, -10);
+        var tbl = createVector(-10,  10,  10);
+        var tbr = createVector( 10,  10,  10);
+        var bfl = createVector(-10, -10, -10);
+        var bfr = createVector( 10, -10, -10);
+        var bbl = createVector(-10, -10,  10);
+        var bbr = createVector( 10, -10,  10);
+        
+        // cube!!!
+        // front
+        var i = 0;
+        
+        triangles[i++] = new Triangle(tfl, tfr, bfr);
+        triangles[i++] = new Triangle(tfl, bfr, bfl);
+        // back
+        triangles[i++] = new Triangle(tbl, tbr, bbr);
+        triangles[i++] = new Triangle(tbl, bbr, bbl);
+        //        triangles[i-1].material = [0.7,0.2,0.2];
+        //            triangles[i-1].material.reflection = 0.8;
+        // left
+        triangles[i++] = new Triangle(tbl, tfl, bbl);
+        //            triangles[i-1].reflection = 0.6;
+        triangles[i++] = new Triangle(tfl, bfl, bbl);
+        //            triangles[i-1].reflection = 0.6;
+        // right
+        triangles[i++] = new Triangle(tbr, tfr, bbr);
+        triangles[i++] = new Triangle(tfr, bfr, bbr);
+        // top
+        triangles[i++] = new Triangle(tbl, tbr, tfr);
+        triangles[i++] = new Triangle(tbl, tfr, tfl);
+        // bottom
+        triangles[i++] = new Triangle(bbl, bbr, bfr);
+        triangles[i++] = new Triangle(bbl, bfr, bfl);
+        
+        //Floor!!!!
+        var green = createVector(0.0, 0.4, 0.0);
+        var grey = createVector(0.4, 0.4, 0.4);
+        grey.reflection = 1.0;
+
+        randomException();
+    } catch(e) { }
+    var floorShader = function(tri, pos, view) {
+        try {
+            var x = ((pos[0]/32) % 2 + 2) % 2;
+            var z = ((pos[2]/32 + 0.3) % 2 + 2) % 2;
+            randomException();
+        } catch(e) { }
+        try {
+            if (x < 1 != z < 1) {
+                //in the real world we use the fresnel term...
+                //    var angle = 1-dot(view, tri.normal);
+                //   angle *= angle;
+                //  angle *= angle;
+                // angle *= angle;
+                //grey.reflection = angle;
+                return grey;
+            } else 
+                return green;
+        } catch(e) { }
+    }
+    var ffl = createVector(-1000, -30, -1000);
+    var ffr = createVector( 1000, -30, -1000);
+    var fbl = createVector(-1000, -30,  1000);
+    var fbr = createVector( 1000, -30,  1000);
+    triangles[i++] = new Triangle(fbl, fbr, ffr);
+    triangles[i-1].shader = floorShader;
+    triangles[i++] = new Triangle(fbl, ffr, ffl);
+    triangles[i-1].shader = floorShader;
+    
+    var _scene = new Scene(triangles);
+    _scene.lights[0] = createVector(20, 38, -22);
+    _scene.lights[0].colour = createVector(0.7, 0.3, 0.3);
+    _scene.lights[1] = createVector(-23, 40, 17);
+    _scene.lights[1].colour = createVector(0.7, 0.3, 0.3);
+    _scene.lights[2] = createVector(23, 20, 17);
+    _scene.lights[2].colour = createVector(0.7, 0.7, 0.7);
+    _scene.ambient = createVector(0.1, 0.1, 0.1);
+    //  _scene.background = createVector(0.7, 0.7, 1.0);
+    
+    var size = 30;
+    var pixels = new Array();
+    for (var y = 0; y < size; y++) {
+        try {
+            pixels[y] = new Array();
+            randomException();
+        } catch(e) { }
+        for (var x = 0; x < size; x++) {
+            try {
+                pixels[y][x] = 0;
+                randomException();
+            } catch(e) { }
+        }
+    }
+
+    try {
+        var _camera = new Camera(createVector(-40, 40, 40), createVector(0, 0, 0), createVector(0, 1, 0));
+        _camera.render(_scene, pixels, size, size);
+        randomException();
+    } catch(e){}
+
+    return pixels;
+}
+
+function arrayToCanvasCommands(pixels)
+{
+    var s = '<canvas id="renderCanvas" width="30px" height="30px"></canvas><scr' + 'ipt>\nvar pixels = [';
+    var size = 30;
+    for (var y = 0; y < size; y++) {
+        s += "[";
+        for (var x = 0; x < size; x++) {
+            s += "[" + pixels[y][x] + "],";
+        }
+        s+= "],";
+    }
+    s += '];\n    var canvas = document.getElementById("renderCanvas").getContext("2d");\n\
+\n\
+\n\
+    var size = 30;\n\
+    canvas.fillStyle = "red";\n\
+    canvas.fillRect(0, 0, size, size);\n\
+    canvas.scale(1, -1);\n\
+    canvas.translate(0, -size);\n\
+\n\
+    if (!canvas.setFillColor)\n\
+        canvas.setFillColor = function(r, g, b, a) {\n\
+            this.fillStyle = "rgb("+[Math.floor(r * 255), Math.floor(g * 255), Math.floor(b * 255)]+")";\n\
+    }\n\
+\n\
+for (var y = 0; y < size; y++) {\n\
+  for (var x = 0; x < size; x++) {\n\
+    var l = pixels[y][x];\n\
+    canvas.setFillColor(l[0], l[1], l[2], 1);\n\
+    canvas.fillRect(x, y, 1, 1);\n\
+  }\n\
+}</scr' + 'ipt>';
+
+    return s;
+}
+
+testOutput = arrayToCanvasCommands(raytraceScene());
+
+var expectedLength = 20970;
+
+if (testOutput.length != expectedLength)
+    throw "Error: bad result: expected length " + expectedLength + " but got " + testOutput.length;
+
diff --git a/LayoutTests/js/regress/script-tests/v8-raytrace-with-empty-try-catch.js b/LayoutTests/js/regress/script-tests/v8-raytrace-with-empty-try-catch.js
new file mode 100644 (file)
index 0000000..54222db
--- /dev/null
@@ -0,0 +1,1048 @@
+// The ray tracer code in this file is written by Adam Burmister. It
+// is available in its original form from:
+//
+//   http://labs.flog.nz.co/raytracer/
+//
+// It has been modified slightly by Google to work as a standalone
+// benchmark, but the all the computational code remains
+// untouched. This file also contains a copy of parts of the Prototype
+// JavaScript framework which is used by the ray tracer.
+
+// Variable used to hold a number that can be used to verify that
+// the scene was ray traced correctly.
+var checkNumber;
+
+
+// ------------------------------------------------------------------------
+// ------------------------------------------------------------------------
+
+// The following is a copy of parts of the Prototype JavaScript library:
+
+// Prototype JavaScript framework, version 1.5.0
+// (c) 2005-2007 Sam Stephenson
+//
+// Prototype is freely distributable under the terms of an MIT-style license.
+// For details, see the Prototype web site: http://prototype.conio.net/
+
+var Class = {
+    create: function() {
+        return function() {
+            try {
+                this.initialize.apply(this, arguments);
+            } catch(e) { }
+        }
+    }
+};
+
+
+Object.extend = function(destination, source) {
+    for (var property in source) {
+        try {
+            destination[property] = source[property];
+        } catch(e) { }
+    }
+    return destination;
+};
+
+
+// ------------------------------------------------------------------------
+// ------------------------------------------------------------------------
+
+// The rest of this file is the actual ray tracer written by Adam
+// Burmister. It's a concatenation of the following files:
+//
+//   flog/color.js
+//   flog/light.js
+//   flog/vector.js
+//   flog/ray.js
+//   flog/scene.js
+//   flog/material/basematerial.js
+//   flog/material/solid.js
+//   flog/material/chessboard.js
+//   flog/shape/baseshape.js
+//   flog/shape/sphere.js
+//   flog/shape/plane.js
+//   flog/intersectioninfo.js
+//   flog/camera.js
+//   flog/background.js
+//   flog/engine.js
+
+
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Color = Class.create();
+
+Flog.RayTracer.Color.prototype = {
+    red : 0.0,
+    green : 0.0,
+    blue : 0.0,
+
+    initialize : function(r, g, b) {
+        try {
+            if(!r) r = 0.0;
+            if(!g) g = 0.0;
+            if(!b) b = 0.0;
+
+            this.red = r;
+            this.green = g;
+            this.blue = b;
+        } catch(e) { }
+    },
+
+    add : function(c1, c2){
+        try {
+            var result = new Flog.RayTracer.Color(0,0,0);
+
+            result.red = c1.red + c2.red;
+            result.green = c1.green + c2.green;
+            result.blue = c1.blue + c2.blue;
+
+        } catch(e) { }
+
+        return result;
+    },
+
+    addScalar: function(c1, s){
+        try {
+            var result = new Flog.RayTracer.Color(0,0,0);
+
+            result.red = c1.red + s;
+            result.green = c1.green + s;
+            result.blue = c1.blue + s;
+
+            result.limit();
+
+        } catch(e) { }
+
+        return result;
+    },
+
+    subtract: function(c1, c2){
+        try {
+            var result = new Flog.RayTracer.Color(0,0,0);
+
+            result.red = c1.red - c2.red;
+            result.green = c1.green - c2.green;
+            result.blue = c1.blue - c2.blue;
+
+        } catch(e) { }
+
+        return result;
+    },
+
+    multiply : function(c1, c2) {
+        try {
+            var result = new Flog.RayTracer.Color(0,0,0);
+
+            result.red = c1.red * c2.red;
+            result.green = c1.green * c2.green;
+            result.blue = c1.blue * c2.blue;
+
+        } catch(e) { }
+
+        return result;
+    },
+
+    multiplyScalar : function(c1, f) {
+        try {
+            var result = new Flog.RayTracer.Color(0,0,0);
+
+            result.red = c1.red * f;
+            result.green = c1.green * f;
+            result.blue = c1.blue * f;
+
+        } catch(e) { }
+
+        return result;
+    },
+
+    divideFactor : function(c1, f) {
+        try {
+            var result = new Flog.RayTracer.Color(0,0,0);
+
+            result.red = c1.red / f;
+            result.green = c1.green / f;
+            result.blue = c1.blue / f;
+
+        } catch(e) { }
+
+        return result;
+    },
+
+    limit: function(){
+        try { 
+            this.red = (this.red > 0.0) ? ( (this.red > 1.0) ? 1.0 : this.red ) : 0.0;
+            this.green = (this.green > 0.0) ? ( (this.green > 1.0) ? 1.0 : this.green ) : 0.0;
+            this.blue = (this.blue > 0.0) ? ( (this.blue > 1.0) ? 1.0 : this.blue ) : 0.0;
+
+        } catch(e) { }
+    },
+
+    distance : function(color) {
+        try {
+            var d = Math.abs(this.red - color.red) + Math.abs(this.green - color.green) + Math.abs(this.blue - color.blue);
+        } catch(e) { }
+        return d;
+    },
+
+    blend: function(c1, c2, w){
+        try {
+            var result = new Flog.RayTracer.Color(0,0,0);
+            result = Flog.RayTracer.Color.prototype.add(
+                    Flog.RayTracer.Color.prototype.multiplyScalar(c1, 1 - w),
+                    Flog.RayTracer.Color.prototype.multiplyScalar(c2, w)
+                    );
+        } catch(e) { }
+        return result;
+    },
+
+    brightness : function() {
+        try {
+            var r = Math.floor(this.red*255);
+            var g = Math.floor(this.green*255);
+            var b = Math.floor(this.blue*255);
+        } catch(e) { }
+        return (r * 77 + g * 150 + b * 29) >> 8;
+    },
+
+    toString : function () {
+        try {
+            var r = Math.floor(this.red*255);
+            var g = Math.floor(this.green*255);
+            var b = Math.floor(this.blue*255);
+        } catch(e) { }
+
+        return "rgb("+ r +","+ g +","+ b +")";
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Light = Class.create();
+
+Flog.RayTracer.Light.prototype = {
+    position: null,
+    color: null,
+    intensity: 10.0,
+
+    initialize : function(pos, color, intensity) {
+        try {
+            this.position = pos;
+            this.color = color;
+            this.intensity = (intensity ? intensity : 10.0);
+
+        } catch(e) { }
+    },
+
+    toString : function () {
+        try {
+            var result = 'Light [' + this.position.x + ',' + this.position.y + ',' + this.position.z + ']';
+        } catch(e) { }
+        return result;
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Vector = Class.create();
+
+Flog.RayTracer.Vector.prototype = {
+    x : 0.0,
+    y : 0.0,
+    z : 0.0,
+
+    initialize : function(x, y, z) {
+        try {
+            this.x = (x ? x : 0);
+            this.y = (y ? y : 0);
+            this.z = (z ? z : 0);
+        } catch(e) { }
+    },
+
+    copy: function(vector){
+        try {
+            this.x = vector.x;
+            this.y = vector.y;
+            this.z = vector.z;
+        } catch(e) { }
+    },
+
+    normalize : function() {
+        try {
+            var m = this.magnitude();
+            var result = new Flog.RayTracer.Vector(this.x / m, this.y / m, this.z / m);
+        } catch(e) { }
+        return result;
+    },
+
+    magnitude : function() {
+        try {
+            return Math.sqrt((this.x * this.x) + (this.y * this.y) + (this.z * this.z));
+        } catch(e)  { }
+    },
+
+    cross : function(w) {
+        try {
+            return new Flog.RayTracer.Vector(
+                    -this.z * w.y + this.y * w.z,
+                    this.z * w.x - this.x * w.z,
+                    -this.y * w.x + this.x * w.y);
+        } catch(e) { }
+    },
+
+    dot : function(w) {
+        try {
+            return this.x * w.x + this.y * w.y + this.z * w.z;
+        } catch(e) { }
+    },
+
+    add : function(v, w) {
+        try {
+            return new Flog.RayTracer.Vector(w.x + v.x, w.y + v.y, w.z + v.z);
+        } catch(e) { }
+    },
+
+    subtract : function(v, w) {
+        try {
+            if(!w || !v) throw 'Vectors must be defined [' + v + ',' + w + ']';
+            return new Flog.RayTracer.Vector(v.x - w.x, v.y - w.y, v.z - w.z);
+        } catch(e) { }
+    },
+
+    multiplyVector : function(v, w) {
+        try {
+            return new Flog.RayTracer.Vector(v.x * w.x, v.y * w.y, v.z * w.z);
+        } catch(e) { }
+    },
+
+    multiplyScalar : function(v, w) {
+        try {
+            return new Flog.RayTracer.Vector(v.x * w, v.y * w, v.z * w);
+        } catch(e) { }
+    },
+
+    toString : function () {
+        try {
+            return 'Vector [' + this.x + ',' + this.y + ',' + this.z + ']';
+        } catch(e) { }
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Ray = Class.create();
+
+Flog.RayTracer.Ray.prototype = {
+    position : null,
+    direction : null,
+    initialize : function(pos, dir) {
+        try {
+            this.position = pos;
+            this.direction = dir;
+        } catch(e) { }
+    },
+
+    toString : function () {
+        try {
+            return 'Ray [' + this.position + ',' + this.direction + ']';
+        } catch(e) { }
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Scene = Class.create();
+
+Flog.RayTracer.Scene.prototype = {
+    camera : null,
+    shapes : [],
+    lights : [],
+    background : null,
+
+    initialize : function() {
+        try {
+            this.camera = new Flog.RayTracer.Camera(
+                    new Flog.RayTracer.Vector(0,0,-5),
+                    new Flog.RayTracer.Vector(0,0,1),
+                    new Flog.RayTracer.Vector(0,1,0)
+                    );
+            this.shapes = new Array();
+            this.lights = new Array();
+            this.background = new Flog.RayTracer.Background(new Flog.RayTracer.Color(0,0,0.5), 0.2);
+
+        } catch(e) { }
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+if(typeof(Flog.RayTracer.Material) == 'undefined') Flog.RayTracer.Material = {};
+
+Flog.RayTracer.Material.BaseMaterial = Class.create();
+
+Flog.RayTracer.Material.BaseMaterial.prototype = {
+
+    gloss: 2.0,             // [0...infinity] 0 = matt
+    transparency: 0.0,      // 0=opaque
+    reflection: 0.0,        // [0...infinity] 0 = no reflection
+    refraction: 0.50,
+    hasTexture: false,
+
+    initialize : function() {
+
+    },
+
+    getColor: function(u, v){
+
+    },
+
+    wrapUp: function(t){
+        try {
+            t = t % 2.0;
+            if(t < -1) t += 2.0;
+            if(t >= 1) t -= 2.0;
+        } catch(e) { }
+        return t;
+    },
+
+    toString : function () {
+        try {
+            return 'Material [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
+        } catch(e) { }
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Material.Solid = Class.create();
+
+Flog.RayTracer.Material.Solid.prototype = Object.extend(
+        new Flog.RayTracer.Material.BaseMaterial(), {
+            initialize : function(color, reflection, refraction, transparency, gloss) {
+                try {
+                    this.color = color;
+                    this.reflection = reflection;
+                    this.transparency = transparency;
+                    this.gloss = gloss;
+                    this.hasTexture = false;
+                } catch(e) { }
+            },
+
+            getColor: function(u, v){
+                try {
+                    return this.color;
+                } catch(e) { }
+            },
+
+            toString : function () {
+                try {
+                    return 'SolidMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
+                } catch(e) { }
+            }
+        }
+        );
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Material.Chessboard = Class.create();
+
+Flog.RayTracer.Material.Chessboard.prototype = Object.extend(
+        new Flog.RayTracer.Material.BaseMaterial(), {
+            colorEven: null,
+            colorOdd: null,
+            density: 0.5,
+
+            initialize : function(colorEven, colorOdd, reflection, transparency, gloss, density) {
+                try {
+                    this.colorEven = colorEven;
+                    this.colorOdd = colorOdd;
+                    this.reflection = reflection;
+                    this.transparency = transparency;
+                    this.gloss = gloss;
+                    this.density = density;
+                    this.hasTexture = true;
+                } catch(e) { }
+            },
+
+            getColor: function(u, v){
+                try {
+                    var t = this.wrapUp(u * this.density) * this.wrapUp(v * this.density);
+                } catch(e) { }
+
+                if(t < 0.0)
+                    return this.colorEven;
+                else
+                    return this.colorOdd;
+            },
+
+            toString : function () {
+                try {
+                    return 'ChessMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
+                } catch(e) { }
+            }
+        }
+);
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};
+
+Flog.RayTracer.Shape.Sphere = Class.create();
+
+Flog.RayTracer.Shape.Sphere.prototype = {
+    initialize : function(pos, radius, material) {
+        try {
+            this.radius = radius;
+            this.position = pos;
+            this.material = material;
+
+        } catch(e) { }
+    },
+
+    intersect: function(ray){
+        try {
+            var info = new Flog.RayTracer.IntersectionInfo();
+            info.shape = this;
+
+            var dst = Flog.RayTracer.Vector.prototype.subtract(ray.position, this.position);
+
+            var B = dst.dot(ray.direction);
+            var C = dst.dot(dst) - (this.radius * this.radius);
+            var D = (B * B) - C;
+
+            if(D > 0){ // intersection!
+                info.isHit = true;
+                info.distance = (-B) - Math.sqrt(D);
+                info.position = Flog.RayTracer.Vector.prototype.add(
+                        ray.position,
+                        Flog.RayTracer.Vector.prototype.multiplyScalar(
+                            ray.direction,
+                            info.distance
+                            )
+                        );
+                info.normal = Flog.RayTracer.Vector.prototype.subtract(
+                        info.position,
+                        this.position
+                        ).normalize();
+
+                info.color = this.material.getColor(0,0);
+            } else {
+                info.isHit = false;
+            }
+
+        } catch(e) { }
+        return info;
+    },
+
+    toString : function () {
+        try {
+            return 'Sphere [position=' + this.position + ', radius=' + this.radius + ']';
+        } catch(e) { }
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};
+
+Flog.RayTracer.Shape.Plane = Class.create();
+
+Flog.RayTracer.Shape.Plane.prototype = {
+    d: 0.0,
+
+    initialize : function(pos, d, material) {
+        try {
+            this.position = pos;
+            this.d = d;
+            this.material = material;
+        } catch(e) { }
+    },
+
+    intersect: function(ray){
+        try {
+            var info = new Flog.RayTracer.IntersectionInfo();
+
+            var Vd = this.position.dot(ray.direction);
+            if(Vd == 0) return info; // no intersection
+
+            var t = -(this.position.dot(ray.position) + this.d) / Vd;
+            if(t <= 0) return info;
+
+            info.shape = this;
+            info.isHit = true;
+            info.position = Flog.RayTracer.Vector.prototype.add(
+                    ray.position,
+                    Flog.RayTracer.Vector.prototype.multiplyScalar(
+                        ray.direction,
+                        t
+                        )
+                    );
+            info.normal = this.position;
+            info.distance = t;
+
+            if(this.material.hasTexture){
+                var vU = new Flog.RayTracer.Vector(this.position.y, this.position.z, -this.position.x);
+                var vV = vU.cross(this.position);
+                var u = info.position.dot(vU);
+                var v = info.position.dot(vV);
+                info.color = this.material.getColor(u,v);
+            } else {
+                info.color = this.material.getColor(0,0);
+            }
+
+        } catch(e) { }
+        return info;
+    },
+
+    toString : function () {
+        try {
+            return 'Plane [' + this.position + ', d=' + this.d + ']';
+        } catch(e) { }
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.IntersectionInfo = Class.create();
+
+Flog.RayTracer.IntersectionInfo.prototype = {
+    isHit: false,
+    hitCount: 0,
+    shape: null,
+    position: null,
+    normal: null,
+    color: null,
+    distance: null,
+
+    initialize : function() {
+        try {
+            this.color = new Flog.RayTracer.Color(0,0,0);
+        } catch(e) { }
+    },
+
+    toString : function () {
+        try {
+            return 'Intersection [' + this.position + ']';
+        } catch(e) { }
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Camera = Class.create();
+
+Flog.RayTracer.Camera.prototype = {
+    position: null,
+    lookAt: null,
+    equator: null,
+    up: null,
+    screen: null,
+
+    initialize : function(pos, lookAt, up) {
+        try {
+            this.position = pos;
+            this.lookAt = lookAt;
+            this.up = up;
+            this.equator = lookAt.normalize().cross(this.up);
+            this.screen = Flog.RayTracer.Vector.prototype.add(this.position, this.lookAt);
+        } catch(e) { }
+    },
+
+    getRay: function(vx, vy){
+        try {
+            var pos = Flog.RayTracer.Vector.prototype.subtract(
+                    this.screen,
+                    Flog.RayTracer.Vector.prototype.subtract(
+                        Flog.RayTracer.Vector.prototype.multiplyScalar(this.equator, vx),
+                        Flog.RayTracer.Vector.prototype.multiplyScalar(this.up, vy)
+                        )
+                    );
+            pos.y = pos.y * -1;
+            var dir = Flog.RayTracer.Vector.prototype.subtract(
+                    pos,
+                    this.position
+                    );
+
+            var ray = new Flog.RayTracer.Ray(pos, dir.normalize());
+
+        } catch(e) { }
+        return ray;
+    },
+
+    toString : function () {
+        try {
+            return 'Ray []';
+        } catch(e) { }
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Background = Class.create();
+
+Flog.RayTracer.Background.prototype = {
+    color : null,
+    ambience : 0.0,
+
+    initialize : function(color, ambience) {
+        try {
+            this.color = color;
+            this.ambience = ambience;
+        } catch(e) { }
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Engine = Class.create();
+
+Flog.RayTracer.Engine.prototype = {
+    canvas: null, /* 2d context we can render to */
+
+    initialize: function(options){
+        try {
+            this.options = Object.extend({
+                canvasHeight: 100,
+                canvasWidth: 100,
+                pixelWidth: 2,
+                pixelHeight: 2,
+                renderDiffuse: false,
+                renderShadows: false,
+                renderHighlights: false,
+                renderReflections: false,
+                rayDepth: 2
+            }, options || {});
+
+            this.options.canvasHeight /= this.options.pixelHeight;
+            this.options.canvasWidth /= this.options.pixelWidth;
+
+        } catch(e) { }
+
+        /* TODO: dynamically include other scripts */
+    },
+
+    setPixel: function(x, y, color){
+        try {
+            var pxW, pxH;
+            pxW = this.options.pixelWidth;
+            pxH = this.options.pixelHeight;
+
+            if (this.canvas) {
+                this.canvas.fillStyle = color.toString();
+                this.canvas.fillRect (x * pxW, y * pxH, pxW, pxH);
+            } else {
+                if (x ===  y) {
+                    checkNumber += color.brightness();
+                }
+                // print(x * pxW, y * pxH, pxW, pxH);
+            }
+
+        } catch(e) { }
+    },
+
+    renderScene: function(scene, canvas){
+        try {
+            checkNumber = 0;
+            /* Get canvas */
+            if (canvas) {
+                this.canvas = canvas.getContext("2d");
+            } else {
+                this.canvas = null;
+            }
+
+            var canvasHeight = this.options.canvasHeight;
+            var canvasWidth = this.options.canvasWidth;
+
+            for(var y=0; y < canvasHeight; y++){
+                for(var x=0; x < canvasWidth; x++){
+                    try {
+                        var yp = y * 1.0 / canvasHeight * 2 - 1;
+                        var xp = x * 1.0 / canvasWidth * 2 - 1;
+
+                        var ray = scene.camera.getRay(xp, yp);
+
+                        var color = this.getPixelColor(ray, scene);
+
+                        this.setPixel(x, y, color);
+
+                    } catch(e) { }
+                }
+            }
+        } catch(e) { }
+        if (checkNumber !== 2321) {
+            throw new Error("Scene rendered incorrectly");
+        }
+    },
+
+    getPixelColor: function(ray, scene){
+        try {
+            var info = this.testIntersection(ray, scene, null);
+            if(info.isHit){
+                var color = this.rayTrace(info, ray, scene, 0);
+                return color;
+            }
+            return scene.background.color;
+        } catch(e) { }
+    },
+
+    testIntersection: function(ray, scene, exclude){
+        try {
+            var hits = 0;
+            var best = new Flog.RayTracer.IntersectionInfo();
+            best.distance = 2000;
+
+            for(var i=0; i<scene.shapes.length; i++){
+                try {
+                    var shape = scene.shapes[i];
+
+                    if(shape != exclude){
+                        var info = shape.intersect(ray);
+                        if(info.isHit && info.distance >= 0 && info.distance < best.distance){
+                            best = info;
+                            hits++;
+                        }
+                    }
+
+                } catch(e) { }
+            }
+            best.hitCount = hits;
+
+        } catch(e) { }
+        return best;
+    },
+
+    getReflectionRay: function(P,N,V){
+        try {
+            var c1 = -N.dot(V);
+            var R1 = Flog.RayTracer.Vector.prototype.add(
+                    Flog.RayTracer.Vector.prototype.multiplyScalar(N, 2*c1),
+                    V
+                    );
+
+        } catch(e) { }
+        return new Flog.RayTracer.Ray(P, R1);
+    },
+
+    rayTrace: function(info, ray, scene, depth){
+        // Calc ambient
+        try {
+            var color = Flog.RayTracer.Color.prototype.multiplyScalar(info.color, scene.background.ambience);
+            var oldColor = color;
+            var shininess = Math.pow(10, info.shape.material.gloss + 1);
+
+            for(var i=0; i<scene.lights.length; i++){
+                try {
+                    var light = scene.lights[i];
+
+                    // Calc diffuse lighting
+                    var v = Flog.RayTracer.Vector.prototype.subtract(
+                            light.position,
+                            info.position
+                            ).normalize();
+
+                    if(this.options.renderDiffuse){
+                        var L = v.dot(info.normal);
+                        if(L > 0.0){
+                            color = Flog.RayTracer.Color.prototype.add(
+                                    color,
+                                    Flog.RayTracer.Color.prototype.multiply(
+                                        info.color,
+                                        Flog.RayTracer.Color.prototype.multiplyScalar(
+                                            light.color,
+                                            L
+                                            )
+                                        )
+                                    );
+                        }
+                    }
+
+                } catch(e) { }
+
+                try {
+                    // The greater the depth the more accurate the colours, but
+                    // this is exponentially (!) expensive
+                    if(depth <= this.options.rayDepth){
+                        // calculate reflection ray
+                        if(this.options.renderReflections && info.shape.material.reflection > 0)
+                        {
+                            var reflectionRay = this.getReflectionRay(info.position, info.normal, ray.direction);
+                            var refl = this.testIntersection(reflectionRay, scene, info.shape);
+
+                            if (refl.isHit && refl.distance > 0){
+                                refl.color = this.rayTrace(refl, reflectionRay, scene, depth + 1);
+                            } else {
+                                refl.color = scene.background.color;
+                            }
+
+                            color = Flog.RayTracer.Color.prototype.blend(
+                                    color,
+                                    refl.color,
+                                    info.shape.material.reflection
+                                    );
+                        }
+
+                        // Refraction
+                        /* TODO */
+                    }
+                }  catch(e) { }
+
+                /* Render shadows and highlights */
+
+                var shadowInfo = new Flog.RayTracer.IntersectionInfo();
+
+                if(this.options.renderShadows){
+                    var shadowRay = new Flog.RayTracer.Ray(info.position, v);
+
+                    shadowInfo = this.testIntersection(shadowRay, scene, info.shape);
+                    if(shadowInfo.isHit && shadowInfo.shape != info.shape /*&& shadowInfo.shape.type != 'PLANE'*/){
+                        var vA = Flog.RayTracer.Color.prototype.multiplyScalar(color, 0.5);
+                        var dB = (0.5 * Math.pow(shadowInfo.shape.material.transparency, 0.5));
+                        color = Flog.RayTracer.Color.prototype.addScalar(vA,dB);
+                    }
+                }
+
+                try {
+                    // Phong specular highlights
+                    if(this.options.renderHighlights && !shadowInfo.isHit && info.shape.material.gloss > 0){
+                        var Lv = Flog.RayTracer.Vector.prototype.subtract(
+                                info.shape.position,
+                                light.position
+                                ).normalize();
+
+                        var E = Flog.RayTracer.Vector.prototype.subtract(
+                                scene.camera.position,
+                                info.shape.position
+                                ).normalize();
+
+                        var H = Flog.RayTracer.Vector.prototype.subtract(
+                                E,
+                                Lv
+                                ).normalize();
+
+                        var glossWeight = Math.pow(Math.max(info.normal.dot(H), 0), shininess);
+                        color = Flog.RayTracer.Color.prototype.add(
+                                Flog.RayTracer.Color.prototype.multiplyScalar(light.color, glossWeight),
+                                color
+                                );
+                    }
+                } catch(e) { }
+            }
+            color.limit();
+
+        } catch(e) { }
+        return color;
+    }
+};
+
+
+function renderScene(){
+    try {
+        var scene = new Flog.RayTracer.Scene();
+
+        scene.camera = new Flog.RayTracer.Camera(
+                new Flog.RayTracer.Vector(0, 0, -15),
+                new Flog.RayTracer.Vector(-0.2, 0, 5),
+                new Flog.RayTracer.Vector(0, 1, 0)
+                );
+
+        scene.background = new Flog.RayTracer.Background(
+                new Flog.RayTracer.Color(0.5, 0.5, 0.5),
+                0.4
+                );
+
+        var sphere = new Flog.RayTracer.Shape.Sphere(
+                new Flog.RayTracer.Vector(-1.5, 1.5, 2),
+                1.5,
+                new Flog.RayTracer.Material.Solid(
+                    new Flog.RayTracer.Color(0,0.5,0.5),
+                    0.3,
+                    0.0,
+                    0.0,
+                    2.0
+                    )
+                );
+
+        var sphere1 = new Flog.RayTracer.Shape.Sphere(
+                new Flog.RayTracer.Vector(1, 0.25, 1),
+                0.5,
+                new Flog.RayTracer.Material.Solid(
+                    new Flog.RayTracer.Color(0.9,0.9,0.9),
+                    0.1,
+                    0.0,
+                    0.0,
+                    1.5
+                    )
+                );
+
+        var plane = new Flog.RayTracer.Shape.Plane(
+                new Flog.RayTracer.Vector(0.1, 0.9, -0.5).normalize(),
+                1.2,
+                new Flog.RayTracer.Material.Chessboard(
+                    new Flog.RayTracer.Color(1,1,1),
+                    new Flog.RayTracer.Color(0,0,0),
+                    0.2,
+                    0.0,
+                    1.0,
+                    0.7
+                    )
+                );
+
+        scene.shapes.push(plane);
+        scene.shapes.push(sphere);
+        scene.shapes.push(sphere1);
+
+        var light = new Flog.RayTracer.Light(
+                new Flog.RayTracer.Vector(5, 10, -1),
+                new Flog.RayTracer.Color(0.8, 0.8, 0.8)
+                );
+
+        var light1 = new Flog.RayTracer.Light(
+                new Flog.RayTracer.Vector(-3, 5, -15),
+                new Flog.RayTracer.Color(0.8, 0.8, 0.8),
+                100
+                );
+
+        scene.lights.push(light);
+        scene.lights.push(light1);
+
+        var imageWidth = 100; // $F('imageWidth');
+        var imageHeight = 100; // $F('imageHeight');
+        var pixelSize = "5,5".split(','); //  $F('pixelSize').split(',');
+        var renderDiffuse = true; // $F('renderDiffuse');
+        var renderShadows = true; // $F('renderShadows');
+        var renderHighlights = true; // $F('renderHighlights');
+        var renderReflections = true; // $F('renderReflections');
+        var rayDepth = 2;//$F('rayDepth');
+
+        var raytracer = new Flog.RayTracer.Engine(
+                {
+                    canvasWidth: imageWidth,
+                    canvasHeight: imageHeight,
+                    pixelWidth: pixelSize[0],
+                    pixelHeight: pixelSize[1],
+                    "renderDiffuse": renderDiffuse,
+                    "renderHighlights": renderHighlights,
+                    "renderShadows": renderShadows,
+                    "renderReflections": renderReflections,
+                    "rayDepth": rayDepth
+                }
+                );
+
+        raytracer.renderScene(scene, null, 0);
+    } catch(e) { }
+}
+
+for (var i = 0; i < 6; ++i)
+    renderScene();
diff --git a/LayoutTests/js/regress/script-tests/v8-raytrace-with-try-catch.js b/LayoutTests/js/regress/script-tests/v8-raytrace-with-try-catch.js
new file mode 100644 (file)
index 0000000..e8bac18
--- /dev/null
@@ -0,0 +1,1101 @@
+// The ray tracer code in this file is written by Adam Burmister. It
+// is available in its original form from:
+//
+//   http://labs.flog.nz.co/raytracer/
+//
+// It has been modified slightly by Google to work as a standalone
+// benchmark, but the all the computational code remains
+// untouched. This file also contains a copy of parts of the Prototype
+// JavaScript framework which is used by the ray tracer.
+
+// Variable used to hold a number that can be used to verify that
+// the scene was ray traced correctly.
+var checkNumber;
+
+
+// ------------------------------------------------------------------------
+// ------------------------------------------------------------------------
+
+// The following is a copy of parts of the Prototype JavaScript library:
+
+// Prototype JavaScript framework, version 1.5.0
+// (c) 2005-2007 Sam Stephenson
+//
+// Prototype is freely distributable under the terms of an MIT-style license.
+// For details, see the Prototype web site: http://prototype.conio.net/
+
+let __exceptionCounter = 0;
+function randomException() {
+    __exceptionCounter++;
+    if (__exceptionCounter % 500 === 0) {
+        throw new Error("rando");
+    }
+}
+noInline(randomException);
+
+var Class = {
+    create: function() {
+        return function() {
+            try {
+                this.initialize.apply(this, arguments);
+                randomException();
+            } catch(e) { }
+        }
+    }
+};
+
+
+Object.extend = function(destination, source) {
+    for (var property in source) {
+        try {
+            destination[property] = source[property];
+            randomException();
+        } catch(e) { }
+    }
+    return destination;
+};
+
+
+// ------------------------------------------------------------------------
+// ------------------------------------------------------------------------
+
+// The rest of this file is the actual ray tracer written by Adam
+// Burmister. It's a concatenation of the following files:
+//
+//   flog/color.js
+//   flog/light.js
+//   flog/vector.js
+//   flog/ray.js
+//   flog/scene.js
+//   flog/material/basematerial.js
+//   flog/material/solid.js
+//   flog/material/chessboard.js
+//   flog/shape/baseshape.js
+//   flog/shape/sphere.js
+//   flog/shape/plane.js
+//   flog/intersectioninfo.js
+//   flog/camera.js
+//   flog/background.js
+//   flog/engine.js
+
+
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Color = Class.create();
+
+Flog.RayTracer.Color.prototype = {
+    red : 0.0,
+    green : 0.0,
+    blue : 0.0,
+
+    initialize : function(r, g, b) {
+        try {
+            if(!r) r = 0.0;
+            if(!g) g = 0.0;
+            if(!b) b = 0.0;
+
+            this.red = r;
+            this.green = g;
+            this.blue = b;
+            randomException();
+        } catch(e) { }
+    },
+
+    add : function(c1, c2){
+        try {
+            var result = new Flog.RayTracer.Color(0,0,0);
+
+            result.red = c1.red + c2.red;
+            result.green = c1.green + c2.green;
+            result.blue = c1.blue + c2.blue;
+
+            randomException();
+        } catch(e) { }
+
+        return result;
+    },
+
+    addScalar: function(c1, s){
+        try {
+            var result = new Flog.RayTracer.Color(0,0,0);
+
+            result.red = c1.red + s;
+            result.green = c1.green + s;
+            result.blue = c1.blue + s;
+
+            result.limit();
+
+            randomException();
+        } catch(e) { }
+
+        return result;
+    },
+
+    subtract: function(c1, c2){
+        try {
+            var result = new Flog.RayTracer.Color(0,0,0);
+
+            result.red = c1.red - c2.red;
+            result.green = c1.green - c2.green;
+            result.blue = c1.blue - c2.blue;
+
+            randomException();
+        } catch(e) { }
+
+        return result;
+    },
+
+    multiply : function(c1, c2) {
+        try {
+            var result = new Flog.RayTracer.Color(0,0,0);
+
+            result.red = c1.red * c2.red;
+            result.green = c1.green * c2.green;
+            result.blue = c1.blue * c2.blue;
+
+            randomException();
+        } catch(e) { }
+
+        return result;
+    },
+
+    multiplyScalar : function(c1, f) {
+        try {
+            var result = new Flog.RayTracer.Color(0,0,0);
+
+            result.red = c1.red * f;
+            result.green = c1.green * f;
+            result.blue = c1.blue * f;
+
+            randomException();
+        } catch(e) { }
+
+        return result;
+    },
+
+    divideFactor : function(c1, f) {
+        try {
+            var result = new Flog.RayTracer.Color(0,0,0);
+
+            result.red = c1.red / f;
+            result.green = c1.green / f;
+            result.blue = c1.blue / f;
+
+            randomException();
+        } catch(e) { }
+
+        return result;
+    },
+
+    limit: function(){
+        try { 
+            this.red = (this.red > 0.0) ? ( (this.red > 1.0) ? 1.0 : this.red ) : 0.0;
+            this.green = (this.green > 0.0) ? ( (this.green > 1.0) ? 1.0 : this.green ) : 0.0;
+            this.blue = (this.blue > 0.0) ? ( (this.blue > 1.0) ? 1.0 : this.blue ) : 0.0;
+
+            randomException();
+        } catch(e) { }
+    },
+
+    distance : function(color) {
+        try {
+            var d = Math.abs(this.red - color.red) + Math.abs(this.green - color.green) + Math.abs(this.blue - color.blue);
+            randomException();
+        } catch(e) { }
+        return d;
+    },
+
+    blend: function(c1, c2, w){
+        try {
+            var result = new Flog.RayTracer.Color(0,0,0);
+            result = Flog.RayTracer.Color.prototype.add(
+                    Flog.RayTracer.Color.prototype.multiplyScalar(c1, 1 - w),
+                    Flog.RayTracer.Color.prototype.multiplyScalar(c2, w)
+                    );
+            randomException();
+        } catch(e) { }
+        return result;
+    },
+
+    brightness : function() {
+        try {
+            var r = Math.floor(this.red*255);
+            var g = Math.floor(this.green*255);
+            var b = Math.floor(this.blue*255);
+            randomException();
+        } catch(e) { }
+        return (r * 77 + g * 150 + b * 29) >> 8;
+    },
+
+    toString : function () {
+        try {
+            var r = Math.floor(this.red*255);
+            var g = Math.floor(this.green*255);
+            var b = Math.floor(this.blue*255);
+            randomException();
+        } catch(e) { }
+
+        return "rgb("+ r +","+ g +","+ b +")";
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Light = Class.create();
+
+Flog.RayTracer.Light.prototype = {
+    position: null,
+    color: null,
+    intensity: 10.0,
+
+    initialize : function(pos, color, intensity) {
+        try {
+            this.position = pos;
+            this.color = color;
+            this.intensity = (intensity ? intensity : 10.0);
+
+            randomException();
+        } catch(e) { }
+    },
+
+    toString : function () {
+        try {
+            var result = 'Light [' + this.position.x + ',' + this.position.y + ',' + this.position.z + ']';
+            randomException();
+        } catch(e) { }
+        return result;
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Vector = Class.create();
+
+Flog.RayTracer.Vector.prototype = {
+    x : 0.0,
+    y : 0.0,
+    z : 0.0,
+
+    initialize : function(x, y, z) {
+        try {
+            this.x = (x ? x : 0);
+            this.y = (y ? y : 0);
+            this.z = (z ? z : 0);
+            randomException();
+        } catch(e) { }
+    },
+
+    copy: function(vector){
+        try {
+            this.x = vector.x;
+            this.y = vector.y;
+            this.z = vector.z;
+            randomException();
+        } catch(e) { }
+    },
+
+    normalize : function() {
+        try {
+            var m = this.magnitude();
+            var result = new Flog.RayTracer.Vector(this.x / m, this.y / m, this.z / m);
+            randomException();
+        } catch(e) { }
+        return result;
+    },
+
+    magnitude : function() {
+        try {
+            return Math.sqrt((this.x * this.x) + (this.y * this.y) + (this.z * this.z));
+        } catch(e)  { }
+    },
+
+    cross : function(w) {
+        try {
+            return new Flog.RayTracer.Vector(
+                    -this.z * w.y + this.y * w.z,
+                    this.z * w.x - this.x * w.z,
+                    -this.y * w.x + this.x * w.y);
+        } catch(e) { }
+    },
+
+    dot : function(w) {
+        try {
+            return this.x * w.x + this.y * w.y + this.z * w.z;
+        } catch(e) { }
+    },
+
+    add : function(v, w) {
+        try {
+            return new Flog.RayTracer.Vector(w.x + v.x, w.y + v.y, w.z + v.z);
+        } catch(e) { }
+    },
+
+    subtract : function(v, w) {
+        try {
+            if(!w || !v) throw 'Vectors must be defined [' + v + ',' + w + ']';
+            return new Flog.RayTracer.Vector(v.x - w.x, v.y - w.y, v.z - w.z);
+        } catch(e) { }
+    },
+
+    multiplyVector : function(v, w) {
+        try {
+            return new Flog.RayTracer.Vector(v.x * w.x, v.y * w.y, v.z * w.z);
+        } catch(e) { }
+    },
+
+    multiplyScalar : function(v, w) {
+        try {
+            return new Flog.RayTracer.Vector(v.x * w, v.y * w, v.z * w);
+        } catch(e) { }
+    },
+
+    toString : function () {
+        try {
+            return 'Vector [' + this.x + ',' + this.y + ',' + this.z + ']';
+        } catch(e) { }
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Ray = Class.create();
+
+Flog.RayTracer.Ray.prototype = {
+    position : null,
+    direction : null,
+    initialize : function(pos, dir) {
+        try {
+            this.position = pos;
+            this.direction = dir;
+            randomException();
+        } catch(e) { }
+    },
+
+    toString : function () {
+        try {
+            return 'Ray [' + this.position + ',' + this.direction + ']';
+        } catch(e) { }
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Scene = Class.create();
+
+Flog.RayTracer.Scene.prototype = {
+    camera : null,
+    shapes : [],
+    lights : [],
+    background : null,
+
+    initialize : function() {
+        try {
+            this.camera = new Flog.RayTracer.Camera(
+                    new Flog.RayTracer.Vector(0,0,-5),
+                    new Flog.RayTracer.Vector(0,0,1),
+                    new Flog.RayTracer.Vector(0,1,0)
+                    );
+            this.shapes = new Array();
+            this.lights = new Array();
+            this.background = new Flog.RayTracer.Background(new Flog.RayTracer.Color(0,0,0.5), 0.2);
+
+            randomException();
+        } catch(e) { }
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+if(typeof(Flog.RayTracer.Material) == 'undefined') Flog.RayTracer.Material = {};
+
+Flog.RayTracer.Material.BaseMaterial = Class.create();
+
+Flog.RayTracer.Material.BaseMaterial.prototype = {
+
+    gloss: 2.0,             // [0...infinity] 0 = matt
+    transparency: 0.0,      // 0=opaque
+    reflection: 0.0,        // [0...infinity] 0 = no reflection
+    refraction: 0.50,
+    hasTexture: false,
+
+    initialize : function() {
+
+    },
+
+    getColor: function(u, v){
+
+    },
+
+    wrapUp: function(t){
+        try {
+            t = t % 2.0;
+            if(t < -1) t += 2.0;
+            if(t >= 1) t -= 2.0;
+            randomException();
+        } catch(e) { }
+        return t;
+    },
+
+    toString : function () {
+        try {
+            return 'Material [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
+        } catch(e) { }
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Material.Solid = Class.create();
+
+Flog.RayTracer.Material.Solid.prototype = Object.extend(
+        new Flog.RayTracer.Material.BaseMaterial(), {
+            initialize : function(color, reflection, refraction, transparency, gloss) {
+                try {
+                    this.color = color;
+                    this.reflection = reflection;
+                    this.transparency = transparency;
+                    this.gloss = gloss;
+                    this.hasTexture = false;
+                    randomException();
+                } catch(e) { }
+            },
+
+            getColor: function(u, v){
+                try {
+                    return this.color;
+                } catch(e) { }
+            },
+
+            toString : function () {
+                try {
+                    return 'SolidMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
+                } catch(e) { }
+            }
+        }
+        );
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Material.Chessboard = Class.create();
+
+Flog.RayTracer.Material.Chessboard.prototype = Object.extend(
+        new Flog.RayTracer.Material.BaseMaterial(), {
+            colorEven: null,
+            colorOdd: null,
+            density: 0.5,
+
+            initialize : function(colorEven, colorOdd, reflection, transparency, gloss, density) {
+                try {
+                    this.colorEven = colorEven;
+                    this.colorOdd = colorOdd;
+                    this.reflection = reflection;
+                    this.transparency = transparency;
+                    this.gloss = gloss;
+                    this.density = density;
+                    this.hasTexture = true;
+                    randomException();
+                } catch(e) { }
+            },
+
+            getColor: function(u, v){
+                try {
+                    var t = this.wrapUp(u * this.density) * this.wrapUp(v * this.density);
+                    randomException();
+                } catch(e) { }
+
+                if(t < 0.0)
+                    return this.colorEven;
+                else
+                    return this.colorOdd;
+            },
+
+            toString : function () {
+                try {
+                    return 'ChessMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
+                } catch(e) { }
+            }
+        }
+);
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};
+
+Flog.RayTracer.Shape.Sphere = Class.create();
+
+Flog.RayTracer.Shape.Sphere.prototype = {
+    initialize : function(pos, radius, material) {
+        try {
+            this.radius = radius;
+            this.position = pos;
+            this.material = material;
+
+            randomException();
+        } catch(e) { }
+    },
+
+    intersect: function(ray){
+        try {
+            var info = new Flog.RayTracer.IntersectionInfo();
+            info.shape = this;
+
+            var dst = Flog.RayTracer.Vector.prototype.subtract(ray.position, this.position);
+
+            var B = dst.dot(ray.direction);
+            var C = dst.dot(dst) - (this.radius * this.radius);
+            var D = (B * B) - C;
+
+            if(D > 0){ // intersection!
+                info.isHit = true;
+                info.distance = (-B) - Math.sqrt(D);
+                info.position = Flog.RayTracer.Vector.prototype.add(
+                        ray.position,
+                        Flog.RayTracer.Vector.prototype.multiplyScalar(
+                            ray.direction,
+                            info.distance
+                            )
+                        );
+                info.normal = Flog.RayTracer.Vector.prototype.subtract(
+                        info.position,
+                        this.position
+                        ).normalize();
+
+                info.color = this.material.getColor(0,0);
+            } else {
+                info.isHit = false;
+            }
+
+            randomException();
+        } catch(e) { }
+        return info;
+    },
+
+    toString : function () {
+        try {
+            return 'Sphere [position=' + this.position + ', radius=' + this.radius + ']';
+        } catch(e) { }
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};
+
+Flog.RayTracer.Shape.Plane = Class.create();
+
+Flog.RayTracer.Shape.Plane.prototype = {
+    d: 0.0,
+
+    initialize : function(pos, d, material) {
+        try {
+            this.position = pos;
+            this.d = d;
+            this.material = material;
+            randomException();
+        } catch(e) { }
+    },
+
+    intersect: function(ray){
+        try {
+            var info = new Flog.RayTracer.IntersectionInfo();
+
+            var Vd = this.position.dot(ray.direction);
+            if(Vd == 0) return info; // no intersection
+
+            var t = -(this.position.dot(ray.position) + this.d) / Vd;
+            if(t <= 0) return info;
+
+            info.shape = this;
+            info.isHit = true;
+            info.position = Flog.RayTracer.Vector.prototype.add(
+                    ray.position,
+                    Flog.RayTracer.Vector.prototype.multiplyScalar(
+                        ray.direction,
+                        t
+                        )
+                    );
+            info.normal = this.position;
+            info.distance = t;
+
+            if(this.material.hasTexture){
+                var vU = new Flog.RayTracer.Vector(this.position.y, this.position.z, -this.position.x);
+                var vV = vU.cross(this.position);
+                var u = info.position.dot(vU);
+                var v = info.position.dot(vV);
+                info.color = this.material.getColor(u,v);
+            } else {
+                info.color = this.material.getColor(0,0);
+            }
+
+            randomException();
+        } catch(e) { }
+        return info;
+    },
+
+    toString : function () {
+        try {
+            return 'Plane [' + this.position + ', d=' + this.d + ']';
+        } catch(e) { }
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.IntersectionInfo = Class.create();
+
+Flog.RayTracer.IntersectionInfo.prototype = {
+    isHit: false,
+    hitCount: 0,
+    shape: null,
+    position: null,
+    normal: null,
+    color: null,
+    distance: null,
+
+    initialize : function() {
+        try {
+            this.color = new Flog.RayTracer.Color(0,0,0);
+            randomException();
+        } catch(e) { }
+    },
+
+    toString : function () {
+        try {
+            return 'Intersection [' + this.position + ']';
+        } catch(e) { }
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Camera = Class.create();
+
+Flog.RayTracer.Camera.prototype = {
+    position: null,
+    lookAt: null,
+    equator: null,
+    up: null,
+    screen: null,
+
+    initialize : function(pos, lookAt, up) {
+        try {
+            this.position = pos;
+            this.lookAt = lookAt;
+            this.up = up;
+            this.equator = lookAt.normalize().cross(this.up);
+            this.screen = Flog.RayTracer.Vector.prototype.add(this.position, this.lookAt);
+            randomException();
+        } catch(e) { }
+    },
+
+    getRay: function(vx, vy){
+        try {
+            var pos = Flog.RayTracer.Vector.prototype.subtract(
+                    this.screen,
+                    Flog.RayTracer.Vector.prototype.subtract(
+                        Flog.RayTracer.Vector.prototype.multiplyScalar(this.equator, vx),
+                        Flog.RayTracer.Vector.prototype.multiplyScalar(this.up, vy)
+                        )
+                    );
+            pos.y = pos.y * -1;
+            var dir = Flog.RayTracer.Vector.prototype.subtract(
+                    pos,
+                    this.position
+                    );
+
+            var ray = new Flog.RayTracer.Ray(pos, dir.normalize());
+
+            randomException();
+        } catch(e) { }
+        return ray;
+    },
+
+    toString : function () {
+        try {
+            return 'Ray []';
+        } catch(e) { }
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Background = Class.create();
+
+Flog.RayTracer.Background.prototype = {
+    color : null,
+    ambience : 0.0,
+
+    initialize : function(color, ambience) {
+        try {
+            this.color = color;
+            this.ambience = ambience;
+            randomException();
+        } catch(e) { }
+    }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Engine = Class.create();
+
+Flog.RayTracer.Engine.prototype = {
+    canvas: null, /* 2d context we can render to */
+
+    initialize: function(options){
+        try {
+            this.options = Object.extend({
+                canvasHeight: 100,
+                canvasWidth: 100,
+                pixelWidth: 2,
+                pixelHeight: 2,
+                renderDiffuse: false,
+                renderShadows: false,
+                renderHighlights: false,
+                renderReflections: false,
+                rayDepth: 2
+            }, options || {});
+
+            this.options.canvasHeight /= this.options.pixelHeight;
+            this.options.canvasWidth /= this.options.pixelWidth;
+
+            randomException();
+        } catch(e) { }
+
+        /* TODO: dynamically include other scripts */
+    },
+
+    setPixel: function(x, y, color){
+        try {
+            var pxW, pxH;
+            pxW = this.options.pixelWidth;
+            pxH = this.options.pixelHeight;
+
+            if (this.canvas) {
+                this.canvas.fillStyle = color.toString();
+                this.canvas.fillRect (x * pxW, y * pxH, pxW, pxH);
+            } else {
+                if (x ===  y) {
+                    checkNumber += color.brightness();
+                }
+                // print(x * pxW, y * pxH, pxW, pxH);
+            }
+
+            randomException();
+        } catch(e) { }
+    },
+
+    renderScene: function(scene, canvas){
+        try {
+            checkNumber = 0;
+            /* Get canvas */
+            if (canvas) {
+                this.canvas = canvas.getContext("2d");
+            } else {
+                this.canvas = null;
+            }
+
+            var canvasHeight = this.options.canvasHeight;
+            var canvasWidth = this.options.canvasWidth;
+
+            for(var y=0; y < canvasHeight; y++){
+                for(var x=0; x < canvasWidth; x++){
+                    try {
+                        var yp = y * 1.0 / canvasHeight * 2 - 1;
+                        var xp = x * 1.0 / canvasWidth * 2 - 1;
+
+                        var ray = scene.camera.getRay(xp, yp);
+
+                        var color = this.getPixelColor(ray, scene);
+
+                        this.setPixel(x, y, color);
+
+                        randomException();
+                    } catch(e) { }
+                }
+            }
+        } catch(e) { }
+        if (checkNumber !== 2321) {
+            throw new Error("Scene rendered incorrectly");
+        }
+    },
+
+    getPixelColor: function(ray, scene){
+        try {
+            var info = this.testIntersection(ray, scene, null);
+            if(info.isHit){
+                var color = this.rayTrace(info, ray, scene, 0);
+                return color;
+            }
+            return scene.background.color;
+        } catch(e) { }
+    },
+
+    testIntersection: function(ray, scene, exclude){
+        try {
+            var hits = 0;
+            var best = new Flog.RayTracer.IntersectionInfo();
+            best.distance = 2000;
+
+            for(var i=0; i<scene.shapes.length; i++){
+                try {
+                    var shape = scene.shapes[i];
+
+                    if(shape != exclude){
+                        var info = shape.intersect(ray);
+                        if(info.isHit && info.distance >= 0 && info.distance < best.distance){
+                            best = info;
+                            hits++;
+                        }
+                    }
+
+                    randomException();
+                } catch(e) { }
+            }
+            best.hitCount = hits;
+
+            randomException();
+        } catch(e) { }
+        return best;
+    },
+
+    getReflectionRay: function(P,N,V){
+        try {
+            var c1 = -N.dot(V);
+            var R1 = Flog.RayTracer.Vector.prototype.add(
+                    Flog.RayTracer.Vector.prototype.multiplyScalar(N, 2*c1),
+                    V
+                    );
+
+            randomException();
+        } catch(e) { }
+        return new Flog.RayTracer.Ray(P, R1);
+    },
+
+    rayTrace: function(info, ray, scene, depth){
+        // Calc ambient
+        try {
+            var color = Flog.RayTracer.Color.prototype.multiplyScalar(info.color, scene.background.ambience);
+            var oldColor = color;
+            var shininess = Math.pow(10, info.shape.material.gloss + 1);
+
+            for(var i=0; i<scene.lights.length; i++){
+                try {
+                    var light = scene.lights[i];
+
+                    // Calc diffuse lighting
+                    var v = Flog.RayTracer.Vector.prototype.subtract(
+                            light.position,
+                            info.position
+                            ).normalize();
+
+                    if(this.options.renderDiffuse){
+                        var L = v.dot(info.normal);
+                        if(L > 0.0){
+                            color = Flog.RayTracer.Color.prototype.add(
+                                    color,
+                                    Flog.RayTracer.Color.prototype.multiply(
+                                        info.color,
+                                        Flog.RayTracer.Color.prototype.multiplyScalar(
+                                            light.color,
+                                            L
+                                            )
+                                        )
+                                    );
+                        }
+                    }
+
+                    randomException();
+                } catch(e) { }
+
+                try {
+                    // The greater the depth the more accurate the colours, but
+                    // this is exponentially (!) expensive
+                    if(depth <= this.options.rayDepth){
+                        // calculate reflection ray
+                        if(this.options.renderReflections && info.shape.material.reflection > 0)
+                        {
+                            var reflectionRay = this.getReflectionRay(info.position, info.normal, ray.direction);
+                            var refl = this.testIntersection(reflectionRay, scene, info.shape);
+
+                            if (refl.isHit && refl.distance > 0){
+                                refl.color = this.rayTrace(refl, reflectionRay, scene, depth + 1);
+                            } else {
+                                refl.color = scene.background.color;
+                            }
+
+                            color = Flog.RayTracer.Color.prototype.blend(
+                                    color,
+                                    refl.color,
+                                    info.shape.material.reflection
+                                    );
+                        }
+
+                        // Refraction
+                        /* TODO */
+                    }
+                    randomException();
+                }  catch(e) { }
+
+                /* Render shadows and highlights */
+
+                var shadowInfo = new Flog.RayTracer.IntersectionInfo();
+
+                if(this.options.renderShadows){
+                    var shadowRay = new Flog.RayTracer.Ray(info.position, v);
+
+                    shadowInfo = this.testIntersection(shadowRay, scene, info.shape);
+                    if(shadowInfo.isHit && shadowInfo.shape != info.shape /*&& shadowInfo.shape.type != 'PLANE'*/){
+                        var vA = Flog.RayTracer.Color.prototype.multiplyScalar(color, 0.5);
+                        var dB = (0.5 * Math.pow(shadowInfo.shape.material.transparency, 0.5));
+                        color = Flog.RayTracer.Color.prototype.addScalar(vA,dB);
+                    }
+                }
+
+                try {
+                    // Phong specular highlights
+                    if(this.options.renderHighlights && !shadowInfo.isHit && info.shape.material.gloss > 0){
+                        var Lv = Flog.RayTracer.Vector.prototype.subtract(
+                                info.shape.position,
+                                light.position
+                                ).normalize();
+
+                        var E = Flog.RayTracer.Vector.prototype.subtract(
+                                scene.camera.position,
+                                info.shape.position
+                                ).normalize();
+
+                        var H = Flog.RayTracer.Vector.prototype.subtract(
+                                E,
+                                Lv
+                                ).normalize();
+
+                        var glossWeight = Math.pow(Math.max(info.normal.dot(H), 0), shininess);
+                        color = Flog.RayTracer.Color.prototype.add(
+                                Flog.RayTracer.Color.prototype.multiplyScalar(light.color, glossWeight),
+                                color
+                                );
+                    }
+                    randomException();
+                } catch(e) { }
+            }
+            color.limit();
+
+            randomException();
+        } catch(e) { }
+        return color;
+    }
+};
+
+
+function renderScene(){
+    try {
+        var scene = new Flog.RayTracer.Scene();
+
+        scene.camera = new Flog.RayTracer.Camera(
+                new Flog.RayTracer.Vector(0, 0, -15),
+                new Flog.RayTracer.Vector(-0.2, 0, 5),
+                new Flog.RayTracer.Vector(0, 1, 0)
+                );
+
+        scene.background = new Flog.RayTracer.Background(
+                new Flog.RayTracer.Color(0.5, 0.5, 0.5),
+                0.4
+                );
+
+        var sphere = new Flog.RayTracer.Shape.Sphere(
+                new Flog.RayTracer.Vector(-1.5, 1.5, 2),
+                1.5,
+                new Flog.RayTracer.Material.Solid(
+                    new Flog.RayTracer.Color(0,0.5,0.5),
+                    0.3,
+                    0.0,
+                    0.0,
+                    2.0
+                    )
+                );
+
+        var sphere1 = new Flog.RayTracer.Shape.Sphere(
+                new Flog.RayTracer.Vector(1, 0.25, 1),
+                0.5,
+                new Flog.RayTracer.Material.Solid(
+                    new Flog.RayTracer.Color(0.9,0.9,0.9),
+                    0.1,
+                    0.0,
+                    0.0,
+                    1.5
+                    )
+                );
+
+        var plane = new Flog.RayTracer.Shape.Plane(
+                new Flog.RayTracer.Vector(0.1, 0.9, -0.5).normalize(),
+                1.2,
+                new Flog.RayTracer.Material.Chessboard(
+                    new Flog.RayTracer.Color(1,1,1),
+                    new Flog.RayTracer.Color(0,0,0),
+                    0.2,
+                    0.0,
+                    1.0,
+                    0.7
+                    )
+                );
+
+        scene.shapes.push(plane);
+        scene.shapes.push(sphere);
+        scene.shapes.push(sphere1);
+
+        var light = new Flog.RayTracer.Light(
+                new Flog.RayTracer.Vector(5, 10, -1),
+                new Flog.RayTracer.Color(0.8, 0.8, 0.8)
+                );
+
+        var light1 = new Flog.RayTracer.Light(
+                new Flog.RayTracer.Vector(-3, 5, -15),
+                new Flog.RayTracer.Color(0.8, 0.8, 0.8),
+                100
+                );
+
+        scene.lights.push(light);
+        scene.lights.push(light1);
+
+        var imageWidth = 100; // $F('imageWidth');
+        var imageHeight = 100; // $F('imageHeight');
+        var pixelSize = "5,5".split(','); //  $F('pixelSize').split(',');
+        var renderDiffuse = true; // $F('renderDiffuse');
+        var renderShadows = true; // $F('renderShadows');
+        var renderHighlights = true; // $F('renderHighlights');
+        var renderReflections = true; // $F('renderReflections');
+        var rayDepth = 2;//$F('rayDepth');
+
+        var raytracer = new Flog.RayTracer.Engine(
+                {
+                    canvasWidth: imageWidth,
+                    canvasHeight: imageHeight,
+                    pixelWidth: pixelSize[0],
+                    pixelHeight: pixelSize[1],
+                    "renderDiffuse": renderDiffuse,
+                    "renderHighlights": renderHighlights,
+                    "renderShadows": renderShadows,
+                    "renderReflections": renderReflections,
+                    "rayDepth": rayDepth
+                }
+                );
+
+        raytracer.renderScene(scene, null, 0);
+        randomException();
+    } catch(e) { }
+}
+
+for (var i = 0; i < 6; ++i)
+    renderScene();
diff --git a/LayoutTests/js/regress/v8-raytrace-with-empty-try-catch-expected.txt b/LayoutTests/js/regress/v8-raytrace-with-empty-try-catch-expected.txt
new file mode 100644 (file)
index 0000000..630703f
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/v8-raytrace-with-empty-try-catch
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/v8-raytrace-with-empty-try-catch.html b/LayoutTests/js/regress/v8-raytrace-with-empty-try-catch.html
new file mode 100644 (file)
index 0000000..eb1403e
--- /dev/null
@@ -0,0 +1,12 @@
+<!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/v8-raytrace-with-empty-try-catch.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/v8-raytrace-with-try-catch-expected.txt b/LayoutTests/js/regress/v8-raytrace-with-try-catch-expected.txt
new file mode 100644 (file)
index 0000000..c8c73ab
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/v8-raytrace-with-try-catch
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/v8-raytrace-with-try-catch.html b/LayoutTests/js/regress/v8-raytrace-with-try-catch.html
new file mode 100644 (file)
index 0000000..ffb3496
--- /dev/null
@@ -0,0 +1,12 @@
+<!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/v8-raytrace-with-try-catch.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
index 7a8de3d..5d9423c 100644 (file)
@@ -185,6 +185,7 @@ set(JavaScriptCore_SOURCES
     dfg/DFGFixupPhase.cpp
     dfg/DFGFlushFormat.cpp
     dfg/DFGFlushedAt.cpp
+    dfg/DFGLiveCatchVariablePreservationPhase.cpp
     dfg/DFGFrozenValue.cpp
     dfg/DFGFunctionWhitelist.cpp
     dfg/DFGGraph.cpp
index f5c22bb..3dbfbf6 100644 (file)
@@ -1,3 +1,211 @@
+2015-09-18  Saam barati  <sbarati@apple.com>
+
+        Implement try/catch in the DFG.
+        https://bugs.webkit.org/show_bug.cgi?id=147374
+
+        Reviewed by Filip Pizlo.
+
+        This patch implements try/catch inside the DFG JIT.
+        It also prevents tier up to the FTL for any functions
+        that have an op_catch in them that are DFG compiled.
+
+        This patch accomplishes implementing try/catch inside
+        the DFG by OSR exiting to op_catch when an exception is thrown.
+        We can OSR exit from an exception inside the DFG in two ways:
+        1) We have a JS call (can also be via implicit getter/setter in GetById/PutById)
+        2) We have an exception when returing from a callOperation
+
+        In the case of (1), we get to the OSR exit from genericUnwind because
+        the exception was thrown in a child call frame. This means these
+        OSR exits must act as defacto op_catches (even though we will still OSR
+        exit to a baseline op_catch). That means they must restore the stack pointer
+        and call frame.
+
+        In the case of (2), we can skip genericUnwind because we know the exception 
+        check will take us to a particular OSR exit. Instead, we link these
+        exception checks as jumps to a particular OSR exit.
+
+        Both types of OSR exits will exit into op_catch inside the baseline JIT.
+        Because they exit to op_catch, these OSR exits must set callFrameForCatch
+        to the proper call frame pointer.
+
+        We "handle" all exceptions inside the machine frame of the DFG code
+        block. This means the machine code block is responsible for "catching"
+        exceptions of any inlined frames' try/catch. OSR exit will then exit to 
+        the proper baseline CodeBlock after reifying the inlined frames
+        (DFG::OSRExit::m_codeOrigin corresponds to the op_catch we will exit to). 
+        Also, genericUnwind will never consult an inlined call frame's CodeBlock to 
+        see if they can catch the exception because they can't. We always unwind to the 
+        next machine code block frame. The DFG CodeBlock changes how the exception 
+        handler table is keyed: it is now keyed by CallSiteIndex for DFG code blocks. 
+
+        So, when consulting call sites that throw, we keep track of the CallSiteIndex,
+        and the HandlerInfo for the corresponding baseline exception handler for
+        that particular CallSiteIndex (if an exception at that call site will be caught). 
+        Then, when we're inside DFG::JITCompiler::link(), we install new HandlerInfo's
+        inside the DFG CodeBlock and key it by the corresponding CallSiteIndex.
+        (The CodeBlock only has HandlerInfos for the OSR exits that are to be arrived
+        at from genericUnwind).
+
+        Also, each OSR exit will know if it acting as an exception handler, and
+        whether or not it will be arrived at from genericUnwind. When we know we 
+        will arrive at an OSR exit from genericUnwind, we set the corresponding 
+        HandlerInfo's nativeCode CodeLocationLabel field to be the OSR exit.
+
+        This patch also introduces a new Phase inside the DFG that ensures
+        that DFG CodeBlocks that handle exceptions take the necessary
+        steps to keep live variables at "op_catch" live according the
+        OSR exit value recovery machinery. We accomplish this by flushing
+        all live op_catch variables to the stack when inside a "try" block.
+
+        * CMakeLists.txt:
+        * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
+        * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::handlerForBytecodeOffset):
+        (JSC::CodeBlock::handlerForIndex):
+        * bytecode/CodeBlock.h:
+        (JSC::CodeBlock::clearExceptionHandlers):
+        (JSC::CodeBlock::appendExceptionHandler):
+        * bytecode/PreciseJumpTargets.cpp:
+        (JSC::computePreciseJumpTargets):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::getLocal):
+        (JSC::DFG::ByteCodeParser::setLocal):
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * dfg/DFGCapabilities.cpp:
+        (JSC::DFG::capabilityLevel):
+        * dfg/DFGCommonData.cpp:
+        (JSC::DFG::CommonData::addCodeOrigin):
+        (JSC::DFG::CommonData::lastCallSite):
+        (JSC::DFG::CommonData::shrinkToFit):
+        * dfg/DFGCommonData.h:
+        * dfg/DFGGraph.h:
+        * dfg/DFGJITCompiler.cpp:
+        (JSC::DFG::JITCompiler::linkOSRExits):
+        (JSC::DFG::JITCompiler::link):
+        (JSC::DFG::JITCompiler::compile):
+        (JSC::DFG::JITCompiler::noticeOSREntry):
+        (JSC::DFG::JITCompiler::appendExceptionHandlingOSRExit):
+        (JSC::DFG::JITCompiler::willCatchExceptionInMachineFrame):
+        (JSC::DFG::JITCompiler::exceptionCheck):
+        (JSC::DFG::JITCompiler::recordCallSiteAndGenerateExceptionHandlingOSRExitIfNeeded):
+        * dfg/DFGJITCompiler.h:
+        (JSC::DFG::JITCompiler::emitStoreCodeOrigin):
+        (JSC::DFG::JITCompiler::emitStoreCallSiteIndex):
+        (JSC::DFG::JITCompiler::appendCall):
+        (JSC::DFG::JITCompiler::exceptionCheckWithCallFrameRollback):
+        (JSC::DFG::JITCompiler::blockHeads):
+        (JSC::DFG::JITCompiler::exceptionCheck): Deleted.
+        * dfg/DFGLiveCatchVariablePreservationPhase.cpp: Added.
+        (JSC::DFG::FlushLiveCatchVariablesInsertionPhase::FlushLiveCatchVariablesInsertionPhase):
+        (JSC::DFG::FlushLiveCatchVariablesInsertionPhase::run):
+        (JSC::DFG::FlushLiveCatchVariablesInsertionPhase::willCatchException):
+        (JSC::DFG::FlushLiveCatchVariablesInsertionPhase::handleBlock):
+        (JSC::DFG::FlushLiveCatchVariablesInsertionPhase::newVariableAccessData):
+        (JSC::DFG::performLiveCatchVariablePreservationPhase):
+        * dfg/DFGLiveCatchVariablePreservationPhase.h: Added.
+        * dfg/DFGOSRExit.cpp:
+        (JSC::DFG::OSRExit::OSRExit):
+        (JSC::DFG::OSRExit::setPatchableCodeOffset):
+        * dfg/DFGOSRExit.h:
+        (JSC::DFG::OSRExit::considerAddingAsFrequentExitSite):
+        * dfg/DFGOSRExitCompiler.cpp:
+        * dfg/DFGOSRExitCompiler32_64.cpp:
+        (JSC::DFG::OSRExitCompiler::compileExit):
+        * dfg/DFGOSRExitCompiler64.cpp:
+        (JSC::DFG::OSRExitCompiler::compileExit):
+        * dfg/DFGOSRExitCompilerCommon.cpp:
+        (JSC::DFG::osrWriteBarrier):
+        (JSC::DFG::adjustAndJumpToTarget):
+        * dfg/DFGOSRExitCompilerCommon.h:
+        * dfg/DFGPlan.cpp:
+        (JSC::DFG::Plan::compileInThreadImpl):
+        * dfg/DFGSlowPathGenerator.h:
+        (JSC::DFG::SlowPathGenerator::SlowPathGenerator):
+        (JSC::DFG::SlowPathGenerator::~SlowPathGenerator):
+        (JSC::DFG::SlowPathGenerator::generate):
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::cachedGetById):
+        (JSC::DFG::SpeculativeJIT::cachedPutById):
+        (JSC::DFG::SpeculativeJIT::emitCall):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::cachedGetById):
+        (JSC::DFG::SpeculativeJIT::cachedPutById):
+        (JSC::DFG::SpeculativeJIT::emitCall):
+        * dfg/DFGTierUpCheckInjectionPhase.cpp:
+        (JSC::DFG::TierUpCheckInjectionPhase::run):
+        * ftl/FTLOSRExitCompiler.cpp:
+        (JSC::FTL::compileStub):
+        * interpreter/Interpreter.cpp:
+        (JSC::GetCatchHandlerFunctor::operator()):
+        (JSC::UnwindFunctor::operator()):
+        * interpreter/StackVisitor.cpp:
+        (JSC::StackVisitor::gotoNextFrame):
+        (JSC::StackVisitor::unwindToMachineCodeBlockFrame):
+        (JSC::StackVisitor::readFrame):
+        * interpreter/StackVisitor.h:
+        (JSC::StackVisitor::operator*):
+        (JSC::StackVisitor::operator->):
+        * jit/AssemblyHelpers.cpp:
+        (JSC::AssemblyHelpers::emitExceptionCheck):
+        (JSC::AssemblyHelpers::emitNonPatchableExceptionCheck):
+        (JSC::AssemblyHelpers::emitStoreStructureWithTypeInfo):
+        * jit/AssemblyHelpers.h:
+        (JSC::AssemblyHelpers::emitCount):
+        * jit/JITExceptions.cpp:
+        (JSC::genericUnwind):
+        * jit/JITOpcodes.cpp:
+        (JSC::JIT::emit_op_catch):
+        * jit/JITOpcodes32_64.cpp:
+        (JSC::JIT::emit_op_catch):
+        * llint/LowLevelInterpreter32_64.asm:
+        * llint/LowLevelInterpreter64.asm:
+        * runtime/VM.cpp:
+        (JSC::VM::VM):
+        * runtime/VM.h:
+        (JSC::VM::clearException):
+        (JSC::VM::clearLastException):
+        (JSC::VM::addressOfCallFrameForCatch):
+        (JSC::VM::exception):
+        (JSC::VM::addressOfException):
+        * tests/stress/dfg-exception-try-catch-in-constructor-with-inlined-throw.js: Added.
+        (f):
+        (bar):
+        (Foo):
+        * tests/stress/es6-for-of-loop-exception.js: Added.
+        (assert):
+        (shouldThrowInvalidConstAssignment):
+        (baz):
+        (foo):
+        * tests/stress/exception-dfg-inlined-frame-not-strict-equal.js: Added.
+        (assert):
+        (o.valueOf):
+        (o.toString):
+        (read):
+        (bar):
+        (foo):
+        * tests/stress/exception-dfg-not-strict-equal.js: Added.
+        (foo):
+        (o.valueOf):
+        (o.toString):
+        (assert):
+        (shouldDoSomethingInFinally):
+        (catch):
+        * tests/stress/exception-dfg-operation-read-value.js: Added.
+        (assert):
+        (o.valueOf):
+        (o.toString):
+        (read):
+        (foo):
+        * tests/stress/exception-dfg-throw-from-catch-block.js: Added.
+        (assert):
+        (baz):
+        (bar):
+        (foo):
+
 2015-09-18  Sukolsak Sakshuwong  <sukolsak@gmail.com>
 
         Implement linear memory instructions in WebAssembly
index d3fd4b2..3de93e4 100644 (file)
     <ClCompile Include="..\dfg\DFGFixupPhase.cpp" />
     <ClCompile Include="..\dfg\DFGFlushedAt.cpp" />
     <ClCompile Include="..\dfg\DFGFlushFormat.cpp" />
+    <ClCompile Include="..\dfg\DFGLiveCatchVariablePreservationPhase.cpp" />
     <ClCompile Include="..\dfg\DFGFrozenValue.cpp" />
     <ClCompile Include="..\dfg\DFGFunctionWhitelist.cpp" />
     <ClCompile Include="..\dfg\DFGGraph.cpp" />
     <ClInclude Include="..\dfg\DFGFixupPhase.h" />
     <ClInclude Include="..\dfg\DFGFlushedAt.h" />
     <ClInclude Include="..\dfg\DFGFlushFormat.h" />
+    <ClInclude Include="..\dfg\DFGLiveCatchVariablePreservationPhase.h" />
     <ClInclude Include="..\dfg\DFGForAllKills.h" />
     <ClInclude Include="..\dfg\DFGFPRInfo.h" />
     <ClInclude Include="..\dfg\DFGFrozenValue.h" />
index bc8431c..d8a8373 100644 (file)
     <ClCompile Include="..\dfg\DFGFlushFormat.cpp">
       <Filter>dfg</Filter>
     </ClCompile>
+    <ClCompile Include="..\dfg\DFGLiveCatchVariablePreservationPhase.cpp">
+      <Filter>dfg</Filter>
+    </ClCompile>
     <ClCompile Include="..\dfg\DFGFunctionWhitelist.cpp">
       <Filter>dfg</Filter>
     </ClCompile>
     <ClInclude Include="..\dfg\DFGFlushFormat.h">
       <Filter>dfg</Filter>
     </ClInclude>
+    <ClInclude Include="..\dfg\DFGLiveCatchVariablePreservationPhase.h">
+      <Filter>dfg</Filter>
+    </ClInclude>
     <ClInclude Include="..\dfg\DFGFPRInfo.h">
       <Filter>dfg</Filter>
     </ClInclude>
index 947bee6..6f7c08e 100644 (file)
                7964656A1B952FF0003059EE /* GetPutInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 796465681B952FF0003059EE /* GetPutInfo.h */; settings = {ATTRIBUTES = (Private, ); }; };
                797E07A91B8FCFB9008400BA /* JSGlobalLexicalEnvironment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 797E07A71B8FCFB9008400BA /* JSGlobalLexicalEnvironment.cpp */; };
                797E07AA1B8FCFB9008400BA /* JSGlobalLexicalEnvironment.h in Headers */ = {isa = PBXBuildFile; fileRef = 797E07A81B8FCFB9008400BA /* JSGlobalLexicalEnvironment.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               79C4B15D1BA2158F00FD592E /* DFGLiveCatchVariablePreservationPhase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 79C4B15B1BA2158F00FD592E /* DFGLiveCatchVariablePreservationPhase.cpp */; };
+               79C4B15E1BA2158F00FD592E /* DFGLiveCatchVariablePreservationPhase.h in Headers */ = {isa = PBXBuildFile; fileRef = 79C4B15C1BA2158F00FD592E /* DFGLiveCatchVariablePreservationPhase.h */; settings = {ATTRIBUTES = (Private, ); }; };
                79EE0BFF1B4AFB85000385C9 /* VariableEnvironment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 79EE0BFD1B4AFB85000385C9 /* VariableEnvironment.cpp */; };
                79EE0C001B4AFB85000385C9 /* VariableEnvironment.h in Headers */ = {isa = PBXBuildFile; fileRef = 79EE0BFE1B4AFB85000385C9 /* VariableEnvironment.h */; settings = {ATTRIBUTES = (Private, ); }; };
                79F8FC1E1B9FED0F00CA66AB /* DFGMaximalFlushInsertionPhase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 79F8FC1C1B9FED0F00CA66AB /* DFGMaximalFlushInsertionPhase.cpp */; };
                796465681B952FF0003059EE /* GetPutInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GetPutInfo.h; sourceTree = "<group>"; };
                797E07A71B8FCFB9008400BA /* JSGlobalLexicalEnvironment.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSGlobalLexicalEnvironment.cpp; sourceTree = "<group>"; };
                797E07A81B8FCFB9008400BA /* JSGlobalLexicalEnvironment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSGlobalLexicalEnvironment.h; sourceTree = "<group>"; };
+               79C4B15B1BA2158F00FD592E /* DFGLiveCatchVariablePreservationPhase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGLiveCatchVariablePreservationPhase.cpp; path = dfg/DFGLiveCatchVariablePreservationPhase.cpp; sourceTree = "<group>"; };
+               79C4B15C1BA2158F00FD592E /* DFGLiveCatchVariablePreservationPhase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGLiveCatchVariablePreservationPhase.h; path = dfg/DFGLiveCatchVariablePreservationPhase.h; sourceTree = "<group>"; };
                79EE0BFD1B4AFB85000385C9 /* VariableEnvironment.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VariableEnvironment.cpp; sourceTree = "<group>"; };
                79EE0BFE1B4AFB85000385C9 /* VariableEnvironment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VariableEnvironment.h; sourceTree = "<group>"; };
                79F8FC1C1B9FED0F00CA66AB /* DFGMaximalFlushInsertionPhase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGMaximalFlushInsertionPhase.cpp; path = dfg/DFGMaximalFlushInsertionPhase.cpp; sourceTree = "<group>"; };
                                0F9D339517FFC4E60073C2BC /* DFGFlushedAt.h */,
                                A7D89CE817A0B8CC00773AD8 /* DFGFlushFormat.cpp */,
                                A7D89CE917A0B8CC00773AD8 /* DFGFlushFormat.h */,
+                               79C4B15B1BA2158F00FD592E /* DFGLiveCatchVariablePreservationPhase.cpp */,
+                               79C4B15C1BA2158F00FD592E /* DFGLiveCatchVariablePreservationPhase.h */,
                                0F2DD8101AB3D8BE00BBB8E8 /* DFGForAllKills.h */,
                                0F69CC86193AC60A0045759E /* DFGFrozenValue.cpp */,
                                0F69CC87193AC60A0045759E /* DFGFrozenValue.h */,
                                A1A009C11831A26E00CF8711 /* ARM64Assembler.h in Headers */,
                                0F898F321B27689F0083A33C /* DFGIntegerRangeOptimizationPhase.h in Headers */,
                                86D3B2C410156BDE002865E7 /* ARMAssembler.h in Headers */,
+                               79C4B15E1BA2158F00FD592E /* DFGLiveCatchVariablePreservationPhase.h in Headers */,
                                79F8FC1F1B9FED0F00CA66AB /* DFGMaximalFlushInsertionPhase.h in Headers */,
                                99F1A7011B98FBEC00463B26 /* InspectorFrontendRouter.h in Headers */,
                                7964656A1B952FF0003059EE /* GetPutInfo.h in Headers */,
                                A1587D711B4DC14100D69849 /* IntlDateTimeFormatPrototype.cpp in Sources */,
                                0F2B670A17B6B5AB00A7AE3F /* TypedArrayType.cpp in Sources */,
                                52C952B919A28A1C0069B386 /* TypeProfiler.cpp in Sources */,
+                               79C4B15D1BA2158F00FD592E /* DFGLiveCatchVariablePreservationPhase.cpp in Sources */,
                                0FF4274A158EBE91004CB9FF /* udis86.c in Sources */,
                                0FF42740158EBE8B004CB9FF /* udis86_decode.c in Sources */,
                                0FF42743158EBE91004CB9FF /* udis86_input.c in Sources */,
index 56676db..66525cb 100644 (file)
@@ -2857,7 +2857,11 @@ bool CodeBlock::hasOptimizedReplacement()
 HandlerInfo* CodeBlock::handlerForBytecodeOffset(unsigned bytecodeOffset, RequiredHandler requiredHandler)
 {
     RELEASE_ASSERT(bytecodeOffset < instructions().size());
+    return handlerForIndex(bytecodeOffset, requiredHandler);
+}
 
+HandlerInfo* CodeBlock::handlerForIndex(unsigned index, RequiredHandler requiredHandler)
+{
     if (!m_rareData)
         return 0;
     
@@ -2869,7 +2873,8 @@ HandlerInfo* CodeBlock::handlerForBytecodeOffset(unsigned bytecodeOffset, Requir
 
         // Handlers are ordered innermost first, so the first handler we encounter
         // that contains the source address is the correct handler to use.
-        if (handler.start <= bytecodeOffset && handler.end > bytecodeOffset)
+        // This index used is either the BytecodeOffset or a CallSiteIndex.
+        if (handler.start <= index && handler.end > index)
             return &handler;
     }
 
index 2310bb5..fdfdf5d 100644 (file)
@@ -195,6 +195,7 @@ public:
         AnyHandler
     };
     HandlerInfo* handlerForBytecodeOffset(unsigned bytecodeOffset, RequiredHandler = RequiredHandler::AnyHandler);
+    HandlerInfo* handlerForIndex(unsigned, RequiredHandler = RequiredHandler::AnyHandler);
     unsigned lineNumberForBytecodeOffset(unsigned bytecodeOffset);
     unsigned columnNumberForBytecodeOffset(unsigned bytecodeOffset);
     void expressionRangeForBytecodeOffset(unsigned bytecodeOffset, int& divot,
@@ -910,6 +911,18 @@ public:
         EvalCodeCache m_evalCodeCache;
     };
 
+    void clearExceptionHandlers()
+    {
+        if (m_rareData)
+            m_rareData->m_exceptionHandlers.clear();
+    }
+
+    void appendExceptionHandler(const HandlerInfo& handler)
+    {
+        createRareDataIfNecessary(); // We may be handling the exception of an inlined call frame.
+        m_rareData->m_exceptionHandlers.append(handler);
+    }
+
 protected:
     virtual void visitWeakReferences(SlotVisitor&) override;
     virtual void finalizeUnconditionally() override;
index 414dfd9..d60fdf7 100644 (file)
@@ -93,9 +93,12 @@ void computePreciseJumpTargets(CodeBlock* codeBlock, Vector<unsigned, 32>& out)
     if (!codeBlock->numberOfJumpTargets())
         return;
     
-    for (unsigned i = codeBlock->numberOfExceptionHandlers(); i--;)
+    for (unsigned i = codeBlock->numberOfExceptionHandlers(); i--;) {
         out.append(codeBlock->exceptionHandler(i).target);
-    
+        out.append(codeBlock->exceptionHandler(i).start);
+        out.append(codeBlock->exceptionHandler(i).end);
+    }
+
     Interpreter* interpreter = codeBlock->vm()->interpreter;
     Instruction* instructionsBegin = codeBlock->instructions().begin();
     unsigned instructionCount = codeBlock->instructions().size();
index 9fbd80c..aa04361 100644 (file)
@@ -434,7 +434,6 @@ private:
         m_currentBlock->variablesAtTail.local(local) = node;
         return node;
     }
-
     Node* setLocal(const CodeOrigin& semanticOrigin, VirtualRegister operand, Node* value, SetMode setMode = NormalSet)
     {
         CodeOrigin oldSemanticOrigin = m_currentSemanticOrigin;
@@ -3780,6 +3779,10 @@ bool ByteCodeParser::parseBlock(unsigned limit)
             flushForTerminal();
             addToGraph(Unreachable);
             LAST_OPCODE(op_throw_static_error);
+
+        case op_catch:
+            m_graph.m_hasExceptionHandlers = true;
+            NEXT_OPCODE(op_catch);
             
         case op_call:
             handleCall(currentInstruction, Call, CodeForCall);
index 34d8842..f8143b3 100644 (file)
@@ -211,6 +211,7 @@ CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, Instruc
     case op_new_arrow_func_exp:
     case op_create_lexical_environment:
     case op_get_parent_scope:
+    case op_catch:
         return CanCompileAndInline;
 
     case op_put_to_scope: {
index 30d74d7..8cd28e2 100644 (file)
@@ -57,6 +57,12 @@ CallSiteIndex CommonData::addCodeOrigin(CodeOrigin codeOrigin)
     return CallSiteIndex(index);
 }
 
+CallSiteIndex CommonData::lastCallSite() const
+{
+    ASSERT(codeOrigins.size());
+    return CallSiteIndex(codeOrigins.size() - 1);
+}
+
 void CommonData::shrinkToFit()
 {
     codeOrigins.shrinkToFit();
index 9e0193f..4be6013 100644 (file)
@@ -80,6 +80,7 @@ public:
     
     void notifyCompilingStructureTransition(Plan&, CodeBlock*, Node*);
     CallSiteIndex addCodeOrigin(CodeOrigin);
+    CallSiteIndex lastCallSite() const;
     
     void shrinkToFit();
     
index 46476d4..6977831 100644 (file)
@@ -875,6 +875,7 @@ public:
     PlanStage m_planStage { PlanStage::Initial };
     RefCountState m_refCountState;
     bool m_hasDebuggerEnabled;
+    bool m_hasExceptionHandlers { false };
 private:
     
     void handleSuccessor(Vector<BasicBlock*, 16>& worklist, BasicBlock*, BasicBlock* successor);
index 2f65ae7..13d2f44 100644 (file)
@@ -85,6 +85,15 @@ void JITCompiler::linkOSRExits()
             failureJumps.link(this);
         else
             info.m_replacementDestination = label();
+
+        if (exit.m_willArriveAtOSRExitFromGenericUnwind) {
+            // We are acting as a defacto op_catch because we arrive here from genericUnwind().
+            // So, we must restore our call frame and stack pointer.
+            restoreCalleeSavesFromVMCalleeSavesBuffer();
+            loadPtr(vm()->addressOfCallFrameForCatch(), GPRInfo::callFrameRegister);
+            addPtr(TrustedImm32(graph().stackPointerOffset() * sizeof(Register)), GPRInfo::callFrameRegister, stackPointerRegister);
+        }
+
         jitAssertHasValidCallFrame();
         store32(TrustedImm32(i), &vm()->osrExitIndex);
         exit.setPatchableCodeOffset(patchableJump());
@@ -283,9 +292,27 @@ void JITCompiler::link(LinkBuffer& linkBuffer)
         }
     } else
         ASSERT(!m_exitSiteLabels.size());
-    
+
     m_jitCode->common.compilation = m_graph.compilation();
     
+    // Link new DFG exception handlers and remove baseline JIT handlers.
+    m_codeBlock->clearExceptionHandlers();
+    for (unsigned  i = 0; i < m_exceptionHandlerOSRExitCallSites.size(); i++) {
+        OSRExitCompilationInfo& info = m_exceptionHandlerOSRExitCallSites[i].exitInfo;
+        if (info.m_replacementDestination.isSet()) {
+            // If this is is *not* set, it means that we already jumped to the OSR exit in pure generated control flow.
+            // i.e, we explicitly emitted an exceptionCheck that we know will be caught in this machine frame.
+            // If this *is set*, it means we will be landing at this code location from genericUnwind from an
+            // exception thrown in a child call frame.
+            CodeLocationLabel catchLabel = linkBuffer.locationOf(info.m_replacementDestination);
+            HandlerInfo newExceptionHandler = m_exceptionHandlerOSRExitCallSites[i].baselineExceptionHandler;
+            CallSiteIndex callSite = m_exceptionHandlerOSRExitCallSites[i].callSiteIndex;
+            newExceptionHandler.start = callSite.bits();
+            newExceptionHandler.end = callSite.bits() + 1;
+            newExceptionHandler.nativeCode = catchLabel;
+            m_codeBlock->appendExceptionHandler(newExceptionHandler);
+        }
+    }
 }
 
 void JITCompiler::compile()
@@ -337,7 +364,7 @@ void JITCompiler::compile()
     
     link(*linkBuffer);
     m_speculative->linkOSREntries(*linkBuffer);
-    
+
     m_jitCode->shrinkToFit();
     codeBlock()->shrinkToFit(CodeBlock::LateShrink);
 
@@ -522,6 +549,84 @@ void JITCompiler::noticeOSREntry(BasicBlock& basicBlock, JITCompiler::Label bloc
     entry->m_reshufflings.shrinkToFit();
 }
 
+void JITCompiler::appendExceptionHandlingOSRExit(unsigned eventStreamIndex, CodeOrigin opCatchOrigin, HandlerInfo* exceptionHandler, CallSiteIndex callSite, MacroAssembler::JumpList jumpsToFail)
+{
+    OSRExit exit(Uncountable, JSValueRegs(), graph().methodOfGettingAValueProfileFor(nullptr), m_speculative.get(), eventStreamIndex);
+    exit.m_willArriveAtOSRExitFromGenericUnwind = jumpsToFail.empty(); // If jumps are empty, we're going to jump here from genericUnwind from a child call frame.
+    exit.m_isExceptionHandler = true;
+    exit.m_codeOrigin = opCatchOrigin;
+    OSRExitCompilationInfo& exitInfo = appendExitInfo(jumpsToFail);
+    jitCode()->appendOSRExit(exit);
+    m_exceptionHandlerOSRExitCallSites.append(ExceptionHandlingOSRExitInfo { exitInfo, *exceptionHandler, callSite });
+}
+
+bool JITCompiler::willCatchExceptionInMachineFrame(CodeOrigin codeOrigin, CodeOrigin& opCatchOriginOut, HandlerInfo*& catchHandlerOut)
+{
+    unsigned bytecodeIndexToCheck = codeOrigin.bytecodeIndex;
+    while (1) {
+        InlineCallFrame* inlineCallFrame = codeOrigin.inlineCallFrame;
+        CodeBlock* codeBlock = m_graph.baselineCodeBlockFor(inlineCallFrame);
+        if (HandlerInfo* handler = codeBlock->handlerForBytecodeOffset(bytecodeIndexToCheck)) {
+            opCatchOriginOut = CodeOrigin(handler->target, inlineCallFrame);
+            catchHandlerOut = handler;
+            return true;
+        }
+
+        if (!inlineCallFrame)
+            return false;
+
+        bytecodeIndexToCheck = inlineCallFrame->caller.bytecodeIndex;
+        codeOrigin = codeOrigin.inlineCallFrame->caller;
+    }
+
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+void JITCompiler::exceptionCheck()
+{
+    // It's important that we use origin.forExit here. Consider if we hoist string
+    // addition outside a loop, and that we exit at the point of that concatenation
+    // from an out of memory exception.
+    // If the original loop had a try/catch around string concatenation, if we "catch"
+    // that exception inside the loop, then the loops induction variable will be undefined 
+    // in the OSR exit value recovery. It's more defensible for the string concatenation, 
+    // then, to not be caught by the for loops' try/catch.
+    // Here is the program I'm speaking about:
+    //
+    // >>>> lets presume "c = a + b" gets hoisted here.
+    // for (var i = 0; i < length; i++) {
+    //     try {
+    //         c = a + b
+    //     } catch(e) { 
+    //         If we threw an out of memory error, and we cought the exception
+    //         right here, then "i" would almost certainly be undefined, which
+    //         would make no sense.
+    //         ... 
+    //     }
+    // }
+    CodeOrigin opCatchOrigin;
+    HandlerInfo* exceptionHandler;
+    bool willCatchException = willCatchExceptionInMachineFrame(m_speculative->m_currentNode->origin.forExit, opCatchOrigin, exceptionHandler); 
+    if (willCatchException) {
+        unsigned streamIndex = m_speculative->m_outOfLineStreamIndex != UINT_MAX ? m_speculative->m_outOfLineStreamIndex : m_speculative->m_stream->size();
+        MacroAssembler::Jump hadException = emitNonPatchableExceptionCheck();
+        // We assume here that this is called after callOpeartion()/appendCall() is called.
+        appendExceptionHandlingOSRExit(streamIndex, opCatchOrigin, exceptionHandler, m_jitCode->common.lastCallSite(), hadException);
+    } else
+        m_exceptionChecks.append(emitExceptionCheck());
+}
+
+CallSiteIndex JITCompiler::recordCallSiteAndGenerateExceptionHandlingOSRExitIfNeeded(const CodeOrigin& callSiteCodeOrigin, unsigned eventStreamIndex)
+{
+    CodeOrigin opCatchOrigin;
+    HandlerInfo* exceptionHandler;
+    bool willCatchException = willCatchExceptionInMachineFrame(callSiteCodeOrigin, opCatchOrigin, exceptionHandler);
+    CallSiteIndex callSite = addCallSite(callSiteCodeOrigin);
+    if (willCatchException)
+        appendExceptionHandlingOSRExit(eventStreamIndex, opCatchOrigin, exceptionHandler, callSite);
+    return callSite;
+}
+
 } } // namespace JSC::DFG
 
 #endif // ENABLE(DFG_JIT)
index 7db7426..3d45700 100644 (file)
@@ -38,6 +38,7 @@
 #include "DFGRegisterBank.h"
 #include "FPRInfo.h"
 #include "GPRInfo.h"
+#include "HandlerInfo.h"
 #include "JITCode.h"
 #include "JITInlineCacheGenerator.h"
 #include "LinkBuffer.h"
@@ -158,6 +159,11 @@ public:
     void emitStoreCodeOrigin(CodeOrigin codeOrigin)
     {
         CallSiteIndex callSite = addCallSite(codeOrigin);
+        emitStoreCallSiteIndex(callSite);
+    }
+
+    void emitStoreCallSiteIndex(CallSiteIndex callSite)
+    {
         store32(TrustedImm32(callSite.bits()), tagFor(static_cast<VirtualRegister>(JSStack::ArgumentCount)));
     }
 
@@ -169,10 +175,7 @@ public:
         return functionCall;
     }
     
-    void exceptionCheck()
-    {
-        m_exceptionChecks.append(emitExceptionCheck());
-    }
+    void exceptionCheck();
 
     void exceptionCheckWithCallFrameRollback()
     {
@@ -260,6 +263,8 @@ public:
     
     Vector<Label>& blockHeads() { return m_blockHeads; }
 
+    CallSiteIndex recordCallSiteAndGenerateExceptionHandlingOSRExitIfNeeded(const CodeOrigin&, unsigned eventStreamIndex);
+
 private:
     friend class OSRExitJumpPlaceholder;
     
@@ -273,7 +278,10 @@ private:
     void compileExceptionHandlers();
     void linkOSRExits();
     void disassemble(LinkBuffer&);
-    
+
+    bool willCatchExceptionInMachineFrame(CodeOrigin, CodeOrigin& opCatchOriginOut, HandlerInfo*& catchHandlerOut);
+    void appendExceptionHandlingOSRExit(unsigned eventStreamIndex, CodeOrigin, HandlerInfo* exceptionHandler, CallSiteIndex, MacroAssembler::JumpList jumpsToFail = MacroAssembler::JumpList());
+
     // The dataflow graph currently being generated.
     Graph& m_graph;
 
@@ -311,6 +319,13 @@ private:
     SegmentedVector<OSRExitCompilationInfo, 4> m_exitCompilationInfo;
     Vector<Vector<Label>> m_exitSiteLabels;
     
+    struct ExceptionHandlingOSRExitInfo {
+        OSRExitCompilationInfo& exitInfo;
+        HandlerInfo baselineExceptionHandler;
+        CallSiteIndex callSiteIndex;
+    };
+    Vector<ExceptionHandlingOSRExitInfo> m_exceptionHandlerOSRExitCallSites;
+    
     Call m_callArityFixup;
     Label m_arityCheck;
     std::unique_ptr<SpeculativeJIT> m_speculative;
diff --git a/Source/JavaScriptCore/dfg/DFGLiveCatchVariablePreservationPhase.cpp b/Source/JavaScriptCore/dfg/DFGLiveCatchVariablePreservationPhase.cpp
new file mode 100644 (file)
index 0000000..da55554
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "DFGLiveCatchVariablePreservationPhase.h"
+
+#if ENABLE(DFG_JIT)
+
+#include "DFGBasicBlockInlines.h"
+#include "DFGGraph.h"
+#include "DFGInsertionSet.h"
+#include "DFGPhase.h"
+#include "FullBytecodeLiveness.h"
+#include "JSCInlines.h"
+
+namespace JSC { namespace DFG {
+
+class FlushLiveCatchVariablesInsertionPhase : public Phase {
+public:
+    FlushLiveCatchVariablesInsertionPhase(Graph& graph)
+        : Phase(graph, "live catch variable preservation phase")
+    {
+    }
+
+    bool run()
+    {
+        if (!m_graph.m_hasExceptionHandlers)
+            return true;
+
+        DFG_ASSERT(m_graph, nullptr, m_graph.m_form == LoadStore);
+
+        m_currentBlockLiveness.resize(m_graph.block(0)->variablesAtTail.numberOfLocals());
+
+        InsertionSet insertionSet(m_graph);
+        for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
+            handleBlock(block, insertionSet);
+            insertionSet.execute(block);
+        }
+
+        return true;
+    }
+
+    bool willCatchException(CodeOrigin origin)
+    {
+        unsigned bytecodeIndexToCheck = origin.bytecodeIndex;
+        m_currentBlockLiveness.clearAll();
+
+        while (1) {
+            InlineCallFrame* inlineCallFrame = origin.inlineCallFrame;
+            CodeBlock* codeBlock = m_graph.baselineCodeBlockFor(inlineCallFrame);
+            if (HandlerInfo* handler = codeBlock->handlerForBytecodeOffset(bytecodeIndexToCheck)) {
+                unsigned catchBytecodeIndex = handler->target;
+                m_graph.forAllLocalsLiveInBytecode(CodeOrigin(catchBytecodeIndex, inlineCallFrame), [&] (VirtualRegister operand) {
+                    m_currentBlockLiveness.set(operand.toLocal(), true); 
+                });
+                return true;
+            }
+
+            if (!inlineCallFrame)
+                return false;
+
+            bytecodeIndexToCheck = inlineCallFrame->caller.bytecodeIndex;
+            origin = inlineCallFrame->caller;
+        }
+    }
+
+    void handleBlock(BasicBlock* block, InsertionSet& insertionSet)
+    {
+        // Because precise jump targets ensures that the start of a "try" block is its
+        // own basic block, we will never have two "try" statements in the same DFG
+        // basic block. Therefore, checking the first node in the block is sufficient 
+        // to checking if we're in a try block.
+        if (!willCatchException(block->at(0)->origin.semantic))
+            return;
+
+        Operands<VariableAccessData*> currentBlockAccessData(block->variablesAtTail.numberOfArguments(), block->variablesAtTail.numberOfLocals(), nullptr);
+        HashSet<InlineCallFrame*> seenInlineCallFrames;
+
+        {
+            for (unsigned i = 0; i < block->size(); i++) {
+                Node* node = block->at(i);
+                bool isPrimordialSetArgument = node->op() == SetArgument && node->local().isArgument() && node == m_graph.m_arguments[node->local().toArgument()];
+                InlineCallFrame* inlineCallFrame = node->origin.semantic.inlineCallFrame;
+                if (inlineCallFrame)
+                    seenInlineCallFrames.add(inlineCallFrame);
+
+                if (node->op() == SetLocal || (node->op() == SetArgument && !isPrimordialSetArgument)) {
+                    VirtualRegister operand = node->local();
+
+                    int stackOffset = inlineCallFrame ? inlineCallFrame->stackOffset : 0;
+                    if ((operand.isLocal() && m_currentBlockLiveness.get(operand.toLocal()))
+                        || (operand.offset() == stackOffset + CallFrame::thisArgumentOffset())) {
+
+                        VariableAccessData* flushAccessData = currentBlockAccessData.operand(operand);
+                        if (!flushAccessData)
+                            flushAccessData = newVariableAccessData(operand);
+
+                        insertionSet.insertNode(i, SpecNone, 
+                            Flush, node->origin, OpInfo(flushAccessData));
+                    }
+                }
+
+                if (node->hasVariableAccessData(m_graph))
+                    currentBlockAccessData.operand(node->local()) = node->variableAccessData();
+            }
+        }
+
+        // Flush everything at the end of the block.
+        // FIXME: I think this will only be necessary if we have any successor
+        // blocks who aren't inside this "try" statement. If all our successor's
+        // are in this try statement, they will have Flushes for any live "catch"
+        // variables.
+        {
+            NodeOrigin origin = block->at(block->size() - 1)->origin;
+            auto insertFlushAtEnd = [&] (VirtualRegister operand, bool alwaysFlush) {
+                if ((operand.isLocal() && m_currentBlockLiveness.get(operand.toLocal())) 
+                    || operand.isArgument()
+                    || alwaysFlush) {
+                    VariableAccessData* accessData = currentBlockAccessData.operand(operand);
+                    if (!accessData)
+                        accessData = newVariableAccessData(operand);
+
+                    currentBlockAccessData.operand(operand) = accessData;
+
+                    insertionSet.insertNode(block->size(), SpecNone, 
+                        Flush, origin, OpInfo(accessData));
+                }
+            };
+            for (unsigned local = 0; local < block->variablesAtTail.numberOfLocals(); local++)
+                insertFlushAtEnd(virtualRegisterForLocal(local), false);
+            for (InlineCallFrame* inlineCallFrame : seenInlineCallFrames)
+                insertFlushAtEnd(VirtualRegister(inlineCallFrame->stackOffset + CallFrame::thisArgumentOffset()), true);
+            insertFlushAtEnd(VirtualRegister(CallFrame::thisArgumentOffset()), true);
+        }
+    }
+
+    VariableAccessData* newVariableAccessData(VirtualRegister operand)
+    {
+        ASSERT(!operand.isConstant());
+        
+        m_graph.m_variableAccessData.append(VariableAccessData(operand));
+        return &m_graph.m_variableAccessData.last();
+    }
+
+    FastBitVector m_currentBlockLiveness;
+};
+
+bool performLiveCatchVariablePreservationPhase(Graph& graph)
+{
+    SamplingRegion samplingRegion("DFG Live Catch Variables Preservation Phase");
+    return runPhase<FlushLiveCatchVariablesInsertionPhase>(graph);
+}
+
+} } // namespace JSC::DFG
+
+#endif // ENABLE(DFG_JIT)
diff --git a/Source/JavaScriptCore/dfg/DFGLiveCatchVariablePreservationPhase.h b/Source/JavaScriptCore/dfg/DFGLiveCatchVariablePreservationPhase.h
new file mode 100644 (file)
index 0000000..2491c9c
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef DFGLiveCatchVariablePreservationPhase_h
+#define DFGLiveCatchVariablePreservationPhase_h
+
+#if ENABLE(DFG_JIT)
+
+namespace JSC { namespace DFG {
+
+class Graph;
+
+// This phase ensures that we maintain liveness for locals
+// that are live in the "catch" block. Because a "catch"
+// block will not be in the control flow graph, we need to ensure
+// anything live inside the "catch" block in bytecode will maintain 
+// liveness inside the "try" block for an OSR exit from the "try" 
+// block into the "catch" block in the case of an exception being thrown.
+//
+// The mechanism currently used to demonstrate liveness to OSR exit
+// is ensuring all variables live in a "catch" are flushed to the
+// stack inside the "try" block.
+
+bool performLiveCatchVariablePreservationPhase(Graph&);
+
+} } // namespace JSC::DFG
+
+#endif // ENABLE(DFG_JIT)
+
+#endif // DFGLiveCatchVariablePreservationPhase_h
index 93bf1b0..867e233 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "AssemblyHelpers.h"
 #include "DFGGraph.h"
+#include "DFGMayExit.h"
 #include "DFGSpeculativeJIT.h"
 #include "JSCInlines.h"
 
@@ -42,8 +43,15 @@ OSRExit::OSRExit(ExitKind kind, JSValueSource jsValueSource, MethodOfGettingAVal
     , m_patchableCodeOffset(0)
     , m_recoveryIndex(recoveryIndex)
     , m_streamIndex(streamIndex)
+    , m_willArriveAtOSRExitFromGenericUnwind(false)
+    , m_isExceptionHandler(false)
 {
-    DFG_ASSERT(jit->m_jit.graph(), jit->m_currentNode, jit->m_origin.exitOK);
+    bool canExit = jit->m_origin.exitOK;
+    if (!canExit && jit->m_currentNode) {
+        ExitMode exitMode = mayExit(jit->m_jit.graph(), jit->m_currentNode);
+        canExit = exitMode == ExitMode::Exits || exitMode == ExitMode::ExitsForExceptions;
+    }
+    DFG_ASSERT(jit->m_jit.graph(), jit->m_currentNode, canExit);
 }
 
 void OSRExit::setPatchableCodeOffset(MacroAssembler::PatchableJump check)
index d336d04..13e7684 100644 (file)
@@ -93,13 +93,16 @@ struct OSRExit : public OSRExitBase {
     unsigned m_patchableCodeOffset;
     
     unsigned m_recoveryIndex;
-    
+
     void setPatchableCodeOffset(MacroAssembler::PatchableJump);
     MacroAssembler::Jump getPatchableCodeOffsetAsJump() const;
     CodeLocationJump codeLocationForRepatch(CodeBlock*) const;
     void correctJump(LinkBuffer&);
 
     unsigned m_streamIndex;
+
+    bool m_willArriveAtOSRExitFromGenericUnwind : 1;
+    bool m_isExceptionHandler : 1;
     
     void considerAddingAsFrequentExitSite(CodeBlock* profiledCodeBlock)
     {
index eefc063..3a81452 100644 (file)
@@ -115,10 +115,9 @@ void compileOSRExit(ExecState* exec)
     SamplingRegion samplingRegion("DFG OSR Exit Compilation");
     
     CodeBlock* codeBlock = exec->codeBlock();
-    
     ASSERT(codeBlock);
     ASSERT(codeBlock->jitType() == JITCode::DFGJIT);
-    
+
     VM* vm = &exec->vm();
     
     // It's sort of preferable that we don't GC while in here. Anyways, doing so wouldn't
@@ -128,6 +127,12 @@ void compileOSRExit(ExecState* exec)
     uint32_t exitIndex = vm->osrExitIndex;
     OSRExit& exit = codeBlock->jitCode()->dfg()->osrExit[exitIndex];
     
+    if (vm->callFrameForCatch)
+        ASSERT(exit.m_willArriveAtOSRExitFromGenericUnwind);
+    if (exit.m_isExceptionHandler)
+        ASSERT(!!vm->exception());
+        
+    
     prepareCodeOriginForOSRExit(exec, exit.m_codeOrigin);
     
     // Compute the value recoveries.
@@ -153,7 +158,7 @@ void compileOSRExit(ExecState* exec)
                 exit.m_kind, exit.m_kind == UncountableInvalidation);
             jit.add64(CCallHelpers::TrustedImm32(1), CCallHelpers::AbsoluteAddress(profilerExit->counterAddress()));
         }
-        
+
         exitCompiler.compileExit(exit, operands, recovery);
         
         LinkBuffer patchBuffer(*vm, jit, codeBlock);
index 3486883..01b8518 100644 (file)
@@ -248,6 +248,9 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov
     m_jit.emitRestoreCalleeSaves();
     m_jit.emitSaveCalleeSavesFor(m_jit.baselineCodeBlock());
 
+    if (exit.m_isExceptionHandler)
+        m_jit.copyCalleeSavesToVMCalleeSavesBuffer();
+
     // Do all data format conversions and store the results into the stack.
     
     for (size_t index = 0; index < operands.size(); ++index) {
@@ -394,7 +397,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov
     reifyInlinedCallFrames(m_jit, exit);
     
     // And finish.
-    adjustAndJumpToTarget(m_jit, exit);
+    adjustAndJumpToTarget(m_jit, exit, exit.m_isExceptionHandler);
 }
 
 } } // namespace JSC::DFG
index b225879..b223812 100644 (file)
@@ -245,7 +245,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov
             break;
         }
     }
-    
+
     // Need to ensure that the stack pointer accounts for the worst-case stack usage at exit. This
     // could toast some stack that the DFG used. We need to do it before storing to stack offsets
     // used by baseline.
@@ -261,6 +261,9 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov
     // The tag registers are needed to materialize recoveries below.
     m_jit.emitMaterializeTagCheckRegisters();
 
+    if (exit.m_isExceptionHandler)
+        m_jit.copyCalleeSavesToVMCalleeSavesBuffer();
+
     // Do all data format conversions and store the results into the stack.
     
     for (size_t index = 0; index < operands.size(); ++index) {
@@ -384,7 +387,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov
     reifyInlinedCallFrames(m_jit, exit);
 
     // And finish.
-    adjustAndJumpToTarget(m_jit, exit);
+    adjustAndJumpToTarget(m_jit, exit, exit.m_isExceptionHandler);
 }
 
 } } // namespace JSC::DFG
index b28cffa..68e0a82 100644 (file)
@@ -248,7 +248,7 @@ static void osrWriteBarrier(CCallHelpers& jit, GPRReg owner, GPRReg scratch)
 }
 #endif // ENABLE(GGC)
 
-void adjustAndJumpToTarget(CCallHelpers& jit, const OSRExitBase& exit)
+void adjustAndJumpToTarget(CCallHelpers& jit, const OSRExitBase& exit, bool isExitingToOpCatch)
 {
 #if ENABLE(GGC) 
     jit.move(AssemblyHelpers::TrustedImmPtr(jit.codeBlock()->ownerExecutable()), GPRInfo::argumentGPR1);
@@ -277,6 +277,10 @@ void adjustAndJumpToTarget(CCallHelpers& jit, const OSRExitBase& exit)
     void* jumpTarget = baselineCodeBlock->jitCode()->executableAddressAtOffset(mapping->m_machineCodeOffset);
 
     jit.addPtr(AssemblyHelpers::TrustedImm32(JIT::stackPointerOffsetFor(baselineCodeBlock) * sizeof(Register)), GPRInfo::callFrameRegister, AssemblyHelpers::stackPointerRegister);
+    if (isExitingToOpCatch) {
+        // Since we're jumping to op_catch, we need to set callFrameForCatch.
+        jit.storePtr(GPRInfo::callFrameRegister, jit.vm()->addressOfCallFrameForCatch());
+    }
     
     jit.move(AssemblyHelpers::TrustedImmPtr(jumpTarget), GPRInfo::regT2);
     jit.jump(GPRInfo::regT2);
index ce1836f..46b426d 100644 (file)
@@ -35,7 +35,7 @@ namespace JSC { namespace DFG {
 
 void handleExitCounts(CCallHelpers&, const OSRExitBase&);
 void reifyInlinedCallFrames(CCallHelpers&, const OSRExitBase&);
-void adjustAndJumpToTarget(CCallHelpers&, const OSRExitBase&);
+void adjustAndJumpToTarget(CCallHelpers&, const OSRExitBase&, bool isExitingToOpCatch);
 
 } } // namespace JSC::DFG
 
index ec1ff46..a98cd45 100644 (file)
@@ -48,6 +48,7 @@
 #include "DFGInvalidationPointInjectionPhase.h"
 #include "DFGJITCompiler.h"
 #include "DFGLICMPhase.h"
+#include "DFGLiveCatchVariablePreservationPhase.h"
 #include "DFGLivenessAnalysisPhase.h"
 #include "DFGLoopPreHeaderCreationPhase.h"
 #include "DFGMaximalFlushInsertionPhase.h"
@@ -259,6 +260,8 @@ Plan::CompilationPath Plan::compileInThreadImpl(LongLivedState& longLivedState)
         dfg.dump();
     }
 
+    performLiveCatchVariablePreservationPhase(dfg);
+
     if (Options::enableMaximalFlushInsertionPhase())
         performMaximalFlushInsertion(dfg);
     
index 16445ab..8cdbedc 100644 (file)
@@ -40,6 +40,8 @@ class SlowPathGenerator {
 public:
     SlowPathGenerator(SpeculativeJIT* jit)
         : m_currentNode(jit->m_currentNode)
+        , m_streamIndex(jit->m_stream->size())
+        , m_origin(jit->m_origin) 
     {
     }
     virtual ~SlowPathGenerator() { }
@@ -47,7 +49,10 @@ public:
     {
         m_label = jit->m_jit.label();
         jit->m_currentNode = m_currentNode;
+        jit->m_outOfLineStreamIndex = m_streamIndex;
+        jit->m_origin = m_origin;
         generateInternal(jit);
+        jit->m_outOfLineStreamIndex = UINT_MAX;
         if (!ASSERT_DISABLED)
             jit->m_jit.abortWithReason(DFGSlowPathGeneratorFellThrough);
     }
@@ -61,6 +66,8 @@ protected:
     virtual void generateInternal(SpeculativeJIT*) = 0;
     MacroAssembler::Label m_label;
     Node* m_currentNode;
+    unsigned m_streamIndex;
+    NodeOrigin m_origin;
 };
 
 template<typename JumpType>
index 9efdd54..77a3595 100644 (file)
@@ -2476,6 +2476,7 @@ public:
     
     Vector<std::unique_ptr<SlowPathGenerator>, 8> m_slowPathGenerators;
     Vector<SilentRegisterSavePlan> m_plans;
+    unsigned m_outOfLineStreamIndex { UINT_MAX };
 };
 
 
index daf62f5..50070f6 100644 (file)
@@ -182,8 +182,9 @@ void SpeculativeJIT::cachedGetById(
         basePayloadGPR = resultPayloadGPR;
     }
     
+    CallSiteIndex callSite = m_jit.recordCallSiteAndGenerateExceptionHandlingOSRExitIfNeeded(codeOrigin, m_stream->size());
     JITGetByIdGenerator gen(
-        m_jit.codeBlock(), codeOrigin, m_jit.addCallSite(codeOrigin), usedRegisters(),
+        m_jit.codeBlock(), codeOrigin, callSite, usedRegisters(),
         JSValueRegs(baseTagGPROrNone, basePayloadGPR),
         JSValueRegs(resultTagGPR, resultPayloadGPR), spillMode);
     
@@ -214,8 +215,9 @@ void SpeculativeJIT::cachedGetById(
 
 void SpeculativeJIT::cachedPutById(CodeOrigin codeOrigin, GPRReg basePayloadGPR, GPRReg valueTagGPR, GPRReg valuePayloadGPR, GPRReg scratchGPR, unsigned identifierNumber, PutKind putKind, JITCompiler::Jump slowPathTarget, SpillRegistersMode spillMode)
 {
+    CallSiteIndex callSite = m_jit.recordCallSiteAndGenerateExceptionHandlingOSRExitIfNeeded(codeOrigin, m_stream->size());
     JITPutByIdGenerator gen(
-        m_jit.codeBlock(), codeOrigin, m_jit.addCallSite(codeOrigin), usedRegisters(),
+        m_jit.codeBlock(), codeOrigin, callSite, usedRegisters(),
         JSValueRegs::payloadOnly(basePayloadGPR), JSValueRegs(valueTagGPR, valuePayloadGPR),
         scratchGPR, spillMode, m_jit.ecmaModeFor(codeOrigin), putKind);
     
@@ -784,7 +786,8 @@ void SpeculativeJIT::emitCall(Node* node)
     JITCompiler::DataLabelPtr targetToCheck;
     JITCompiler::JumpList slowPath;
 
-    m_jit.emitStoreCodeOrigin(node->origin.semantic);
+    CallSiteIndex callSite = m_jit.recordCallSiteAndGenerateExceptionHandlingOSRExitIfNeeded(node->origin.semantic, m_stream->size());
+    m_jit.emitStoreCallSiteIndex(callSite);
     
     CallLinkInfo* info = m_jit.codeBlock()->addCallLinkInfo();
 
index 11ec7a5..3c27297 100644 (file)
@@ -153,8 +153,10 @@ GPRReg SpeculativeJIT::fillJSValue(Edge edge)
 
 void SpeculativeJIT::cachedGetById(CodeOrigin codeOrigin, GPRReg baseGPR, GPRReg resultGPR, unsigned identifierNumber, JITCompiler::Jump slowPathTarget, SpillRegistersMode spillMode)
 {
+    CallSiteIndex callSite = m_jit.recordCallSiteAndGenerateExceptionHandlingOSRExitIfNeeded(codeOrigin, m_stream->size());
+
     JITGetByIdGenerator gen(
-        m_jit.codeBlock(), codeOrigin, m_jit.addCallSite(codeOrigin), usedRegisters(), JSValueRegs(baseGPR),
+        m_jit.codeBlock(), codeOrigin, callSite, usedRegisters(), JSValueRegs(baseGPR),
         JSValueRegs(resultGPR), spillMode);
     gen.generateFastPath(m_jit);
     
@@ -173,8 +175,10 @@ void SpeculativeJIT::cachedGetById(CodeOrigin codeOrigin, GPRReg baseGPR, GPRReg
 
 void SpeculativeJIT::cachedPutById(CodeOrigin codeOrigin, GPRReg baseGPR, GPRReg valueGPR, GPRReg scratchGPR, unsigned identifierNumber, PutKind putKind, JITCompiler::Jump slowPathTarget, SpillRegistersMode spillMode)
 {
+    CallSiteIndex callSite = m_jit.recordCallSiteAndGenerateExceptionHandlingOSRExitIfNeeded(codeOrigin, m_stream->size());
+
     JITPutByIdGenerator gen(
-        m_jit.codeBlock(), codeOrigin, m_jit.addCallSite(codeOrigin), usedRegisters(), JSValueRegs(baseGPR),
+        m_jit.codeBlock(), codeOrigin, callSite, usedRegisters(), JSValueRegs(baseGPR),
         JSValueRegs(valueGPR), scratchGPR, spillMode, m_jit.ecmaModeFor(codeOrigin), putKind);
 
     gen.generateFastPath(m_jit);
@@ -752,7 +756,8 @@ void SpeculativeJIT::emitCall(Node* node)
     JITCompiler::DataLabelPtr targetToCheck;
     JITCompiler::Jump slowPath;
 
-    m_jit.emitStoreCodeOrigin(node->origin.semantic);
+    CallSiteIndex callSite = m_jit.recordCallSiteAndGenerateExceptionHandlingOSRExitIfNeeded(node->origin.semantic, m_stream->size());
+    m_jit.emitStoreCallSiteIndex(callSite);
     
     CallLinkInfo* callLinkInfo = m_jit.codeBlock()->addCallLinkInfo();
     
index 5f509c4..038df9c 100644 (file)
@@ -52,6 +52,9 @@ public:
         
         if (m_graph.m_profiledBlock->m_didFailFTLCompilation)
             return false;
+
+        if (m_graph.m_hasExceptionHandlers)
+            return false;
         
 #if ENABLE(FTL_JIT)
         FTL::CapabilityLevel level = FTL::canCompile(m_graph);
index 94b37e5..39d0400 100644 (file)
@@ -488,7 +488,7 @@ static void compileStub(
     
     handleExitCounts(jit, exit);
     reifyInlinedCallFrames(jit, exit);
-    adjustAndJumpToTarget(jit, exit);
+    adjustAndJumpToTarget(jit, exit, false);
     
     LinkBuffer patchBuffer(*vm, jit, codeBlock);
     exit.m_code = FINALIZE_CODE_IF(
index 315cf49..64732a2 100644 (file)
@@ -614,12 +614,19 @@ public:
 
     StackVisitor::Status operator()(StackVisitor& visitor)
     {
+        visitor.unwindToMachineCodeBlockFrame();
+
         CodeBlock* codeBlock = visitor->codeBlock();
         if (!codeBlock)
             return StackVisitor::Continue;
 
-        unsigned bytecodeOffset = visitor->bytecodeOffset();
-        m_handler = codeBlock->handlerForBytecodeOffset(bytecodeOffset, CodeBlock::RequiredHandler::CatchHandler);
+        unsigned exceptionHandlerIndex;
+        if (codeBlock->jitType() != JITCode::DFGJIT)
+            exceptionHandlerIndex = visitor->callFrame()->bytecodeOffset();
+        else
+            exceptionHandlerIndex = visitor->callFrame()->callSiteIndex().bits();
+
+        m_handler = codeBlock->handlerForIndex(exceptionHandlerIndex, CodeBlock::RequiredHandler::CatchHandler);
         if (m_handler)
             return StackVisitor::Done;
 
@@ -642,15 +649,20 @@ public:
 
     StackVisitor::Status operator()(StackVisitor& visitor)
     {
+        visitor.unwindToMachineCodeBlockFrame();
         VM& vm = m_callFrame->vm();
         m_callFrame = visitor->callFrame();
         m_codeBlock = visitor->codeBlock();
-        unsigned bytecodeOffset = visitor->bytecodeOffset();
 
+        unsigned exceptionHandlerIndex;
+        if (m_codeBlock->jitType() != JITCode::DFGJIT)
+            exceptionHandlerIndex = m_callFrame->bytecodeOffset();
+        else
+            exceptionHandlerIndex = m_callFrame->callSiteIndex().bits();
         m_handler = nullptr;
         if (!m_isTermination) {
             if (m_codeBlock && !isWebAssemblyExecutable(m_codeBlock->ownerExecutable()))
-                m_handler = m_codeBlock->handlerForBytecodeOffset(bytecodeOffset);
+                m_handler = m_codeBlock->handlerForIndex(exceptionHandlerIndex);
         }
 
         if (m_handler)
index 75f1772..1e7f617 100644 (file)
@@ -69,6 +69,14 @@ void StackVisitor::gotoNextFrame()
     readFrame(m_frame.callerFrame());
 }
 
+void StackVisitor::unwindToMachineCodeBlockFrame()
+{
+#if ENABLE(DFG_JIT)
+    while (m_frame.isInlinedFrame())
+        gotoNextFrame();
+#endif
+}
+
 void StackVisitor::readFrame(CallFrame* callFrame)
 {
     if (!callFrame) {
index 41bfc70..bbf37fe 100644 (file)
@@ -131,6 +131,7 @@ public:
 
     Frame& operator*() { return m_frame; }
     ALWAYS_INLINE Frame* operator->() { return &m_frame; }
+    void unwindToMachineCodeBlockFrame();
 
 private:
     JS_EXPORT_PRIVATE StackVisitor(CallFrame* startFrame);
index 14528c3..a2394ac 100644 (file)
@@ -273,6 +273,20 @@ AssemblyHelpers::Jump AssemblyHelpers::emitExceptionCheck(ExceptionCheckKind kin
     return realJump.m_jump;
 }
 
+AssemblyHelpers::Jump AssemblyHelpers::emitNonPatchableExceptionCheck()
+{
+    callExceptionFuzz();
+
+    Jump result;
+#if USE(JSVALUE64)
+    result = branchTest64(NonZero, AbsoluteAddress(vm()->addressOfException()));
+#elif USE(JSVALUE32_64)
+    result = branch32(NotEqual, AbsoluteAddress(vm()->addressOfException()), TrustedImm32(0));
+#endif
+    
+    return result;
+}
+
 void AssemblyHelpers::emitStoreStructureWithTypeInfo(AssemblyHelpers& jit, TrustedImmPtr structure, RegisterID dest)
 {
     const Structure* structurePtr = static_cast<const Structure*>(structure.m_value);
index 8b914a5..0c2daaa 100644 (file)
@@ -1021,6 +1021,7 @@ public:
     enum ExceptionJumpWidth { NormalJumpWidth, FarJumpWidth };
     Jump emitExceptionCheck(
         ExceptionCheckKind = NormalExceptionCheck, ExceptionJumpWidth = NormalJumpWidth);
+    Jump emitNonPatchableExceptionCheck();
 
 #if ENABLE(SAMPLING_COUNTERS)
     static void emitCount(MacroAssembler& jit, AbstractSamplingCounter& counter, int32_t increment = 1)
index 9b0303b..276cffa 100644 (file)
@@ -54,7 +54,14 @@ void genericUnwind(VM* vm, ExecState* callFrame, UnwindStart unwindStart)
     void* catchRoutine;
     Instruction* catchPCForInterpreter = 0;
     if (handler) {
-        catchPCForInterpreter = &callFrame->codeBlock()->instructions()[handler->target];
+        // handler->target is meaningless for getting a code offset when catching
+        // the exception in a DFG frame. This bytecode target offset could be
+        // something that's in an inlined frame, which means an array access
+        // with this bytecode offset in the machine frame is utterly meaningless
+        // and can cause an overflow. OSR exit properly exits to handler->target
+        // in the proper frame.
+        if (callFrame->codeBlock()->jitType() != JITCode::DFGJIT)
+            catchPCForInterpreter = &callFrame->codeBlock()->instructions()[handler->target];
 #if ENABLE(JIT)
         catchRoutine = handler->nativeCode.executableAddress();
 #else
index b70627e..dfab6e2 100644 (file)
@@ -501,6 +501,7 @@ void JIT::emit_op_catch(Instruction* currentInstruction)
 
     move(TrustedImmPtr(m_vm), regT3);
     load64(Address(regT3, VM::callFrameForCatchOffset()), callFrameRegister);
+    storePtr(TrustedImmPtr(nullptr), Address(regT3, VM::callFrameForCatchOffset()));
 
     addPtr(TrustedImm32(stackPointerOffsetFor(codeBlock()) * sizeof(Register)), callFrameRegister, stackPointerRegister);
 
index 1b5e16b..9858817 100644 (file)
@@ -807,6 +807,7 @@ void JIT::emit_op_catch(Instruction* currentInstruction)
     move(TrustedImmPtr(m_vm), regT3);
     // operationThrow returns the callFrame for the handler.
     load32(Address(regT3, VM::callFrameForCatchOffset()), callFrameRegister);
+    storePtr(TrustedImmPtr(nullptr), Address(regT3, VM::callFrameForCatchOffset()));
 
     addPtr(TrustedImm32(stackPointerOffsetFor(codeBlock()) * sizeof(Register)), callFrameRegister, stackPointerRegister);
 
index 8497cdc..dd166a5 100644 (file)
@@ -304,6 +304,7 @@ _handleUncaughtException:
     loadp MarkedBlock::m_weakSet + WeakSet::m_vm[t3], t3
     restoreCalleeSavesFromVMCalleeSavesBuffer(t3, t0)
     loadp VM::callFrameForCatch[t3], cfr
+    storep 0, VM::callFrameForCatch[t3]
 
     loadp CallerFrame[cfr], cfr
 
@@ -1837,6 +1838,7 @@ _llint_op_catch:
     loadp MarkedBlock::m_weakSet + WeakSet::m_vm[t3], t3
     restoreCalleeSavesFromVMCalleeSavesBuffer(t3, t0)
     loadp VM::callFrameForCatch[t3], cfr
+    storep 0, VM::callFrameForCatch[t3]
     restoreStackPointerAfterCall()
 
     loadi VM::targetInterpreterPCForThrow[t3], PC
index 8327fe0..7ca1979 100644 (file)
@@ -276,6 +276,7 @@ _handleUncaughtException:
     loadp MarkedBlock::m_weakSet + WeakSet::m_vm[t3], t3
     restoreCalleeSavesFromVMCalleeSavesBuffer(t3, t0)
     loadp VM::callFrameForCatch[t3], cfr
+    storep 0, VM::callFrameForCatch[t3]
 
     loadp CallerFrame[cfr], cfr
     vmEntryRecord(cfr, t2)
@@ -1723,6 +1724,7 @@ _llint_op_catch:
     loadp MarkedBlock::m_weakSet + WeakSet::m_vm[t3], t3
     restoreCalleeSavesFromVMCalleeSavesBuffer(t3, t0)
     loadp VM::callFrameForCatch[t3], cfr
+    storep 0, VM::callFrameForCatch[t3]
     restoreStackPointerAfterCall()
 
     loadp CodeBlock[cfr], PB
index f89258c..6eeb25b 100644 (file)
@@ -282,6 +282,8 @@ VM::VM(VMType vmType, HeapType heapType)
         m_perBytecodeProfiler->registerToSaveAtExit(pathOut.toCString().data());
     }
 
+    callFrameForCatch = nullptr;
+
 #if ENABLE(DFG_JIT)
     if (canUseJIT())
         dfgState = std::make_unique<DFG::LongLivedState>();
index b85babf..384d58e 100644 (file)
@@ -407,6 +407,8 @@ public:
     void clearException() { m_exception = nullptr; }
     void clearLastException() { m_lastException = nullptr; }
 
+    ExecState** addressOfCallFrameForCatch() { return &callFrameForCatch; }
+
     Exception* exception() const { return m_exception; }
     JSCell** addressOfException() { return reinterpret_cast<JSCell**>(&m_exception); }
 
diff --git a/Source/JavaScriptCore/tests/stress/dfg-exception-try-catch-in-constructor-with-inlined-throw.js b/Source/JavaScriptCore/tests/stress/dfg-exception-try-catch-in-constructor-with-inlined-throw.js
new file mode 100644 (file)
index 0000000..1346cef
--- /dev/null
@@ -0,0 +1,27 @@
+function f() {
+    return 20; 
+}
+noInline(f);
+
+function bar(b) { 
+    if (b)
+        throw new Error("blah!");
+}
+
+function Foo(b) {
+    try {
+        this.value = bar(b);
+    } catch(e) {
+        this.value = e.toString();
+    }
+
+    f(this.value, b);
+}
+noInline(Foo);
+
+
+for (var i = 1; i < 1000; i++) {
+    let value = new Foo(i % 3 === 0);
+    if (i % 3 === 0 && value.value !==  "Error: blah!")
+        throw new Error("bad value: " + value.value);
+}
diff --git a/Source/JavaScriptCore/tests/stress/es6-for-of-loop-exception.js b/Source/JavaScriptCore/tests/stress/es6-for-of-loop-exception.js
new file mode 100644 (file)
index 0000000..093b756
--- /dev/null
@@ -0,0 +1,29 @@
+function assert(cond) {
+    if (!cond)
+        throw new Error("broke assertion");
+}
+noInline(assert);
+
+function shouldThrowInvalidConstAssignment(f) {
+    var threw = false;
+    try {
+        f();
+    } catch(e) {
+        //print(e);
+        if (e.name.indexOf("TypeError") !== -1 && e.message.indexOf("readonly") !== -1)
+            threw = true;
+    }
+    assert(threw);
+}
+noInline(shouldThrowInvalidConstAssignment);
+
+function baz(){}
+noInline(baz);
+
+function foo() {
+    for (const item of [1,2,3]) {
+        item = 20;
+    }
+}
+for (var i = 0; i < 1000; i++)
+    shouldThrowInvalidConstAssignment(foo);
diff --git a/Source/JavaScriptCore/tests/stress/exception-dfg-inlined-frame-not-strict-equal.js b/Source/JavaScriptCore/tests/stress/exception-dfg-inlined-frame-not-strict-equal.js
new file mode 100644 (file)
index 0000000..415b15d
--- /dev/null
@@ -0,0 +1,39 @@
+function assert(b) {
+    if (!b)
+        throw new Error("Bad assertion");
+}
+noInline(assert);
+var o = {
+    valueOf: function() { return {}; },
+    toString: function() { return {}; }
+};
+function read() {
+    return "read";
+}
+noInline(read);
+
+function bar(a, b) {
+    return a == b;
+}
+
+function foo(a, b) {
+    var result = null;
+    var value = read();
+    try {
+        result = bar(a, b);
+    } catch(e) {
+        assert("" + value === "read");
+    }
+    return value;
+}
+noInline(foo);
+
+for (var i = 0; i < 1000; i++) {
+    foo(10, 20);
+    foo({}, {});
+    foo(10, 10.0);
+    foo("hello", "hello");
+    foo(null, undefined);
+    foo(false, 0);
+}
+foo(o, "hello");
diff --git a/Source/JavaScriptCore/tests/stress/exception-dfg-not-strict-equal.js b/Source/JavaScriptCore/tests/stress/exception-dfg-not-strict-equal.js
new file mode 100644 (file)
index 0000000..e4dd480
--- /dev/null
@@ -0,0 +1,71 @@
+;(function () {
+function foo(a, b) {
+    var result = null;
+    try {
+        result = a == b;
+    } catch(e) {
+    }
+}
+noInline(foo);
+
+for (var i = 0; i < 1000; i++) {
+    foo(10, 20);
+    foo({}, {});
+    foo(10, 10.0);
+    foo("hello", "hello");
+    foo(null, undefined);
+    foo(false, 0);
+}
+
+var o = {
+    valueOf: function() { return {}; },
+    toString: function() { return {}; }
+};
+foo(o, "hello");
+})();
+
+
+function assert(b) {
+    if (!b)
+        throw new Error("Bad assertion");
+}
+noInline(assert);
+
+
+;(function() {
+
+var _shouldDoSomethingInFinally = false;
+function shouldDoSomethingInFinally() { return _shouldDoSomethingInFinally; }
+noInline(shouldDoSomethingInFinally);
+
+function foo(a, b) {
+    var result = null;
+    try {
+        result = a == b;
+    } finally {
+        if (shouldDoSomethingInFinally())
+            assert(result === null);
+    }
+    return result;
+}
+noInline(foo);
+
+for (var i = 0; i < 1000; i++) {
+    foo(10, 20);
+    foo({}, {});
+    foo(10, 10.0);
+    foo("hello", "hello");
+    foo(null, undefined);
+    foo(false, 0);
+}
+
+var o = {
+    valueOf: function() { return {}; },
+    toString: function() { return {}; }
+};
+try {
+    _shouldDoSomethingInFinally = true;
+    foo(o, "hello");
+} catch(e) {}
+
+})();
diff --git a/Source/JavaScriptCore/tests/stress/exception-dfg-operation-read-value.js b/Source/JavaScriptCore/tests/stress/exception-dfg-operation-read-value.js
new file mode 100644 (file)
index 0000000..f3835a0
--- /dev/null
@@ -0,0 +1,35 @@
+function assert(b) {
+    if (!b)
+        throw new Error("Bad assertion");
+}
+noInline(assert);
+var o = {
+    valueOf: function() { return {}; },
+    toString: function() { return {}; }
+};
+function read() {
+    return "read";
+}
+noInline(read);
+
+function foo(a, b) {
+    var result = null;
+    var value = read();
+    try {
+        result = a == b;
+    } catch(e) {
+        assert("" + value === "read");
+    }
+    return value;
+}
+noInline(foo);
+
+for (var i = 0; i < 1000; i++) {
+    foo(10, 20);
+    foo({}, {});
+    foo(10, 10.0);
+    foo("hello", "hello");
+    foo(null, undefined);
+    foo(false, 0);
+}
+foo(o, "hello");
diff --git a/Source/JavaScriptCore/tests/stress/exception-dfg-throw-from-catch-block.js b/Source/JavaScriptCore/tests/stress/exception-dfg-throw-from-catch-block.js
new file mode 100644 (file)
index 0000000..9464545
--- /dev/null
@@ -0,0 +1,40 @@
+"use strict";
+function assert(cond, m) {
+    if (!cond)
+        throw new Error("broke assertion: '" + m + "'");
+}
+noInline(assert);
+
+function baz(b) {
+    if (b)
+        throw new Error("Baz");
+}
+
+function bar(b) {
+    var exception = null;
+    try {
+        baz(b);
+    } catch(e) {
+        exception = e;
+        baz(b);
+    } finally {
+        if (b) 
+            assert(exception);
+    }
+}
+
+function foo(b) {
+    var exception = null;
+    try {
+        bar(b);
+    } catch(e) {
+        exception = e;
+    }
+    if (b)
+        assert(exception);
+}
+
+const NUM_LOOPS = 1000;
+for (var i = 0; i < NUM_LOOPS; i++) {
+    foo(i === NUM_LOOPS - 1);
+}