dienashorner 0.1.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
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