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
+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
--- /dev/null
+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
+
--- /dev/null
+<!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>
--- /dev/null
+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
+
--- /dev/null
+<!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>
--- /dev/null
+/*
+ * 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;
+
--- /dev/null
+/*
+ * 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;
+
--- /dev/null
+// 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();
--- /dev/null
+// 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();
--- /dev/null
+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
+
--- /dev/null
+<!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>
--- /dev/null
+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
+
--- /dev/null
+<!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>
dfg/DFGFixupPhase.cpp
dfg/DFGFlushFormat.cpp
dfg/DFGFlushedAt.cpp
+ dfg/DFGLiveCatchVariablePreservationPhase.cpp
dfg/DFGFrozenValue.cpp
dfg/DFGFunctionWhitelist.cpp
dfg/DFGGraph.cpp
+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
<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" />
<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>
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 */,
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;
// 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;
}
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,
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;
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();
m_currentBlock->variablesAtTail.local(local) = node;
return node;
}
-
Node* setLocal(const CodeOrigin& semanticOrigin, VirtualRegister operand, Node* value, SetMode setMode = NormalSet)
{
CodeOrigin oldSemanticOrigin = m_currentSemanticOrigin;
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);
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: {
return CallSiteIndex(index);
}
+CallSiteIndex CommonData::lastCallSite() const
+{
+ ASSERT(codeOrigins.size());
+ return CallSiteIndex(codeOrigins.size() - 1);
+}
+
void CommonData::shrinkToFit()
{
codeOrigins.shrinkToFit();
void notifyCompilingStructureTransition(Plan&, CodeBlock*, Node*);
CallSiteIndex addCodeOrigin(CodeOrigin);
+ CallSiteIndex lastCallSite() const;
void shrinkToFit();
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);
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());
}
} 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()
link(*linkBuffer);
m_speculative->linkOSREntries(*linkBuffer);
-
+
m_jitCode->shrinkToFit();
codeBlock()->shrinkToFit(CodeBlock::LateShrink);
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)
#include "DFGRegisterBank.h"
#include "FPRInfo.h"
#include "GPRInfo.h"
+#include "HandlerInfo.h"
#include "JITCode.h"
#include "JITInlineCacheGenerator.h"
#include "LinkBuffer.h"
void emitStoreCodeOrigin(CodeOrigin codeOrigin)
{
CallSiteIndex callSite = addCallSite(codeOrigin);
+ emitStoreCallSiteIndex(callSite);
+ }
+
+ void emitStoreCallSiteIndex(CallSiteIndex callSite)
+ {
store32(TrustedImm32(callSite.bits()), tagFor(static_cast<VirtualRegister>(JSStack::ArgumentCount)));
}
return functionCall;
}
- void exceptionCheck()
- {
- m_exceptionChecks.append(emitExceptionCheck());
- }
+ void exceptionCheck();
void exceptionCheckWithCallFrameRollback()
{
Vector<Label>& blockHeads() { return m_blockHeads; }
+ CallSiteIndex recordCallSiteAndGenerateExceptionHandlingOSRExitIfNeeded(const CodeOrigin&, unsigned eventStreamIndex);
+
private:
friend class OSRExitJumpPlaceholder;
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;
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;
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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
#include "AssemblyHelpers.h"
#include "DFGGraph.h"
+#include "DFGMayExit.h"
#include "DFGSpeculativeJIT.h"
#include "JSCInlines.h"
, 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)
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)
{
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
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.
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);
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) {
reifyInlinedCallFrames(m_jit, exit);
// And finish.
- adjustAndJumpToTarget(m_jit, exit);
+ adjustAndJumpToTarget(m_jit, exit, exit.m_isExceptionHandler);
}
} } // namespace JSC::DFG
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.
// 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) {
reifyInlinedCallFrames(m_jit, exit);
// And finish.
- adjustAndJumpToTarget(m_jit, exit);
+ adjustAndJumpToTarget(m_jit, exit, exit.m_isExceptionHandler);
}
} } // namespace JSC::DFG
}
#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);
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);
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
#include "DFGInvalidationPointInjectionPhase.h"
#include "DFGJITCompiler.h"
#include "DFGLICMPhase.h"
+#include "DFGLiveCatchVariablePreservationPhase.h"
#include "DFGLivenessAnalysisPhase.h"
#include "DFGLoopPreHeaderCreationPhase.h"
#include "DFGMaximalFlushInsertionPhase.h"
dfg.dump();
}
+ performLiveCatchVariablePreservationPhase(dfg);
+
if (Options::enableMaximalFlushInsertionPhase())
performMaximalFlushInsertion(dfg);
public:
SlowPathGenerator(SpeculativeJIT* jit)
: m_currentNode(jit->m_currentNode)
+ , m_streamIndex(jit->m_stream->size())
+ , m_origin(jit->m_origin)
{
}
virtual ~SlowPathGenerator() { }
{
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);
}
virtual void generateInternal(SpeculativeJIT*) = 0;
MacroAssembler::Label m_label;
Node* m_currentNode;
+ unsigned m_streamIndex;
+ NodeOrigin m_origin;
};
template<typename JumpType>
Vector<std::unique_ptr<SlowPathGenerator>, 8> m_slowPathGenerators;
Vector<SilentRegisterSavePlan> m_plans;
+ unsigned m_outOfLineStreamIndex { UINT_MAX };
};
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);
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);
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();
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);
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);
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();
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);
handleExitCounts(jit, exit);
reifyInlinedCallFrames(jit, exit);
- adjustAndJumpToTarget(jit, exit);
+ adjustAndJumpToTarget(jit, exit, false);
LinkBuffer patchBuffer(*vm, jit, codeBlock);
exit.m_code = FINALIZE_CODE_IF(
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;
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)
readFrame(m_frame.callerFrame());
}
+void StackVisitor::unwindToMachineCodeBlockFrame()
+{
+#if ENABLE(DFG_JIT)
+ while (m_frame.isInlinedFrame())
+ gotoNextFrame();
+#endif
+}
+
void StackVisitor::readFrame(CallFrame* callFrame)
{
if (!callFrame) {
Frame& operator*() { return m_frame; }
ALWAYS_INLINE Frame* operator->() { return &m_frame; }
+ void unwindToMachineCodeBlockFrame();
private:
JS_EXPORT_PRIVATE StackVisitor(CallFrame* startFrame);
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);
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)
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
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);
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);
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
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
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)
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
m_perBytecodeProfiler->registerToSaveAtExit(pathOut.toCString().data());
}
+ callFrameForCatch = nullptr;
+
#if ENABLE(DFG_JIT)
if (canUseJIT())
dfgState = std::make_unique<DFG::LongLivedState>();
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); }
--- /dev/null
+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);
+}
--- /dev/null
+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);
--- /dev/null
+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");
--- /dev/null
+;(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) {}
+
+})();
--- /dev/null
+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");
--- /dev/null
+"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);
+}