Hi all,I would like to describe my concerns on current exception handling below.How it is handled currently:def a some_method_that_doesnt_exist end begin cxt['a'] = method :a cxt.eval('a') rescue V8::JSError => e if e.in_ruby? puts "some ruby error' else puts "some JS error' end endDoesn't look so good, right?First proposal is to split V8::JSError into V8::JSError::Ruby and V8::JSError:JS, thus we can rescue them individually or together, using their common ancestor. (described in https://github.com/cowboyd/therubyracer/issues/200)My second concern is that there's no sane way to understand what kind of exception we got from ruby:require 'v8' class Ex < StandardError end class A def c puts "raising" raise Ex.new "i'm a specific ruby exception" end end c = V8::Context.new c['a'] = A.new begin c.eval('a.c') rescue Ex => e puts "rescued ex: #{e}" rescue V8::JSError => e2 puts "rescued js: #{e2.inspect}" puts e2.in_ruby? rescue StandardError => e3 puts "rescued se: #{e3}" endGuess, which one is rescued?V8::JSError, and `in_ruby?` is returning true.If we are passing a wrapper for Net::HTTP to JS code, how do we distinguish between Net::HTTPBadResponse and NoMethodError?In first case there's an error with networking, and it is recoverable in JS code (try another host, report, log et c.).The second case is quite different and means there's an error in evaluated JS.How do we distinguish these two?
Let's move to my third concern.JS is known to be a safe sandbox. If JS is trying to do something malicious, we should be notified.require 'v8' class MaliciousScript < Exception end class A def a path raise MaliciousScript.new "hey, the script is trying to `rm -rf /`!" if path =~ /\// end end c = V8::Context.new c['a'] = A.new begin c.eval 'a.a("my")' puts "ok" c.eval <<-EOF try { a.a("/") } catch(e){ // Hey let's hack those bastards! } EOF puts "No exception!" rescue MaliciousScript => e puts "rescued maliciuos script: #{e}" rescue V8::JSError => e2 puts "rescued js: #{e2.inspect}" puts e2.in_ruby? rescue Exception => e3 puts "rescued se: #{e3}" endGuess what?ok No exception!\
My proposals here abridged:1. Split V8::JSError into two, JS specific and Ruby specific2. Let Ruby catch ruby specific exceptions in a straightforward way
3. Restrict JS from catching exceptions it's not intended to catch
Please let me know what you think.PS Just for the case you may have questions regarding what i'm doing. I'm working on a platform which allows users to host and execute JS, and it's Ruby controlled/based. I provide an API to JS, and would like to do so in a controlled way.
PPS Should I crosspost/move this to javascript-and-friends? Not sure if it's common to therubyrhino or execjs.
BR, Phil
Hey, just a few quick notes from me ...
I'm a bit against the proposed error "split" into two classes I'd rather prefer using a sub-class (I was even considering this with therubyrhino lately) :
https://github.com/kares/therubyrhino/commit/db54adc223d8f5881fee81e533ae6ea419f27228#L1R165
As the spec shows it's a bit cumbersome to get the original Ruby error, I was thinking of using a `Rhino::JSError` sub-class when a Ruby error is being wrapped.
This would also keep things backwards-compatible if we'd have a `class Rhino::JSRubyError < Rhino::JSError` while JSRubyError will provide a simple reader to retrieve the wrapper (Ruby embedded into JavaScript) error that is expected to be always a non-nil "native" error.
It does not address your second concern but is good to know as a reminder: therubyracer and therubyrhino do share a common policy of only rescuing StandardError and ScriptError :
https://github.com/cowboyd/redjs/blob/0d844f066666f967a78b20beb164c52d9ac3f5ca/lib/redjs/spec/context_spec.rb#L952
Since NoMethodError is a StandardError and not a "fatal" one it will be handled by the V8/Rhino engine - I do think this behaviour is correct.
I'm a bit against the proposed error "split" into two classes I'd rather prefer using a sub-class
This would also keep things backwards-compatible if we'd have a `class Rhino::JSRubyError < Rhino::JSError`
It does not address your second concern but is good to know as a reminder: therubyracer and therubyrhino do share a common policy of only rescuing StandardError and ScriptError
As this is primarily a V8 concern currently I will most-likely adapt Rhino in a similar way as V8 handles this.
Does the new error handling in 0.11.0 address these concerns?
we could mix in modules to the V8::Error instance indicating whether the root cause was in ruby or Javascript
that way, you could distinguish (optionally) in your rescuebeginrescue V8::Error::JavaScriptError => e#root cause from JSrescue V8::Error::RubyError => e#root cause was from Rubyrescue V8::Error#don't care which language originated the exception, only that one did occur during JS evaluationend
I'd love to hear your thoughts on this. If you want to take a stab at time boxing scripts in V8 (like you can already do with Rhino) I'd be happy to help you with it.
Does the new error handling in 0.11.0 address these concerns?It's really really better now. I've finally moved to 0.11 today and that was the only way to handle one kind of errors. I believe this could be done by major refactorings, but with 0.11 it was quick and simple to get the root error as `cause` and set an `if`.we could mix in modules to the V8::Error instance indicating whether the root cause was in ruby or JavascriptBoth subclassing and mixins should work and will look just the same from caller/rescuer's perspective.that way, you could distinguish (optionally) in your rescuebeginrescue V8::Error::JavaScriptError => e#root cause from JSrescue V8::Error::RubyError => e#root cause was from Rubyrescue V8::Error#don't care which language originated the exception, only that one did occur during JS evaluationendYep, looks perfect.
I'd love to hear your thoughts on this. If you want to take a stab at time boxing scripts in V8 (like you can already do with Rhino) I'd be happy to help you with it.Really appreciated. Will time boxing allow for limiting the execution of the script somehow? I wonder if this is V8 already. Is that something like Firefox's feature "the script processing is taking to long, do you want to stop it?" ?
BR, Phil
Hey Charlie and Philipp,
sure it's agreeable, but it's a bit different in Rhino (I should have
noticed this earlier) but I shall manage a refactoring :
rename Rhino::JSError into Rhino::Error (and make sure "generic" non-JS
RhinoExceptions are created as such)
introduce JSError as a sub-class of Error (for JavaScriptExceptions -
sub-class of RhinoException)
and than add RubyError as a sub-class of ? - seems to me that this kind of
error is actually still a JSError (JavaScriptError)
shouldn't we RubyError < JSError instead of RubyError < Error ?
On Oct 4, 2012, at 6:11 AM, Karol Bucek wrote:
Hey Charlie and Philipp,
sure it's agreeable, but it's a bit different in Rhino (I should have
noticed this earlier) but I shall manage a refactoring :
rename Rhino::JSError into Rhino::Error (and make sure "generic" non-JS
RhinoExceptions are created as such)
introduce JSError as a sub-class of Error (for JavaScriptExceptions -
sub-class of RhinoException)
and than add RubyError as a sub-class of ? - seems to me that this kind of
error is actually still a JSError (JavaScriptError)
shouldn't we RubyError < JSError instead of RubyError < Error ?It all boils down to what it is that each exceptional case is supposed to represent. My idea was that [ENGINE]::Error would represent that some exception happened during JavaScript execution, so perhaps an explicit JavaScriptError is not necessary, and all we need is two error classes? *::Error and *::RubyError < *::Error
The other question, which is not addressed so far, but we might as well, is do we want to reflect the four JavaScript exception types into Ruby so that you can discriminate in rescue clauses*::ReferenceError < *::Error*::RangeError < *::Error*::TypeError < *::Error*::SyntaxError < *::ErrorIf we do, then it probably *would* be handy to have a superclass JavaScriptError so that you could catch any one of these with a single statement:+-Error+-JavaScriptError| +ReferenceError| +RangeError| +TypeError| +SyntaxError+-RubyError
On Thu, Oct 4, 2012 at 1:43 PM, Charles Lowell <cow...@thefrontside.net> wrote:On Oct 4, 2012, at 6:11 AM, Karol Bucek wrote:
Hey Charlie and Philipp,
sure it's agreeable, but it's a bit different in Rhino (I should have
noticed this earlier) but I shall manage a refactoring :
rename Rhino::JSError into Rhino::Error (and make sure "generic" non-JS
RhinoExceptions are created as such)
introduce JSError as a sub-class of Error (for JavaScriptExceptions -
sub-class of RhinoException)
and than add RubyError as a sub-class of ? - seems to me that this kind of
error is actually still a JSError (JavaScriptError)
shouldn't we RubyError < JSError instead of RubyError < Error ?It all boils down to what it is that each exceptional case is supposed to represent. My idea was that [ENGINE]::Error would represent that some exception happened during JavaScript execution, so perhaps an explicit JavaScriptError is not necessary, and all we need is two error classes? *::Error and *::RubyError < *::Error
I see your point Charlie, that's what I have assumed as well as there was (and is) only a single JSError is Rhino.
But there's other kinds of errors coming from the engine there's the "generic" RhinoException (JavaScriptException is it's sub-class) for "other" non-JS errors.
I'm sure you can find similar cases for V8 (for a kind of engine expection), thus I thought we should address this as well while reviewing errors.
The other question, which is not addressed so far, but we might as well, is do we want to reflect the four JavaScript exception types into Ruby so that you can discriminate in rescue clauses*::ReferenceError < *::Error*::RangeError < *::Error*::TypeError < *::Error*::SyntaxError < *::ErrorIf we do, then it probably *would* be handy to have a superclass JavaScriptError so that you could catch any one of these with a single statement:+-Error+-JavaScriptError| +ReferenceError| +RangeError| +TypeError| +SyntaxError+-RubyError
Optionally this sound great, I'm not sure I'll implement this on Rhino cause it might require some error message parsing in my-case.
But if you can do this easily with V8 it might turn out useful eventually ...