From b1eb16bd580bc724197aaa68b2897b82b241eed0 Mon Sep 17 00:00:00 2001 From: Sam Stephenson Date: Sat, 18 Jun 2011 12:59:08 -0500 Subject: [PATCH] Add support for embedded SpiderMonkey via Johnson --- README.md | 4 +- execjs.gemspec | 5 ++ lib/execjs/johnson_runtime.rb | 112 ++++++++++++++++++++++++++++++++++ lib/execjs/runtimes.rb | 4 ++ 4 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 lib/execjs/johnson_runtime.rb diff --git a/README.md b/README.md index 809a308..b0a27c6 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,11 @@ returns the result to you as a Ruby object. ExecJS supports these runtimes: * [therubyracer](https://github.com/cowboyd/therubyracer) - Google V8 - embedded within MRI Ruby + embedded within Ruby * [therubyrhino](https://github.com/cowboyd/therubyrhino) - Mozilla Rhino embedded within JRuby +* [Johnson](https://github.com/jbarnette/johnson) - Mozilla + SpiderMonkey embedded within Ruby * [Mustang](https://github.com/nu7hatch/mustang) - Mustang V8 embedded within Ruby * [Node.js](http://nodejs.org/) diff --git a/execjs.gemspec b/execjs.gemspec index 936de8c..17f3009 100644 --- a/execjs.gemspec +++ b/execjs.gemspec @@ -13,6 +13,7 @@ Gem::Specification.new do |s| s.files = [ "lib/execjs.rb", "lib/execjs/external_runtime.rb", + "lib/execjs/johnson_runtime.rb", "lib/execjs/module.rb", "lib/execjs/version.rb", "lib/execjs/mustang_runtime.rb", @@ -29,7 +30,11 @@ Gem::Specification.new do |s| ] s.add_dependency "multi_json", "~>1.0" + s.add_development_dependency "johnson" + s.add_development_dependency "mustang" s.add_development_dependency "rake" + s.add_development_dependency "therubyracer" + s.add_development_dependency "therubyrhino" s.authors = ["Sam Stephenson", "Josh Peek"] s.email = ["sstephenson@gmail.com", "josh@joshpeek.com"] diff --git a/lib/execjs/johnson_runtime.rb b/lib/execjs/johnson_runtime.rb new file mode 100644 index 0000000..1d3e1ff --- /dev/null +++ b/lib/execjs/johnson_runtime.rb @@ -0,0 +1,112 @@ +module ExecJS + class JohnsonRuntime + class Context + def initialize(source = "") + @runtime = Johnson::Runtime.new + @runtime.evaluate(source) + end + + def exec(source, options = {}) + souce = source.encode('UTF-8') if source.respond_to?(:encode) + + if /\S/ =~ source + eval "(function(){#{source}})()", options + end + end + + def eval(source, options = {}) + souce = source.encode('UTF-8') if source.respond_to?(:encode) + + if /\S/ =~ source + unbox @runtime.evaluate("(#{source})") + end + rescue Johnson::Error => e + if syntax_error?(e) + raise RuntimeError, e.message + else + raise ProgramError, e.message + end + end + + def call(properties, *args) + unbox @runtime.evaluate(properties).call(*args) + rescue Johnson::Error => e + if syntax_error?(e) + raise RuntimeError, e.message + else + raise ProgramError, e.message + end + end + + def unbox(value) + case + when function?(value) + nil + when string?(value) + value.respond_to?(:force_encoding) ? + value.force_encoding('UTF-8') : + value + when array?(value) + value.map { |v| unbox(v) } + when object?(value) + value.inject({}) do |vs, (k, v)| + vs[k] = unbox(v) unless function?(v) + vs + end + else + value + end + end + + private + def syntax_error?(error) + error.message =~ /^syntax error at / + end + + def function?(value) + value.respond_to?(:function?) && value.function? + end + + def string?(value) + value.is_a?(String) + end + + def array?(value) + array_test.call(value) + end + + def object?(value) + value.respond_to?(:inject) + end + + def array_test + @array_test ||= @runtime.evaluate("(function(a) {return a instanceof [].constructor})") + end + end + + def name + "Johnson (SpiderMonkey)" + end + + def exec(source) + context = Context.new + context.exec(source) + end + + def eval(source) + context = Context.new + context.eval(source) + end + + def compile(source) + Context.new(source) + end + + def available? + require "johnson" + true + rescue LoadError + false + end + end +end diff --git a/lib/execjs/runtimes.rb b/lib/execjs/runtimes.rb index 80e11cc..83b92f6 100644 --- a/lib/execjs/runtimes.rb +++ b/lib/execjs/runtimes.rb @@ -1,5 +1,6 @@ require "execjs/module" require "execjs/external_runtime" +require "execjs/johnson_runtime" require "execjs/mustang_runtime" require "execjs/ruby_racer_runtime" require "execjs/ruby_rhino_runtime" @@ -10,6 +11,8 @@ module ExecJS RubyRhino = RubyRhinoRuntime.new + Johnson = JohnsonRuntime.new + Mustang = MustangRuntime.new Node = ExternalRuntime.new( @@ -70,6 +73,7 @@ module ExecJS @runtimes ||= [ RubyRacer, RubyRhino, + Johnson, Mustang, Node, JavaScriptCore,