From 05ece36c5d598460273cb88dcceb009e6145f657 Mon Sep 17 00:00:00 2001 From: kares Date: Wed, 5 Dec 2012 10:00:41 +0100 Subject: [PATCH] handle Rhino's 64K code generation (method) limit on the fly simple retry running the failed compilation in interpreter mode --- lib/rhino/context.rb | 48 +++++++++++++++++++++++++++++++------- spec/rhino/context_spec.rb | 15 ++++++++++++ 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/lib/rhino/context.rb b/lib/rhino/context.rb index f172f0a..30ae9b5 100644 --- a/lib/rhino/context.rb +++ b/lib/rhino/context.rb @@ -240,24 +240,56 @@ module Rhino alias :version= :javascript_version= # Enter this context for operations. - # Some methods such as eval() will fail unless this context is open ! + # Some methods such as eval() will fail unless the context is open. def open(&block) do_open(&block) rescue JS::RhinoException => e + if code_generation_error?(e) + warn "[INFO] Rhino byte-code generation failed forcing #{@native} into interpreted mode" + self.optimization_level = -1 + retry + end raise Rhino::JSError.new(e) end private - def do_open - factory.enterContext(@native) - begin - yield self - ensure - factory.exit - end + def do_open # :nodoc + factory.enterContext(@native) + begin + yield self + ensure + factory.exit end + end + CODE_GENERATION_ERROR_MESSAGE = 'generated bytecode for method exceeds 64K limit' # :nodoc + + CODE_GENERATION_TRACE_CLASS_NAME = 'org.mozilla.javascript.optimizer.Codegen' # :nodoc + CODE_GENERATION_TRACE_METHOD_NAME = 'reportClassFileFormatException' # :nodoc + # at org.mozilla.javascript.optimizer.Codegen.reportClassFileFormatException + + def code_generation_error?(exception) # :nodoc + if ( exception.is_a?(NativeException) rescue nil ) # JRuby 1.6 wrapping + exception = exception.cause + end + if exception.class == Rhino::JS::EvaluatorException + if exception.message.index(CODE_GENERATION_ERROR_MESSAGE) + return true + end + # NOTE: unfortunately Rhino localizes the error messages! + # and the ClassFileFormatException is not kept as a cause + class_name = CODE_GENERATION_TRACE_CLASS_NAME + method_name = CODE_GENERATION_TRACE_METHOD_NAME + for trace in exception.getStackTrace() + if class_name == trace.class_name && method_name == trace.method_name + return true + end + end + end + false + end + end class IOReader < java.io.Reader # :nodoc: diff --git a/spec/rhino/context_spec.rb b/spec/rhino/context_spec.rb index a08a4ea..2fa6a91 100644 --- a/spec/rhino/context_spec.rb +++ b/spec/rhino/context_spec.rb @@ -166,4 +166,19 @@ describe Rhino::Context do end end + it "handles code generation error when 'generated bytecode for method exceeds 64K limit'" do + context = Rhino::Context.new + + big_script = '' + 10000.times { |i| big_script << "var s#{i} = '#{i}';\n" } + 10000.times { |i| big_script << "var n#{i} = +#{i} ;\n" } + + lambda { + context.eval big_script + }.should_not raise_error + + context.eval('( s9999 )').should == '9999' + context.eval('( n9999 )').should == +9999 + end + end \ No newline at end of file