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.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.rspec +1 -0
- data/Gemfile +13 -0
- data/LICENSE +201 -0
- data/README.md +157 -0
- data/Rakefile +18 -0
- data/dienashorner.gemspec +24 -0
- data/lib/nashorn/context.rb +188 -0
- data/lib/nashorn/convert.rb +57 -0
- data/lib/nashorn/error.rb +105 -0
- data/lib/nashorn/ext.rb +201 -0
- data/lib/nashorn/rhino/less.rb +84 -0
- data/lib/nashorn/rhino.rb +34 -0
- data/lib/nashorn/ruby/access.rb +68 -0
- data/lib/nashorn/ruby/attribute_access.rb +41 -0
- data/lib/nashorn/ruby/default_access.rb +37 -0
- data/lib/nashorn/ruby.rb +361 -0
- data/lib/nashorn/version.rb +3 -0
- data/lib/nashorn.rb +37 -0
- data/spec/nashorn/access_spec.rb +122 -0
- data/spec/nashorn/error_spec.rb +199 -0
- data/spec/nashorn/integration/bar.js +11 -0
- data/spec/nashorn/integration/foo.js +7 -0
- data/spec/nashorn/integration/index.js +7 -0
- data/spec/nashorn/integration/loop/element1.js +3 -0
- data/spec/nashorn/integration/loop/element2.js +8 -0
- data/spec/nashorn/integration/loop.js +3 -0
- data/spec/nashorn/integration_spec.rb +149 -0
- data/spec/nashorn/ruby_spec.rb +352 -0
- data/spec/nashorn_spec.rb +23 -0
- data/spec/spec_helper.rb +38 -0
- metadata +105 -0
data/lib/nashorn/ruby.rb
ADDED
@@ -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
|
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
|