dienashorner 0.1.1

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.
@@ -0,0 +1,361 @@
1
+ require 'nashorn/ruby/access'
2
+
3
+ module Nashorn
4
+ module Ruby
5
+
6
+ @@access = nil
7
+ def self.access; @@access ||= Ruby::DefaultAccess.new end
8
+
9
+ def self.access=(access)
10
+ @@access = ( access.respond_to?(:get) && access.respond_to?(:put) ) ? access :
11
+ begin
12
+ access =
13
+ if access && ! access.is_a?(Class) # Ruby.access = :attribute
14
+ name = access.to_s.chomp('_access')
15
+ name = name[0, 1].capitalize << name[1..-1]
16
+ name = :"#{name}Access"
17
+ if Ruby.const_defined?(name)
18
+ Ruby.const_get(name) # e.g. Nashorn::Ruby::AttributeAccess
19
+ else
20
+ const_get(name) # e.g. Nashorn::Ruby::FooAccess
21
+ end
22
+ else # nil, false, Class
23
+ access
24
+ end
25
+ access.is_a?(Class) ? access.new : access
26
+ end
27
+ end
28
+
29
+ # Shared "JSObject" implementation.
30
+ module Scriptable
31
+ include JS::JSObject
32
+
33
+ attr_reader :unwrap
34
+
35
+ # @override JSObject
36
+ def getMember(name)
37
+ return nil if exclude?(name)
38
+ Ruby.access.get(unwrap, name) { super }
39
+ end
40
+
41
+ # @override JSObject
42
+ def hasMember(name)
43
+ return nil if exclude?(name)
44
+ Ruby.access.has(unwrap, name) { super }
45
+ end
46
+
47
+ # @override JSObject
48
+ def setMember(name, value)
49
+ return nil if exclude?(name)
50
+ Ruby.access.set(unwrap, name, value) { super }
51
+ end
52
+
53
+ # @override JSObject
54
+ def getSlot(name)
55
+ return nil if exclude?(name)
56
+ Ruby.access.get_slot(unwrap, name) { super }
57
+ end
58
+
59
+ # @override JSObject
60
+ def hasSlot(name)
61
+ return nil if exclude?(name)
62
+ Ruby.access.has_slot(unwrap, name) { super }
63
+ end
64
+
65
+ # @override JSObject
66
+ def setSlot(name, value)
67
+ return nil if exclude?(name)
68
+ Ruby.access.set_slot(unwrap, name, value) { super }
69
+ end
70
+
71
+ # @override JSObject
72
+ def removeMember(name)
73
+ setMember(name, nil) # TODO remove_method?
74
+ end
75
+
76
+ # @override JSObject
77
+ def keySet
78
+ ids = java.util.HashSet.new
79
+ super_set = super
80
+ unwrap.public_methods(false).each do |name|
81
+ next unless name = convert(name.to_s)
82
+ unless super_set.include?(name)
83
+ ids.add name.to_java # java.lang.String
84
+ end
85
+ end
86
+ ids
87
+ end
88
+
89
+ private
90
+
91
+ def convert(name)
92
+ if exclude?(name)
93
+ nil
94
+ elsif name.end_with?('=')
95
+ name[0...-1]
96
+ else
97
+ name
98
+ end
99
+ end
100
+
101
+ FETCH = '[]'.freeze
102
+ STORE = '[]='.freeze
103
+
104
+ def exclude?(name)
105
+ name.eql?(FETCH) || name.eql?(STORE)
106
+ end
107
+
108
+ end
109
+
110
+ class Object < JS::AbstractJSObject
111
+ include Scriptable
112
+
113
+ # wrap an arbitrary (ruby) object
114
+ def self.wrap(object)
115
+ Ruby.cache(object) { new(object) }
116
+ end
117
+
118
+ def initialize(object)
119
+ super()
120
+ @unwrap = object
121
+ end
122
+
123
+ # @override ECMA [[Class]] property
124
+ def getClassName
125
+ @unwrap.class.to_s # to_s handles 'nameless' classes as well
126
+ end
127
+
128
+ # @override
129
+ def isInstance(instance)
130
+ instance.class.equal? @unwrap
131
+ end
132
+
133
+ # @override
134
+ def isArray; @unwrap.is_a?(Array) end
135
+
136
+ # @override
137
+ def isFunction; false end
138
+
139
+ # @override
140
+ def isStrictFunction; false end
141
+
142
+ # @override
143
+ #def newObject(args); fail end
144
+
145
+ def toString
146
+ "[ruby #{getClassName}]" # [object User]
147
+ end
148
+
149
+ def equals(other)
150
+ other.is_a?(Object) && unwrap.eql?(other.unwrap)
151
+ end
152
+
153
+ def ==(other)
154
+ if other.is_a?(Object)
155
+ unwrap == other.unwrap
156
+ else
157
+ unwrap == other
158
+ end
159
+ end
160
+
161
+ def hashCode; @unwrap.hash end
162
+
163
+ def to_a
164
+ isArray ? @unwrap : super
165
+ end
166
+
167
+ end
168
+
169
+ class Function < JS::AbstractJSObject
170
+ include Scriptable
171
+
172
+ # wrap a callable (Method/Proc)
173
+ def self.wrap(callable)
174
+ Ruby.cache(callable.to_s) { new(callable) }
175
+ end
176
+
177
+ def initialize(callable)
178
+ super()
179
+ @unwrap = callable
180
+ end
181
+
182
+ # @override ECMA [[Class]] property
183
+ def getClassName
184
+ @unwrap.to_s # to_s handles 'nameless' classes as well
185
+ end
186
+
187
+ #def getFunctionName
188
+ # @callable.is_a?(Proc) ? "" : @callable.name
189
+ #end
190
+
191
+ # @override
192
+ def isInstance(instance)
193
+ instance.class.equal? @unwrap
194
+ end
195
+
196
+ # @override
197
+ def isArray; false end
198
+
199
+ # @override
200
+ def isFunction; true end
201
+
202
+ # @override
203
+ def isStrictFunction; false end
204
+
205
+ # @override
206
+ #def newObject(args); fail end
207
+
208
+ def length # getLength
209
+ arity = @unwrap.arity
210
+ arity < 0 ? ( arity + 1 ).abs : arity
211
+ end
212
+
213
+ def arity; length end
214
+
215
+ def equals(other) # JS == operator
216
+ return false unless other.is_a?(Function)
217
+ return true if unwrap == other.unwrap
218
+ # Method.== does check if their bind to the same object
219
+ # JS == means they might be bind to different objects :
220
+ unwrap.to_s == other.unwrap.to_s # "#<Method: Foo#bar>"
221
+ end
222
+
223
+ def ==(other)
224
+ if other.is_a?(Object)
225
+ unwrap == other.unwrap
226
+ else
227
+ unwrap == other
228
+ end
229
+ end
230
+
231
+ # @override
232
+ def call(*args) # call(Object thiz, Object... args)
233
+ # unless args.first.is_a?(JS::Context)
234
+ # return super # assume a Ruby #call
235
+ # end
236
+
237
+ # NOTE: distinguish a Ruby vs Java call here :
238
+ arr = args[1]
239
+ if arr && args.size == 2 && # Java Function#call dispatch
240
+ arr.respond_to?(:java_class) && arr.java_class.array?
241
+ this = args[0]; args = arr.to_a; java_args = true
242
+ end
243
+ # this = args.shift # Java Function#call dispatch
244
+
245
+ callable = @unwrap
246
+
247
+ if callable.is_a?(UnboundMethod)
248
+ this = args.shift unless java_args
249
+ callable = callable.bind(Nashorn.to_rb(this)) # TODO wrap TypeError ?
250
+ end
251
+ # JS function style :
252
+ if ( arity = callable.arity ) != -1 # (a1, *a).arity == -2
253
+ if arity > -1 && args.size > arity # omit 'redundant' arguments
254
+ args = args.slice(0, arity)
255
+ elsif arity > args.size || # fill 'missing' arguments
256
+ ( arity < -1 && (arity = arity.abs - 1) > args.size )
257
+ (arity - args.size).times { args.push(nil) }
258
+ end
259
+ end
260
+ rb_args = Nashorn.args_to_rb(args)
261
+ begin
262
+ result = callable.call(*rb_args)
263
+ rescue StandardError, ScriptError => e
264
+ raise e unless java_args
265
+ # TODO is this wrapping needed with __Nashorn__ ?
266
+ raise Ruby.wrap_error(e, e.backtrace) # thus `try { } catch (e)` works in JS
267
+ end
268
+ java_args ? Nashorn.to_js(result) : result
269
+ # Nashorn.to_js(result) # TODO do not convert if java_args ?
270
+ end
271
+
272
+ # make sure redefined :call is aliased not the one "inherited" from
273
+ # JS::BaseFunction#call when invoking __call__ (@see ext.rb)
274
+ alias_method :__call__, :call
275
+
276
+ end
277
+
278
+ class Constructor < Function
279
+
280
+ # wrap a ruby class as as constructor function
281
+ def self.wrap(klass)
282
+ # NOTE: caching here seems redundant since we implemented JS::Wrapper
283
+ # and a ruby class objects seems always the same ref under JRuby ...
284
+ Ruby.cache(klass) { new(klass) }
285
+ end
286
+
287
+ def initialize(klass)
288
+ super(klass.method(:new))
289
+ @klass = klass
290
+ end
291
+
292
+ def unwrap; @klass end
293
+
294
+ # @override ECMA [[Class]] property
295
+ def getClassName; @klass.name end
296
+
297
+ # @override
298
+ def isInstance(instance)
299
+ return false unless instance
300
+ return true if instance.is_a?(@klass)
301
+ instance.is_a?(Object) && instance.unwrap.is_a?(@klass)
302
+ end
303
+
304
+ # @override
305
+ def isArray; false end
306
+
307
+ # @override
308
+ def isFunction; true end
309
+
310
+ # @override
311
+ def isStrictFunction; false end
312
+
313
+ # @override
314
+ def newObject(args); @klass.new(*args) end
315
+
316
+ # override int BaseFunction#getLength()
317
+ #def getLength
318
+ # arity = @klass.instance_method(:initialize).arity
319
+ # arity < 0 ? ( arity + 1 ).abs : arity
320
+ #end
321
+
322
+ end
323
+
324
+ def self.cache(key, &block)
325
+ return yield
326
+
327
+ #context = JS::Context.getCurrentContext
328
+ #context ? context.cache(key, &block) : yield
329
+ end
330
+
331
+ # @private
332
+ class Exception < JS::NashornException
333
+
334
+ def initialize(value)
335
+ super wrap_value(value)
336
+ end
337
+
338
+ private
339
+
340
+ def wrap_value(value)
341
+ value.is_a?(Object) ? value : Object.wrap(value)
342
+ end
343
+
344
+ end
345
+
346
+ def self.wrap_error(e, backtrace = nil)
347
+ error = Exception.new(e)
348
+ error.set_backtrace(backtrace) if backtrace
349
+ error
350
+ end
351
+
352
+ end
353
+
354
+ # @private
355
+ RubyObject = Ruby::Object # :nodoc
356
+ # @private
357
+ RubyFunction = Ruby::Function # :nodoc
358
+ # @private
359
+ RubyConstructor = Ruby::Constructor # :nodoc
360
+
361
+ end
@@ -0,0 +1,3 @@
1
+ module Nashorn
2
+ VERSION = '0.1.1'
3
+ end
data/lib/nashorn.rb ADDED
@@ -0,0 +1,37 @@
1
+ begin
2
+ require 'java'
3
+ rescue LoadError => e
4
+ raise e if defined? JRUBY_VERSION
5
+ raise LoadError, "Please use JRuby with Nashorn : #{e.message}", e.backtrace
6
+ end
7
+
8
+ if ENV_JAVA['java.version'] < '1.8'
9
+ warn "Nashorn needs Java >= 8, JRE #{ENV_JAVA['java.version']} likely won't work!"
10
+ end
11
+
12
+ module Nashorn
13
+ # @private
14
+ module JS
15
+ include_package 'jdk.nashorn.api.scripting'
16
+ # include_package 'jdk.nashorn.internal.runtime'
17
+ ScriptObject = Java::JdkNashornInternalRuntime::ScriptObject rescue nil
18
+ end
19
+
20
+ def eval_js(source, options = {})
21
+ factory = JS::NashornScriptEngineFactory.new
22
+ factory.getScriptEngine.eval(source)
23
+ end
24
+
25
+ module_function :eval_js # due include Nashorn
26
+
27
+ class << self
28
+ alias_method :eval, :eval_js # Nashorn.eval '"4" + 2'
29
+ end
30
+
31
+ end
32
+
33
+ require 'nashorn/version'
34
+ require 'nashorn/ext'
35
+ require 'nashorn/convert'
36
+ require 'nashorn/error'
37
+ require 'nashorn/ruby'
@@ -0,0 +1,122 @@
1
+ require File.expand_path('../spec_helper', File.dirname(__FILE__))
2
+
3
+ describe Nashorn::Ruby::AttributeAccess do
4
+
5
+ before(:all) do
6
+ Nashorn::Ruby.access = Nashorn::Ruby::AttributeAccess.new
7
+ end
8
+
9
+ after(:all) do
10
+ Nashorn::Ruby.access = nil
11
+ end
12
+
13
+ class Meh
14
+
15
+ attr_reader :anAttr0
16
+ attr_accessor :the_attr_1
17
+
18
+ def initialize
19
+ @anAttr0 = nil
20
+ @the_attr_1 = 'attr_1'
21
+ @an_attr_2 = 'attr_2'
22
+ end
23
+
24
+ def theMethod0; @theMethod0; end
25
+
26
+ def a_method1; 1; end
27
+
28
+ def the_method_2; '2'; end
29
+
30
+ end
31
+
32
+ it "gets methods and instance variables" do
33
+ rb_object = Nashorn::Ruby::Object.wrap Meh.new
34
+
35
+ rb_object.get('anAttr0', nil).should be_nil
36
+ rb_object.get('the_attr_1', nil).should == 'attr_1'
37
+
38
+ # rb_object.get('an_attr_2', nil).should be(Rhino::JS::Scriptable::NOT_FOUND) # no reader
39
+ rb_object.get('an_attr_2', nil).should be nil
40
+
41
+ [ 'theMethod0', 'a_method1', 'the_method_2' ].each do |name|
42
+ rb_object.get(name, nil).should be_a(Nashorn::Ruby::Function)
43
+ end
44
+
45
+ # rb_object.get('non-existent-method', nil).should be(Rhino::JS::Scriptable::NOT_FOUND)
46
+ rb_object.get('non-existent-method', nil).should be nil
47
+ end
48
+
49
+ it "has methods and instance variables" do
50
+ rb_object = Nashorn::Ruby::Object.wrap Meh.new
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
55
+
56
+ [ 'theMethod0', 'a_method1', 'the_method_2' ].each do |name|
57
+ rb_object.has(name, nil).should be_true
58
+ end
59
+
60
+ rb_object.has('non-existent-method', nil).should be_false
61
+ end
62
+
63
+ it "puts using attr writer" do
64
+ start = mock('start')
65
+ start.expects(:put).never
66
+ rb_object = Nashorn::Ruby::Object.wrap Meh.new
67
+
68
+ rb_object.put('the_attr_1', start, 42)
69
+ rb_object.the_attr_1.should == 42
70
+ end
71
+
72
+ it "might set access as a symbol" do
73
+ prev_access = Nashorn::Ruby::access # Rhino::Ruby::Scriptable.access
74
+ module FooAccess; end # backward compatibility
75
+ class Foo2Access; end
76
+
77
+ begin
78
+
79
+ Nashorn::Ruby::access = nil
80
+ lambda {
81
+ Nashorn::Ruby::access = :attribute
82
+ }.should_not raise_error
83
+ Nashorn::Ruby::access.should be_a Nashorn::Ruby::AttributeAccess
84
+
85
+ Nashorn::Ruby::access = nil
86
+ lambda {
87
+ Nashorn::Ruby::access = :attribute_access
88
+ }.should_not raise_error
89
+ Nashorn::Ruby::access.should be_a Nashorn::Ruby::AttributeAccess
90
+
91
+ lambda {
92
+ Nashorn::Ruby::access = :foo
93
+ }.should_not raise_error
94
+ Nashorn::Ruby::access.should == FooAccess
95
+
96
+ lambda {
97
+ Nashorn::Ruby::access = :foo2
98
+ }.should_not raise_error
99
+ Nashorn::Ruby::access.should be_a Foo2Access
100
+
101
+ lambda {
102
+ Nashorn::Ruby::access = :bar
103
+ }.should raise_error
104
+
105
+ ensure
106
+ Nashorn::Ruby::access = prev_access # Rhino::Ruby::Scriptable.access = prev_access
107
+ end
108
+ end
109
+
110
+ it "is backward compatibile with the 'module' way" do
111
+ # Rhino::Ruby::AttributeAccess.respond_to?(:has).should be true
112
+ # Rhino::Ruby::AttributeAccess.respond_to?(:get).should be true
113
+ # Rhino::Ruby::AttributeAccess.respond_to?(:put).should be true
114
+
115
+ Nashorn::Ruby::access = Nashorn::Ruby::AttributeAccess
116
+
117
+ rb_object = Nashorn::Ruby::Object.wrap Meh.new
118
+ rb_object.get('theMethod0', nil).should be_a(Nashorn::Ruby::Function)
119
+ rb_object.has('non-existent-method', nil).should be false
120
+ end
121
+
122
+ end