context = V8::Context.new(timeout: 20000) do |context|
context['ForbidAccess'] = ->(message) { throw NotImplementedError }
end
begin
context.eval("ForbidAccess();")
rescue => e
puts "e.class = #{e.class.name}"
puts "e.causes = #{e.causes}"
puts "e.root_cause = #{e.root_cause}"
puts "e.root_cause.class = #{e.root_cause.class}"
end
The console output:
e.class = V8::Error
e.causes = [#<V8::Error: uncaught throw NotImplementedError>, #<ArgumentError: uncaught throw NotImplementedError>]
e.root_cause = uncaught throw NotImplementedError
e.root_cause.class = ArgumentError
How do I get access to the NotImplementedError object?
(NotImplementedError is just for show. It will get replaced with a custom exception containing a message etc.)
You probably aren't doing what you think you are doing. The throw
keyword is not for exceptions. It is actually a local jump similar to goto
from other languages. See this snippet:
catch :done do
while true
array = [1,2,3]
for i in array
if i > 2
throw :done
end
end
end
end
It is just a control flow structure where the "caught" object must match the "thrown" one. But you can't simply catch all throws and figure out which object it was. For exceptions (like NotImplementedError
) the correct thing to use is raise
:
context = V8::Context.new(timeout: 20000) do |context|
context['ForbidAccess'] = ->(message) { raise NotImplementedError }
end
begin
context.eval("ForbidAccess();")
rescue => e
puts "e.root_cause = #{e.root_cause.inspect}"
# correctly prints #<NotImplementedError: NotImplementedError>
end
As for why you see ArgumentError
there, it is simple: A throw can't get through a begin
-rescue
structure (that rescues from exceptions). When an uncaught throw meets a rescue, a new exception is created about it. Check below:
begin
throw "whatever"
rescue e
p e #=> ArgumentError: uncaught throw "whatever"
end
This is what happens internally and all the V8 library see is an ArgumentError
popping up.