dienashorner 0.1.1

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