dienashorner 0.1.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1ca8cc68b27b38c33dbdb7f1e0ac5822305ff736
4
- data.tar.gz: aa5411ac802f8ff7e4dac8a88a3772b1eb392f0b
3
+ metadata.gz: dd7cd7502f49f9a24841e42881064fce73a09df1
4
+ data.tar.gz: 3b5f2dc911bf0384853232e290f25405a900786c
5
5
  SHA512:
6
- metadata.gz: 79d0770cf58dfe94314196ed577a06291f61427e200c66ef4cdc066017e093f4bd2f5123ce36f2f4884244820372be08e370c0bd390ec6d0fa3256a6bc389023
7
- data.tar.gz: 4b0a046d56d0878788299b90b85a46e46fb4777fc7d5c0ac78270c043c59a6a11949f2f5633db682020fe2639ba2f83fd8748f98684294d6ac28803326eb153a
6
+ metadata.gz: 514de8502051b964befd3bb7edd902969f9a15251cbb4042a9be361f07b46a451807da9681e9cb574a24451dcaf967d0e6a3473ae9b83d3997899431185865b4
7
+ data.tar.gz: ff10758e53b1b206bac06d2104a6041b68ce150b4f353e640a96b92fe7fa24a36aaec12dc927208a6433038e84c44f2bdd79b69496b86b755d1d42cf6442e6c9
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # dia nashorner
2
2
 
3
- Embed the ~~Mozilla Rhino~~ Nashorn JavaScript interpreter into Ruby.
3
+ Embed the ~~Mozilla Rhino~~ [Nashorn][0] JavaScript interpreter into Ruby.
4
4
 
5
5
  Nashorn JS engine is available with Java 8 installs (try `jjs -v`).
6
6
 
@@ -147,11 +147,25 @@ Far from being a drop-in replacement although there's `require 'nashorn/rhino'`.
147
147
  will simply need to :`require 'nashorn/rhino/less'` before a `require 'less'`.
148
148
 
149
149
 
150
+ ### ExecJS
151
+
152
+ dienashorner gem ships with an [ExecJS][3] compatible runtime, its best to load it
153
+ (`require 'nashorn/execjs/load'`) before ExecJS's auto-detection takes place :
154
+ ```ruby
155
+ gem 'execjs', require: false
156
+ gem 'dienashorner', platform: :jruby, require: [ 'nashorn/execjs/load', 'execjs' ]
157
+ ```
158
+
159
+
150
160
  ### Nashorn
151
161
 
152
- Nashorn JavaScript runtime is part of OpenJDK http://openjdk.java.net/projects/nashorn/
162
+ Nashorn JavaScript runtime is part of [OpenJDK][4] (available since 8u40).
153
163
 
154
164
 
155
165
  ## Copyright
156
166
 
157
167
  Copyright (c) 2016 Karol Bucek. Apache License v2 (see LICENSE for details).
168
+
169
+ [0]: http://www.oracle.com/technetwork/articles/java/jf14-nashorn-2126515.html
170
+ [3]: https://github.com/rails/execjs
171
+ [4]: http://openjdk.java.net/projects/nashorn/
data/lib/nashorn.rb CHANGED
@@ -15,6 +15,7 @@ module Nashorn
15
15
  include_package 'jdk.nashorn.api.scripting'
16
16
  # include_package 'jdk.nashorn.internal.runtime'
17
17
  ScriptObject = Java::JdkNashornInternalRuntime::ScriptObject rescue nil
18
+ # Undefined = Java::JdkNashornInternalRuntime::Undefined rescue nil
18
19
  end
19
20
 
20
21
  def eval_js(source, options = {})
@@ -28,6 +29,8 @@ module Nashorn
28
29
  alias_method :eval, :eval_js # Nashorn.eval '"4" + 2'
29
30
  end
30
31
 
32
+ autoload :Context, 'nashorn/context'
33
+
31
34
  end
32
35
 
33
36
  require 'nashorn/version'
@@ -1,11 +1,23 @@
1
1
  class << Nashorn
2
2
 
3
+ ScriptObjectMirror = Nashorn::JS::ScriptObjectMirror
4
+ private_constant :ScriptObjectMirror
5
+ ScriptObject = Nashorn::JS::ScriptObject
6
+ private_constant :ScriptObject
7
+
3
8
  def to_rb(object, unmirror = false)
4
9
  # ConsString for optimized String + operations :
5
10
  return object.toString if object.is_a?(Java::JavaLang::CharSequence)
6
11
  return object.unwrap if object.is_a?(Nashorn::RubyObject)
7
12
  return object.unwrap if object.is_a?(Nashorn::RubyFunction)
8
- return nil if Nashorn::JS::ScriptObjectMirror.isUndefined(object)
13
+ return nil if ScriptObjectMirror.isUndefined(object)
14
+ # NOTE: "correct" Nashorn leaking-out internals :
15
+ if ScriptObject && object.is_a?(ScriptObject)
16
+ # BUGY: Java::JavaLang::ClassCastException:
17
+ # jdk.nashorn.internal.scripts.JO4 cannot be cast to jdk.nashorn.api.scripting.ScriptObjectMirror
18
+ # jdk.nashorn.api.scripting.ScriptUtils.wrap(jdk/nashorn/api/scripting/ScriptUtils.java:92)
19
+ # object = Nashorn::JS::ScriptUtils.wrap(object)
20
+ end
9
21
  return js_mirror_to_rb(object) if unmirror
10
22
  object
11
23
  end
@@ -32,22 +44,27 @@ class << Nashorn
32
44
  args.map { |arg| to_rb(arg) }
33
45
  end
34
46
 
35
- def args_to_js(args)
36
- args.map { |arg| to_js(arg) }.to_java
47
+ def args_to_js(args, to_java = false)
48
+ args = args.map { |arg| to_js(arg) }
49
+ to_java ? args.to_java : args
37
50
  end
38
51
 
39
- DEEP_MIRROR = ENV['nashorn.to_rb.unmirror.deep'] &&
40
- ENV['nashorn.to_rb.unmirror.deep'].length > 0 &&
41
- ENV['nashorn.to_rb.unmirror.deep'] != 'false'
52
+ DEEP_UNMIRROR = ENV_JAVA['nashorn.to_rb.unmirror.deep'] &&
53
+ ENV_JAVA['nashorn.to_rb.unmirror.deep'].length > 0 &&
54
+ ENV_JAVA['nashorn.to_rb.unmirror.deep'] != 'false'
42
55
 
43
- def js_mirror_to_rb(object, deep = DEEP_MIRROR)
44
- object = Nashorn::JS::ScriptUtils.unwrap(object)
56
+ def js_mirror_to_rb(object, deep = DEEP_UNMIRROR)
45
57
  if object.is_a?(Nashorn::JS::JSObject)
46
- return object.values.to_a if object.isArray
47
- return object if object.isFunction
58
+ if object.isArray
59
+ return object.raw_values.to_a unless deep
60
+ object.raw_values.map { |obj| to_rb(obj, true) }
61
+ end
62
+ return object if object.isFunction # TODO CallableHash < Hash?
48
63
  # Nashorn::JS::ScriptObjectMirror is already a Map but still :
49
64
  hash = {}
50
- object.keySet.each { |key| hash[key] = to_rb object[key], true }
65
+ for key in object.keySet
66
+ hash[key] = deep ? to_rb(object[key], true) : object[key]
67
+ end
51
68
  hash
52
69
  else
53
70
  object
data/lib/nashorn/error.rb CHANGED
@@ -8,12 +8,17 @@ module Nashorn
8
8
  if value.is_a?(Exception)
9
9
  super "#{value.class.name}: #{value.message}"
10
10
  elsif value.is_a?(JS::ScriptObject) # && @native.to_s.index('Error:')
11
- super @native.message
11
+ super normalize_message(@native)
12
12
  else
13
13
  super value
14
14
  end
15
15
  else
16
- super cause ? cause.message : @native
16
+ if cause = self.cause
17
+ message = normalize_message(cause)
18
+ else
19
+ message = normalize_message(@native)
20
+ end
21
+ super message
17
22
  end
18
23
  end
19
24
 
@@ -30,7 +35,7 @@ module Nashorn
30
35
  if @native.respond_to?(:cause) && @native.cause
31
36
  @cause = @native.cause
32
37
  else
33
- @native.is_a?(JS::NashornException) ? @native : nil
38
+ @cause = @native.is_a?(JS::NashornException) ? @native : nil
34
39
  end
35
40
  end
36
41
 
@@ -51,11 +56,20 @@ module Nashorn
51
56
  end
52
57
 
53
58
  # Return the thown (native) JavaScript value.
54
- def value(unwrap = false)
55
- return @value if defined?(@value) && ! unwrap
56
- @value = get_thrown unless defined?(@value)
57
- return @value.unwrap if unwrap && @value.respond_to?(:unwrap)
58
- @value
59
+ def value(unwrap = nil) # (unwrap = false)
60
+ # return @value if defined?(@value) && ! unwrap
61
+ # @value = get_thrown unless defined?(@value)
62
+ # return @value.unwrap if unwrap && @value.respond_to?(:unwrap)
63
+ # @value
64
+
65
+ return @value if defined?(@value)
66
+
67
+ if thrown = get_thrown
68
+ # NOTE: thrown sometimes leaks a Nashorn::JS::ScriptObject
69
+ @value = Nashorn.to_rb thrown
70
+ else
71
+ @value = thrown
72
+ end
59
73
  end
60
74
  alias_method :thrown, :value
61
75
 
@@ -70,10 +84,19 @@ module Nashorn
70
84
 
71
85
  # Returns the JavaScript back-trace part for this error (the script stack).
72
86
  def javascript_backtrace(raw_elements = false)
87
+ return @javascript_backtrace if (@javascript_backtrace ||= nil) && ! raw_elements
88
+
73
89
  return nil unless cause.is_a?(JS::NashornException)
74
- JS::NashornException.getScriptFrames(cause).map do |element|
75
- raw_elements ? element : element.to_s # ScriptStackElement
90
+
91
+ return JS::NashornException.getScriptFrames(cause) if raw_elements
92
+
93
+ js_backtrace = []
94
+ js_backtrace << @_trace_trail if defined?(@_trace_trail)
95
+
96
+ for element in JS::NashornException.getScriptFrames(cause)
97
+ js_backtrace << element.to_s # element - ScriptStackElement
76
98
  end
99
+ @javascript_backtrace = js_backtrace
77
100
  end
78
101
 
79
102
  # jdk.nashorn.internal.runtime::ECMAException < NashornException has these :
@@ -90,6 +113,14 @@ module Nashorn
90
113
  cause.respond_to?(:getColumnNumber) ? cause.getColumnNumber : nil
91
114
  end
92
115
 
116
+ PARSER_EXCEPTION = 'Java::JdkNashornInternalRuntime::ParserException'
117
+ private_constant :PARSER_EXCEPTION if respond_to?(:private_constant)
118
+
119
+ # @private invented for ExceJS
120
+ def self.parse_error?(error)
121
+ PARSER_EXCEPTION.eql? error.class.name
122
+ end
123
+
93
124
  private
94
125
 
95
126
  def get_thrown
@@ -100,6 +131,18 @@ module Nashorn
100
131
  end
101
132
  end
102
133
 
134
+ def normalize_message(error)
135
+ # "<eval>:1:1 Expected an operand but found )\n ())\n^"
136
+ # extract first trace part of message :
137
+ message = error.message
138
+ return message unless JSError.parse_error?(error)
139
+ if match = message.match(/^(.*?\:\d+\:\d+)\s/)
140
+ @_trace_trail = match[1]
141
+ return message[@_trace_trail.length + 1..-1]
142
+ end
143
+ message
144
+ end
145
+
103
146
  end
104
147
 
105
148
  end
@@ -0,0 +1,16 @@
1
+ # load ExecJS with an available Nashorn runtime!
2
+
3
+ require 'execjs/runtimes' # only require 'execjs' triggers auto-detect
4
+
5
+ module ExecJS
6
+ if const_defined?(:NashornRuntime)
7
+ warn "ExecJS::NashornRuntime exists, you probably want to avoid loading #{__FILE__}"
8
+ end
9
+
10
+ require 'nashorn/execjs/runtime'
11
+
12
+ unless at = Runtimes.runtimes.find { |runtime| runtime.is_a?(ExecJS::NashornRuntime) }
13
+ at = Runtimes.runtimes.index(RubyRhino) if const_defined?(:RubyRhino)
14
+ Runtimes.runtimes.insert (at || 0), ExecJS::NashornRuntime.new
15
+ end
16
+ end
@@ -0,0 +1,85 @@
1
+ require 'execjs/runtime'
2
+
3
+ module ExecJS
4
+ class NashornRuntime < Runtime
5
+ class Context < Runtime::Context
6
+
7
+ def initialize(runtime, source = nil)
8
+ source = encode(source) if source
9
+ @nashorn_context = ::Nashorn::Context.new
10
+ @nashorn_context.eval(source) if source
11
+ rescue Exception => e
12
+ raise wrap_error(e)
13
+ end
14
+
15
+ def exec(source, options = nil) # options not used
16
+ source = encode(source)
17
+ eval "(function(){#{source}})()", options if /\S/ =~ source
18
+ end
19
+
20
+ def eval(source, options = nil) # options not used
21
+ source = encode(source)
22
+ unbox @nashorn_context.eval("(#{source})") if /\S/ =~ source
23
+ rescue Exception => e
24
+ raise wrap_error(e)
25
+ end
26
+
27
+ def call(prop, *args)
28
+ evaled = @nashorn_context.eval(prop)
29
+ unbox evaled.call(*args)
30
+ rescue Exception => e
31
+ raise wrap_error(e)
32
+ end
33
+
34
+ def unbox(value)
35
+ value = ::Nashorn::to_rb(value, false) # ExecJS likes its own way :
36
+
37
+ if value.is_a?(::Nashorn::JS::JSObject)
38
+ return nil if value.isFunction
39
+ return value.values.map { |v| unbox(v) } if value.isArray
40
+ hash = {}; value.each_raw do |key, val|
41
+ next if val.respond_to?(:isFunction) && val.isFunction
42
+ hash[key] = unbox(val)
43
+ end
44
+ return hash
45
+ end
46
+ value
47
+ end
48
+
49
+ def wrap_error(e)
50
+ return e unless e.is_a?(::Nashorn::JSError)
51
+
52
+ error_class = ::Nashorn::JSError.parse_error?(e.cause) ? RuntimeError : ProgramError
53
+
54
+ backtrace = e.backtrace
55
+
56
+ if js_stack = e.javascript_backtrace
57
+ backtrace = backtrace - js_stack
58
+ # ["<<eval>>.<anonymous>(<eval>:1)", "<<eval>>.<program>(<eval>:1)"]
59
+ js_stack = js_stack.map { |line| line.sub(/\<eval\>\:/, "(execjs):") }
60
+ backtrace = js_stack + backtrace
61
+ elsif backtrace
62
+ backtrace = backtrace.dup; backtrace.unshift('(execjs):1')
63
+ end
64
+
65
+ error = error_class.new e.value ? e.value.to_s : e.message
66
+ error.set_backtrace(backtrace)
67
+ error
68
+ end
69
+
70
+ end
71
+
72
+ def name
73
+ 'dienashorner (Nashorn)'
74
+ end
75
+
76
+ def available?
77
+ return false unless defined? JRUBY_VERSION
78
+ require 'nashorn'
79
+ true
80
+ rescue LoadError
81
+ false
82
+ end
83
+
84
+ end
85
+ end
data/lib/nashorn/ext.rb CHANGED
@@ -1,6 +1,11 @@
1
1
  module Nashorn
2
2
  module JS
3
3
 
4
+ AbstractJSObject.module_eval do
5
+ alias_method :raw_values, :values
6
+ alias_method :__call__, :call
7
+ end
8
+
4
9
  JSObject.module_eval do
5
10
 
6
11
  def [](key)
@@ -91,8 +96,7 @@ module Nashorn
91
96
  else
92
97
  if hasMember(name_str) && property = getMember(name_str)
93
98
  if property.is_a?(JSObject) && property.isFunction
94
- js_args = Nashorn.args_to_js(args)
95
- Nashorn.to_rb property.__call__(self, js_args)
99
+ Nashorn.to_rb property.__call__(self, *Nashorn.args_to_js(args))
96
100
  else
97
101
  if args.size > 0
98
102
  raise ArgumentError, "can't call '#{name_str}' with args: #{args.inspect} as it's a property"
@@ -115,21 +119,7 @@ module Nashorn
115
119
  # JavaScript's Function#call but rather as Ruby's Method#call !
116
120
  # Use #apply or #bind before calling to achieve the same effect.
117
121
  def call(*args)
118
- this = nil
119
- Nashorn.to_rb __call__ this, Nashorn.args_to_js(args)
120
- rescue JS::NashornException => e
121
- raise Nashorn::JSError.new(e)
122
- end
123
-
124
- # bind a JavaScript function into the given (this) context
125
- #def bind(this, *args)
126
- # args = Nashorn.args_to_js(args)
127
- # Rhino::JS::BoundFunction.new(self, Nashorn.to_js(this), args)
128
- #end
129
-
130
- # use JavaScript functions constructors from Ruby as `fn.new`
131
- def new(*args)
132
- newObject Nashorn.args_to_js(args)
122
+ Nashorn.to_rb __call__ nil, *Nashorn.args_to_js(args) # this = nil
133
123
  rescue JS::NashornException => e
134
124
  raise Nashorn::JSError.new(e)
135
125
  end
@@ -140,20 +130,28 @@ module Nashorn
140
130
  # NOTE: That #call from Ruby does not have the same semantics as
141
131
  # JavaScript's Function#call but rather as Ruby's Method#call !
142
132
  def apply(this, *args)
143
- __call__ Nashorn.to_js(this), Nashorn.args_to_js(args)
133
+ __call__ Nashorn.to_js(this), *Nashorn.args_to_js(args)
144
134
  rescue JS::NashornException => e
145
135
  raise Nashorn::JSError.new(e)
146
136
  end
147
137
  alias_method :methodcall, :apply # V8::Function compatibility
148
138
 
149
- end
139
+ # bind a JavaScript function into the given (this) context
140
+ #def bind(this, *args)
141
+ # args = Nashorn.args_to_js(args)
142
+ # Rhino::JS::BoundFunction.new(self, Nashorn.to_js(this), args)
143
+ #end
144
+
145
+ # use JavaScript functions constructors from Ruby as `fn.new`
146
+ def new(*args)
147
+ newObject *Nashorn.args_to_js(args)
148
+ rescue JS::NashornException => e
149
+ raise Nashorn::JSError.new(e)
150
+ end
150
151
 
151
- AbstractJSObject.module_eval do
152
- alias_method :raw_values, :values
153
- alias_method :__call__, :call
154
152
  end
155
153
 
156
- ScriptObjectMirror.module_eval do # implements java.util.Map
154
+ ScriptObjectMirror.class_eval do # implements java.util.Map
157
155
 
158
156
  # @private NOTE: duplicated from JSObject
159
157
  def [](key)
@@ -169,15 +167,32 @@ module Nashorn
169
167
 
170
168
  # @private NOTE: duplicated from JSObject
171
169
  def call(*args)
172
- this = nil
173
- Nashorn.to_rb __call__ this, Nashorn.args_to_js(args)
170
+ # Nashorn.to_rb __call__ nil, *Nashorn.args_to_js(args) # this = nil
171
+
172
+
173
+ #if isFunction
174
+ # this = Nashorn.to_js args.shift
175
+ #
176
+ # puts "calling this = #{this.inspect} args = #{args.inspect}"
177
+ #
178
+ # result = __call__(this, Nashorn.args_to_js(args))
179
+ #else
180
+
181
+ #puts "calling args = #{args.inspect} Nashorn.args_to_js(args) #{Nashorn.args_to_js(args).inspect}"
182
+
183
+ result = __call__ nil, *Nashorn.args_to_js(args)
184
+
185
+ #puts "called result = #{result.inspect} #{result.class}"
186
+
187
+ Nashorn.to_rb result
188
+ #end
174
189
  rescue JS::NashornException => e
175
190
  raise Nashorn::JSError.new(e)
176
191
  end
177
192
 
178
193
  # def callMember(this, *args)
179
194
  # this = nil
180
- # Nashorn.to_rb __call__ this, Nashorn.args_to_js(args)
195
+ # Nashorn.to_rb __call__ this, *Nashorn.args_to_js(args)
181
196
  # rescue JS::NashornException => e
182
197
  # raise Nashorn::JSError.new(e)
183
198
  # end
data/lib/nashorn/ruby.rb CHANGED
@@ -269,8 +269,7 @@ module Nashorn
269
269
  # Nashorn.to_js(result) # TODO do not convert if java_args ?
270
270
  end
271
271
 
272
- # make sure redefined :call is aliased not the one "inherited" from
273
- # JS::BaseFunction#call when invoking __call__ (@see ext.rb)
272
+ # make sure redefined :call is aliased not the one "inherited" (@see ext.rb)
274
273
  alias_method :__call__, :call
275
274
 
276
275
  end
@@ -331,22 +330,21 @@ module Nashorn
331
330
  # @private
332
331
  class Exception < JS::NashornException
333
332
 
334
- def initialize(value)
335
- super wrap_value(value)
336
- end
337
-
338
- private
333
+ attr_reader :thrown
339
334
 
340
- def wrap_value(value)
341
- value.is_a?(Object) ? value : Object.wrap(value)
335
+ def initialize(error)
336
+ super error.message, error.is_a?(Java::JavaLang::Throwable) ? error : nil
337
+ @thrown = error
342
338
  end
343
339
 
340
+ def getThrown; @thrown end
341
+
344
342
  end
345
343
 
346
344
  def self.wrap_error(e, backtrace = nil)
347
- error = Exception.new(e)
348
- error.set_backtrace(backtrace) if backtrace
349
- error
345
+ js_error = Exception.new(e)
346
+ js_error.set_backtrace(backtrace) if backtrace
347
+ js_error
350
348
  end
351
349
 
352
350
  end
@@ -1,3 +1,3 @@
1
1
  module Nashorn
2
- VERSION = '0.1.1'
2
+ VERSION = '0.2.2'
3
3
  end
@@ -32,7 +32,7 @@ describe Nashorn::Ruby::AttributeAccess do
32
32
  it "gets methods and instance variables" do
33
33
  rb_object = Nashorn::Ruby::Object.wrap Meh.new
34
34
 
35
- rb_object.get('anAttr0', nil).should be_nil
35
+ rb_object.get('anAttr0', nil).should be nil
36
36
  rb_object.get('the_attr_1', nil).should == 'attr_1'
37
37
 
38
38
  # rb_object.get('an_attr_2', nil).should be(Rhino::JS::Scriptable::NOT_FOUND) # no reader
@@ -49,23 +49,21 @@ describe Nashorn::Ruby::AttributeAccess do
49
49
  it "has methods and instance variables" do
50
50
  rb_object = Nashorn::Ruby::Object.wrap Meh.new
51
51
 
52
- rb_object.has('anAttr0', nil).should be_true
53
- rb_object.has('the_attr_1', nil).should be_true
54
- rb_object.has('an_attr_2', nil).should be_false # no reader nor writer
52
+ rb_object.has('anAttr0', nil).should be true
53
+ rb_object.has('the_attr_1', nil).should be true
54
+ rb_object.has('an_attr_2', nil).should be false # no reader nor writer
55
55
 
56
56
  [ 'theMethod0', 'a_method1', 'the_method_2' ].each do |name|
57
- rb_object.has(name, nil).should be_true
57
+ rb_object.has(name, nil).should be true
58
58
  end
59
59
 
60
- rb_object.has('non-existent-method', nil).should be_false
60
+ rb_object.has('non-existent-method', nil).should be false
61
61
  end
62
62
 
63
63
  it "puts using attr writer" do
64
- start = mock('start')
65
- start.expects(:put).never
66
64
  rb_object = Nashorn::Ruby::Object.wrap Meh.new
67
65
 
68
- rb_object.put('the_attr_1', start, 42)
66
+ rb_object.put('the_attr_1', 42)
69
67
  rb_object.the_attr_1.should == 42
70
68
  end
71
69
 
@@ -3,6 +3,8 @@ require File.expand_path('../spec_helper', File.dirname(__FILE__))
3
3
  describe Nashorn::JSError do
4
4
 
5
5
  it "works as a StandardError with a message being passed" do
6
+ pending 'currently JSError.new always gets a (nashorn) exception'
7
+
6
8
  js_error = Nashorn::JSError.new 'an error message'
7
9
  lambda { js_error.to_s && js_error.inspect }.should_not raise_error
8
10
 
@@ -11,38 +13,40 @@ describe Nashorn::JSError do
11
13
  js_error.javascript_backtrace.should be nil
12
14
  end
13
15
 
14
- # it "might wrap a NashornException wrapped in a NativeException like error" do
15
- # # JRuby's NativeException.new(Nashorn_e) does not work as it is
16
- # # intended to handle Java exceptions ... no new on the Ruby side
17
- # native_error_class = Class.new(RuntimeError) do
18
- # def initialize(cause); @cause = cause end
19
- # def cause; @cause end
20
- # end
21
- #
22
- # nashorn_e = javax.script.ScriptException.new("42".to_java)
23
- # js_error = Nashorn::JSError.new native_error_class.new(nashorn_e)
24
- # lambda { js_error.to_s && js_error.inspect }.should_not raise_error
25
- #
26
- # js_error.cause.should be nashorn_e
27
- # js_error.message.should == '42'
28
- # js_error.javascript_backtrace.should be nil
29
- # end
16
+ it "might wrap a NashornException wrapped in a NativeException like error" do
17
+ pending 'probably not relevant'
18
+
19
+ # JRuby's NativeException.new(Nashorn_e) does not work as it is
20
+ # intended to handle Java exceptions ... no new on the Ruby side
21
+ native_error_class = Class.new(RuntimeError) do
22
+ def initialize(cause); @cause = cause end
23
+ def cause; @cause end
24
+ end
25
+
26
+ nashorn_e = javax.script.ScriptException.new("42".to_java)
27
+ js_error = Nashorn::JSError.new native_error_class.new(nashorn_e)
28
+ lambda { js_error.to_s && js_error.inspect }.should_not raise_error
29
+
30
+ js_error.cause.should be nashorn_e
31
+ js_error.message.should == '42'
32
+ js_error.javascript_backtrace.should be nil
33
+ end
30
34
 
31
35
  it "keeps the thrown javascript object value" do
32
36
  begin
33
37
  Nashorn::Context.eval "throw { foo: 'bar' }"
34
- rescue => e
35
- e.should be_a(Nashorn::JSError)
36
- e.message.should == e.value.to_s
38
+ rescue Nashorn::JSError => e
37
39
 
38
- pending
40
+ pending 'CAN NOT WORK-AROUND DUE A NASHORN BUG!'
39
41
 
40
- puts e.value.inspect
41
- puts e.value.class
42
- puts e.value.class.included_modules.inspect
43
- puts e.value.class.superclass
44
- puts e.value.class.superclass.included_modules.inspect
42
+ e.message.should == e.value.to_s
45
43
 
44
+ # Java::JdkNashornInternalRuntime::ScriptObject leaks out?!
45
+ #if Nashorn::JS::ScriptObject
46
+ # e.value.should be_a(Nashorn::JS::ScriptObject)
47
+ #else
48
+ # e.value.should be_a(Nashorn::JS::JSObject)
49
+ #end
46
50
  e.value.should be_a(Nashorn::JS::JSObject)
47
51
  e.value['foo'].should == 'bar'
48
52
  else
@@ -158,8 +162,7 @@ describe Nashorn::JSError do
158
162
  context = Nashorn::Context.new
159
163
  context.eval "function foo() { throw 'bar' }"
160
164
  context['foo'].apply(nil)
161
- rescue => e
162
- e.should be_a Nashorn::JSError
165
+ rescue Nashorn::JSError => e
163
166
  e.value.should == 'bar'
164
167
  else
165
168
  fail "expected to rescue"
@@ -177,14 +180,13 @@ describe Nashorn::JSError do
177
180
  hi = context.eval "( function hi(arg) { Hello.hello(arg); } )"
178
181
  begin
179
182
  hi.call(24)
180
- rescue => e
181
- e.should be_a Nashorn::JSError
182
- e.value.should_not be nil
183
- # Java::JdkNashornInternalObjects::NativeTypeError
184
- # e.value.should be_a Nashorn::Ruby::Object
185
- # e.value(true).should be_a RuntimeError # unwraps ruby object
183
+ rescue Nashorn::JSError => e
184
+ e.thrown.should_not be nil
185
+ # NOTE: ... or should it be wrapped?
186
+ e.value.should be_a Nashorn::Ruby::Object if false
187
+ e.value(true).should be_a RuntimeError # unwraps ruby object
186
188
  # prints the original message (beyond [ruby RuntimeError]) :
187
- e.message.should =~ /TypeError: .* has no such function \"hello\"/
189
+ e.message.should =~ /hello/
188
190
  else
189
191
  fail "expected to rescue"
190
192
  end
@@ -0,0 +1,374 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'minitest/autorun'
3
+
4
+ require 'json'
5
+
6
+ require 'nashorn/execjs/load'
7
+ require 'execjs'
8
+
9
+ if defined? Minitest::Test
10
+ Test = Minitest::Test
11
+ elsif defined? MiniTest::Unit::TestCase
12
+ Test = MiniTest::Unit::TestCase
13
+ end
14
+
15
+ class TestExecJS < Test
16
+ # def test_runtime_available
17
+ # runtime = ExecJS::ExternalRuntime.new(command: "nonexistent")
18
+ # assert !runtime.available?
19
+ #
20
+ # runtime = ExecJS::ExternalRuntime.new(command: "ruby")
21
+ # assert runtime.available?
22
+ # end
23
+ #
24
+ # def test_runtime_assignment
25
+ # original_runtime = ExecJS.runtime
26
+ # runtime = ExecJS::ExternalRuntime.new(command: "nonexistent")
27
+ # assert_raises(ExecJS::RuntimeUnavailable) { ExecJS.runtime = runtime }
28
+ # assert_equal original_runtime, ExecJS.runtime
29
+ #
30
+ # runtime = ExecJS::ExternalRuntime.new(command: "ruby")
31
+ # ExecJS.runtime = runtime
32
+ # assert_equal runtime, ExecJS.runtime
33
+ # ensure
34
+ # ExecJS.runtime = original_runtime
35
+ # end
36
+
37
+ def test_context_call
38
+ context = ExecJS.compile("id = function(v) { return v; }")
39
+ assert_equal "bar", context.call("id", "bar")
40
+ end
41
+
42
+ def test_nested_context_call
43
+ context = ExecJS.compile("a = {}; a.b = {}; a.b.id = function(v) { return v; }")
44
+ assert_equal "bar", context.call("a.b.id", "bar")
45
+ end
46
+
47
+ def test_context_call_missing_function
48
+ context = ExecJS.compile("")
49
+ assert_raises ExecJS::ProgramError do
50
+ context.call("missing")
51
+ end
52
+ end
53
+
54
+ {
55
+ "function() {}" => nil,
56
+ "0" => 0,
57
+ "null" => nil,
58
+ "undefined" => nil,
59
+ "true" => true,
60
+ "false" => false,
61
+ "[1, 2]" => [1, 2],
62
+ "[1, function() {}]" => [1, nil],
63
+ "'hello'" => "hello",
64
+ "'red yellow blue'.split(' ')" => ["red", "yellow", "blue"],
65
+ "{a:1,b:2}" => {"a"=>1,"b"=>2},
66
+ "{a:true,b:function (){}}" => {"a"=>true},
67
+ "'café'" => "café",
68
+ '"☃"' => "☃",
69
+ '"\u2603"' => "☃",
70
+ "'\u{1f604}'".encode("UTF-8") => "\u{1f604}".encode("UTF-8"), # Smiling emoji
71
+ "'\u{1f1fa}\u{1f1f8}'".encode("UTF-8") => "\u{1f1fa}\u{1f1f8}".encode("UTF-8"), # US flag
72
+ '"\\\\"' => "\\"
73
+ }.each_with_index do |(input, output), index|
74
+ define_method("test_exec_string_#{index}") do
75
+ assert_equal output, ExecJS.exec("return #{input}")
76
+ end
77
+
78
+ define_method("test_eval_string_#{index}") do
79
+ assert_equal output, ExecJS.eval(input)
80
+ end
81
+
82
+ define_method("test_compile_return_string_#{index}") do
83
+ context = ExecJS.compile("var a = #{input};")
84
+ assert_equal output, context.eval("a")
85
+ end
86
+
87
+ define_method("test_compile_call_string_#{index}") do
88
+ context = ExecJS.compile("function a() { return #{input}; }")
89
+ assert_equal output, context.call("a")
90
+ end
91
+ end
92
+
93
+ [
94
+ nil,
95
+ true,
96
+ false,
97
+ 1,
98
+ 3.14,
99
+ "hello",
100
+ "\\",
101
+ "café",
102
+ "☃",
103
+ "\u{1f604}".encode("UTF-8"), # Smiling emoji
104
+ "\u{1f1fa}\u{1f1f8}".encode("UTF-8"), # US flag
105
+ [1, 2, 3],
106
+ [1, [2, 3]],
107
+ [1, [2, [3]]],
108
+ ["red", "yellow", "blue"],
109
+ { "a" => 1, "b" => 2},
110
+ { "a" => 1, "b" => [2, 3]},
111
+ { "a" => true }
112
+ ].each_with_index do |value, index|
113
+ json_value = JSON.generate(value, quirks_mode: true)
114
+
115
+ define_method("test_json_value_#{index}") do
116
+ assert_equal value, JSON.parse(json_value, quirks_mode: true)
117
+ end
118
+
119
+ define_method("test_exec_value_#{index}") do
120
+ assert_equal value, ExecJS.exec("return #{json_value}")
121
+ end
122
+
123
+ define_method("test_eval_value_#{index}") do
124
+ assert_equal value, ExecJS.eval("#{json_value}")
125
+ end
126
+
127
+ define_method("test_strinigfy_value_#{index}") do
128
+ # context = ExecJS.compile("function json(obj) { return obj || JSON.stringify(obj); }")
129
+ context = ExecJS.compile("function json(obj) { return JSON.stringify(obj); }")
130
+
131
+ puts context.inspect; puts "value = #{value.inspect}"
132
+
133
+ assert_equal json_value, context.call("json", value)
134
+ end
135
+
136
+ define_method("test_call_value_#{index}") do
137
+ context = ExecJS.compile("function id(obj) { return obj; }")
138
+ assert_equal value, context.call("id", value)
139
+ end
140
+ end
141
+
142
+ def test_eval_blank
143
+ assert_nil ExecJS.eval("")
144
+ assert_nil ExecJS.eval(" ")
145
+ assert_nil ExecJS.eval(" ")
146
+ end
147
+
148
+ def test_exec_return
149
+ assert_nil ExecJS.exec("return")
150
+ end
151
+
152
+ def test_exec_no_return
153
+ assert_nil ExecJS.exec("1")
154
+ end
155
+
156
+ def test_encoding
157
+ utf8 = Encoding.find('UTF-8')
158
+
159
+ assert_equal utf8, ExecJS.exec("return 'hello'").encoding
160
+ assert_equal utf8, ExecJS.eval("'☃'").encoding
161
+
162
+ ascii = "'hello'".encode('US-ASCII')
163
+ result = ExecJS.eval(ascii)
164
+ assert_equal "hello", result
165
+ assert_equal utf8, result.encoding
166
+
167
+ assert_raises Encoding::UndefinedConversionError do
168
+ binary = "\xde\xad\xbe\xef".force_encoding("BINARY")
169
+ ExecJS.eval(binary)
170
+ end
171
+ end
172
+
173
+ def test_encoding_compile
174
+ utf8 = Encoding.find('UTF-8')
175
+
176
+ context = ExecJS.compile("foo = function(v) { return '¶' + v; }".encode("ISO8859-15"))
177
+
178
+ assert_equal utf8, context.exec("return foo('hello')").encoding
179
+ assert_equal utf8, context.eval("foo('☃')").encoding
180
+
181
+ ascii = "foo('hello')".encode('US-ASCII')
182
+ result = context.eval(ascii)
183
+ assert_equal "¶hello", result
184
+ assert_equal utf8, result.encoding
185
+
186
+ assert_raises Encoding::UndefinedConversionError do
187
+ binary = "\xde\xad\xbe\xef".force_encoding("BINARY")
188
+ context.eval(binary)
189
+ end
190
+ end
191
+
192
+ def test_surrogate_pairs
193
+ # Smiling emoji
194
+ str = "\u{1f604}".encode("UTF-8")
195
+ assert_equal 2, ExecJS.eval("'#{str}'.length")
196
+ assert_equal str, ExecJS.eval("'#{str}'")
197
+
198
+ # US flag emoji
199
+ str = "\u{1f1fa}\u{1f1f8}".encode("UTF-8")
200
+ assert_equal 4, ExecJS.eval("'#{str}'.length")
201
+ assert_equal str, ExecJS.eval("'#{str}'")
202
+ end
203
+
204
+ def test_compile_anonymous_function
205
+ context = ExecJS.compile("foo = function() { return \"bar\"; }")
206
+ assert_equal "bar", context.exec("return foo()")
207
+ assert_equal "bar", context.eval("foo()")
208
+ assert_equal "bar", context.call("foo")
209
+ end
210
+
211
+ def test_compile_named_function
212
+ context = ExecJS.compile("function foo() { return \"bar\"; }")
213
+ assert_equal "bar", context.exec("return foo()")
214
+ assert_equal "bar", context.eval("foo()")
215
+ assert_equal "bar", context.call("foo")
216
+ end
217
+
218
+ def test_this_is_global_scope
219
+ assert_equal true, ExecJS.eval("this === (function() {return this})()")
220
+ assert_equal true, ExecJS.exec("return this === (function() {return this})()")
221
+ end
222
+
223
+ def test_browser_self_is_undefined
224
+ assert ExecJS.eval("typeof self == 'undefined'")
225
+ end
226
+
227
+ def test_node_global_is_undefined
228
+ assert ExecJS.eval("typeof global == 'undefined'")
229
+ end
230
+
231
+ def test_commonjs_vars_are_undefined
232
+ assert ExecJS.eval("typeof module == 'undefined'")
233
+ assert ExecJS.eval("typeof exports == 'undefined'")
234
+ assert ExecJS.eval("typeof require == 'undefined'")
235
+ end
236
+
237
+ def test_console_is_undefined
238
+ assert ExecJS.eval("typeof console == 'undefined'")
239
+ end
240
+
241
+ def test_timers_are_undefined
242
+ assert ExecJS.eval("typeof setTimeout == 'undefined'")
243
+ assert ExecJS.eval("typeof setInterval == 'undefined'")
244
+ assert ExecJS.eval("typeof clearTimeout == 'undefined'")
245
+ assert ExecJS.eval("typeof clearInterval == 'undefined'")
246
+ assert ExecJS.eval("typeof setImmediate == 'undefined'")
247
+ assert ExecJS.eval("typeof clearImmediate == 'undefined'")
248
+ end
249
+
250
+ def test_compile_large_scripts
251
+ body = "var foo = 'bar';\n" * 100_000
252
+ assert ExecJS.exec("function foo() {\n#{body}\n};\nreturn true")
253
+ end
254
+
255
+ def test_exec_syntax_error
256
+ begin
257
+ ExecJS.exec(")")
258
+ flunk
259
+ rescue ExecJS::RuntimeError => e
260
+
261
+ #puts "\n\nEXEC: " + e.inspect + "\n #{e.backtrace.join("\n ")}\n"
262
+
263
+ assert e
264
+ assert e.backtrace[0].include?("(execjs):1"), e.backtrace.join("\n")
265
+ end
266
+ end
267
+
268
+ def test_eval_syntax_error
269
+ begin
270
+ ExecJS.eval(")")
271
+ flunk
272
+ rescue ExecJS::RuntimeError => e
273
+ assert e
274
+ assert e.backtrace[0].include?("(execjs):1"), e.backtrace.join("\n")
275
+ end
276
+ end
277
+
278
+ def test_compile_syntax_error
279
+ begin
280
+ ExecJS.compile(")")
281
+ flunk
282
+ rescue ExecJS::RuntimeError => e
283
+ assert e
284
+ assert e.backtrace[0].include?("(execjs):1"), e.backtrace.join("\n")
285
+ end
286
+ end
287
+
288
+ def test_exec_thrown_error
289
+ begin
290
+ ExecJS.exec("throw new Error('hello')")
291
+ flunk
292
+ rescue ExecJS::ProgramError => e
293
+ assert e
294
+ assert e.backtrace[0].include?("(execjs):1"), e.backtrace.join("\n")
295
+ end
296
+ end
297
+
298
+ def test_eval_thrown_error
299
+ begin
300
+ ExecJS.eval("(function(){ throw new Error('hello') })()")
301
+ flunk
302
+ rescue ExecJS::ProgramError => e
303
+ assert e
304
+ assert e.backtrace[0].include?("(execjs):1"), e.backtrace.join("\n")
305
+ end
306
+ end
307
+
308
+ def test_compile_thrown_error
309
+ begin
310
+ ExecJS.compile("throw new Error('hello')")
311
+ flunk
312
+ rescue ExecJS::ProgramError => e
313
+ assert e
314
+ assert e.backtrace[0].include?("(execjs):1"), e.backtrace.join("\n")
315
+ end
316
+ end
317
+
318
+ def test_exec_thrown_string
319
+ assert_raises ExecJS::ProgramError do
320
+ ExecJS.exec("throw 'hello'")
321
+ end
322
+ end
323
+
324
+ def test_eval_thrown_string
325
+ assert_raises ExecJS::ProgramError do
326
+ ExecJS.eval("(function(){ throw 'hello' })()")
327
+ end
328
+ end
329
+
330
+ def test_compile_thrown_string
331
+ assert_raises ExecJS::ProgramError do
332
+ ExecJS.compile("throw 'hello'")
333
+ end
334
+ end
335
+ =begin
336
+ def test_babel
337
+ skip if ExecJS.runtime.is_a?(ExecJS::RubyRhinoRuntime)
338
+
339
+ assert source = File.read(File.expand_path("../fixtures/babel.js", __FILE__))
340
+ source = <<-JS
341
+ var self = this;
342
+ #{source}
343
+ babel.eval = function(code) {
344
+ return eval(babel.transform(code)["code"]);
345
+ }
346
+ JS
347
+ context = ExecJS.compile(source)
348
+ assert_equal 64, context.call("babel.eval", "((x) => x * x)(8)")
349
+ end
350
+
351
+ def test_coffeescript
352
+ assert source = File.read(File.expand_path("../fixtures/coffee-script.js", __FILE__))
353
+ context = ExecJS.compile(source)
354
+ assert_equal 64, context.call("CoffeeScript.eval", "((x) -> x * x)(8)")
355
+ end
356
+
357
+ def test_uglify
358
+ assert source = File.read(File.expand_path("../fixtures/uglify.js", __FILE__))
359
+ source = <<-JS
360
+ #{source}
361
+
362
+ function uglify(source) {
363
+ var ast = UglifyJS.parse(source);
364
+ var stream = UglifyJS.OutputStream();
365
+ ast.print(stream);
366
+ return stream.toString();
367
+ }
368
+ JS
369
+ context = ExecJS.compile(source)
370
+ assert_equal "function foo(bar){return bar}",
371
+ context.call("uglify", "function foo(bar) {\n return bar;\n}")
372
+ end
373
+ =end
374
+ end
data/spec/nashorn_spec.rb CHANGED
@@ -4,7 +4,7 @@ describe Nashorn do
4
4
 
5
5
  it 'allows to eval js on Nashorn' do
6
6
  expect( Nashorn.eval '"4" + 2' ).to eql '42'
7
- expect( Nashorn.eval_js 'true + 10' ).to eql '11'
7
+ expect( Nashorn.eval_js 'true + 100' ).to eql 101
8
8
  end
9
9
 
10
10
  class NashornStub
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dienashorner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Karol Bucek
@@ -58,6 +58,8 @@ files:
58
58
  - lib/nashorn/context.rb
59
59
  - lib/nashorn/convert.rb
60
60
  - lib/nashorn/error.rb
61
+ - lib/nashorn/execjs/load.rb
62
+ - lib/nashorn/execjs/runtime.rb
61
63
  - lib/nashorn/ext.rb
62
64
  - lib/nashorn/rhino.rb
63
65
  - lib/nashorn/rhino/less.rb
@@ -76,6 +78,7 @@ files:
76
78
  - spec/nashorn/integration/loop/element2.js
77
79
  - spec/nashorn/integration_spec.rb
78
80
  - spec/nashorn/ruby_spec.rb
81
+ - spec/nashorn/test_execjs.rb
79
82
  - spec/nashorn_spec.rb
80
83
  - spec/spec_helper.rb
81
84
  homepage: https://github.com/kares/dienashorner