diff --git a/.travis.yml b/.travis.yml index 7fa80ee..65df6c1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,16 +8,22 @@ matrix: include: - rvm: 2.0.0 env: EXECJS_RUNTIME=Node + - rvm: 2.0.0 + env: EXECJS_RUNTIME=Duktape - rvm: 2.0.0 env: EXECJS_RUNTIME=RubyRacer - rvm: 2.1 env: EXECJS_RUNTIME=Node + - rvm: 2.1 + env: EXECJS_RUNTIME=Duktape - rvm: 2.1 env: EXECJS_RUNTIME=RubyRacer - rvm: 2.2 env: EXECJS_RUNTIME=Node + - rvm: 2.2 + env: EXECJS_RUNTIME=Duktape - rvm: 2.2 env: EXECJS_RUNTIME=RubyRacer @@ -30,5 +36,7 @@ matrix: env: EXECJS_RUNTIME=JavaScriptCore - os: osx env: EXECJS_RUNTIME=Node + - os: osx + env: EXECJS_RUNTIME=Duktape - os: osx env: EXECJS_RUNTIME=RubyRacer diff --git a/Gemfile b/Gemfile index 3f1a9ea..2d5fb79 100644 --- a/Gemfile +++ b/Gemfile @@ -3,6 +3,7 @@ source 'https://rubygems.org' gemspec group :test do + gem 'duktape', platform: :mri gem 'therubyracer', platform: :mri gem 'therubyrhino', ">=1.73.3", platform: :jruby gem 'minitest', require: false diff --git a/lib/execjs/duktape_runtime.rb b/lib/execjs/duktape_runtime.rb new file mode 100644 index 0000000..cfbc7f9 --- /dev/null +++ b/lib/execjs/duktape_runtime.rb @@ -0,0 +1,68 @@ +require "execjs/runtime" +require "json" + +module ExecJS + class DuktapeRuntime < Runtime + class Context < Runtime::Context + def initialize(runtime, source = "") + @ctx = Duktape::Context.new(complex_object: nil) + @ctx.exec_string(encode(source), '(execjs)') + rescue Exception => e + raise wrap_error(e) + end + + def exec(source, options = {}) + return unless /\S/ =~ source + @ctx.eval_string("(function(){#{encode(source)}})()", '(execjs)') + rescue Exception => e + raise wrap_error(e) + end + + def eval(source, options = {}) + return unless /\S/ =~ source + @ctx.eval_string("(#{encode(source)})", '(execjs)') + rescue Exception => e + raise wrap_error(e) + end + + def call(identifier, *args) + @ctx.call_prop(identifier.split("."), *args) + rescue Exception => e + raise wrap_error(e) + end + + private + def wrap_error(e) + klass = case e + when Duktape::SyntaxError + RuntimeError + when Duktape::Error + ProgramError + when Duktape::InternalError + RuntimeError + end + + if klass + re = / \(line (\d+)\)$/ + lineno = e.message[re, 1] || 1 + error = klass.new(e.message.sub(re, "")) + error.set_backtrace(["(execjs):#{lineno}"] + e.backtrace) + error + else + e + end + end + end + + def name + "Duktape" + end + + def available? + require "duktape" + true + rescue LoadError + false + end + end +end diff --git a/lib/execjs/runtimes.rb b/lib/execjs/runtimes.rb index 3d3c990..4881170 100644 --- a/lib/execjs/runtimes.rb +++ b/lib/execjs/runtimes.rb @@ -1,5 +1,6 @@ require "execjs/module" require "execjs/disabled_runtime" +require "execjs/duktape_runtime" require "execjs/external_runtime" require "execjs/ruby_racer_runtime" require "execjs/ruby_rhino_runtime" @@ -8,6 +9,8 @@ module ExecJS module Runtimes Disabled = DisabledRuntime.new + Duktape = DuktapeRuntime.new + RubyRacer = RubyRacerRuntime.new RubyRhino = RubyRhinoRuntime.new @@ -70,6 +73,7 @@ module ExecJS def self.runtimes @runtimes ||= [ + Duktape, RubyRacer, RubyRhino, JavaScriptCore,