therubyrhino 1.73.1 → 1.73.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.travis.yml +6 -0
- data/README.rdoc +55 -7
- data/Rakefile +5 -1
- data/lib/rhino/context.rb +159 -22
- data/lib/rhino/error.rb +13 -8
- data/lib/rhino/rhino_ext.rb +5 -5
- data/lib/rhino/ruby.rb +1 -1
- data/lib/rhino/ruby/attribute_access.rb +8 -6
- data/lib/rhino/version.rb +1 -1
- data/lib/rhino/wormhole.rb +9 -2
- data/spec/rhino/context_spec.rb +74 -0
- data/spec/rhino/error_spec.rb +29 -4
- data/spec/rhino/wormhole_spec.rb +44 -4
- data/therubyrhino.gemspec +1 -1
- metadata +5 -4
data/.travis.yml
ADDED
data/README.rdoc
CHANGED
@@ -47,18 +47,18 @@ Embed the Mozilla Rhino Javascript interpreter into Ruby
|
|
47
47
|
|
48
48
|
Rhino::Context.open do |context|
|
49
49
|
context["math"] = MyMath.new
|
50
|
-
context.eval("math.plus(20,22)") #=> 42
|
50
|
+
context.eval("math.plus(20, 22)") #=> 42
|
51
51
|
end
|
52
52
|
|
53
53
|
# make a ruby object *be* your javascript environment
|
54
54
|
math = MyMath.new
|
55
55
|
Rhino::Context.open(:with => math) do |context|
|
56
|
-
context.eval("plus(20,22)") #=> 42
|
56
|
+
context.eval("plus(20, 22)") #=> 42
|
57
57
|
end
|
58
58
|
|
59
59
|
#or the equivalent
|
60
60
|
|
61
|
-
math.eval_js("plus(20,22)")
|
61
|
+
math.eval_js("plus(20, 22)")
|
62
62
|
|
63
63
|
# Configure your embedding setup
|
64
64
|
|
@@ -69,14 +69,25 @@ Embed the Mozilla Rhino Javascript interpreter into Ruby
|
|
69
69
|
|
70
70
|
#Turn on Java integration from javascript (probably a bad idea)
|
71
71
|
Rhino::Context.open(:java => true) do |context|
|
72
|
-
context.eval("java.lang.System.exit()") #it's dangerous!
|
72
|
+
context.eval("java.lang.System.exit()") # it's dangerous!
|
73
73
|
end
|
74
74
|
|
75
75
|
#limit the number of instructions that can be executed in order to prevent
|
76
76
|
#rogue scripts
|
77
|
-
Rhino::Context.open do |context|
|
77
|
+
Rhino::Context.open(:restrictable => true) do |context|
|
78
78
|
context.instruction_limit = 100000
|
79
|
-
context.eval("while (true);") # =>
|
79
|
+
context.eval("while (true);") # => Rhino::RunawayScriptError
|
80
|
+
end
|
81
|
+
|
82
|
+
#limit the time a script executes
|
83
|
+
#rogue scripts
|
84
|
+
Rhino::Context.open(:restrictable => true, :java => true) do |context|
|
85
|
+
context.timeout_limit = 1.5 # seconds
|
86
|
+
context.eval %Q{
|
87
|
+
for (var i = 0; i < 100; i++) {
|
88
|
+
java.lang.Thread.sleep(100);
|
89
|
+
}
|
90
|
+
} # => Rhino::ScriptTimeoutError
|
80
91
|
end
|
81
92
|
|
82
93
|
==== Different ways of loading javascript source
|
@@ -93,6 +104,43 @@ In addition to just evaluating strings, you can also use streams such as files.
|
|
93
104
|
context.load("mysource.js")
|
94
105
|
end
|
95
106
|
|
107
|
+
==== Configurable Ruby access
|
108
|
+
|
109
|
+
By default accessing Ruby objects from javascript is compatible with therubyracer:
|
110
|
+
https://github.com/cowboyd/therubyracer/wiki/Accessing-Ruby-Objects-From-JavaScript
|
111
|
+
|
112
|
+
Thus you end-up calling arbitrary no-arg methods as if they were javascript properties,
|
113
|
+
since instance accessors (properties) and methods (functions) are indistinguishable:
|
114
|
+
|
115
|
+
Rhino::Context.open do |context|
|
116
|
+
context['Time'] = Time
|
117
|
+
context.eval('Time.now')
|
118
|
+
end
|
119
|
+
|
120
|
+
However, you can customize this behavior and there's another access implementation
|
121
|
+
that attempts to mirror only attributes as properties as close as possible:
|
122
|
+
|
123
|
+
class Foo
|
124
|
+
attr_accessor :bar
|
125
|
+
|
126
|
+
def initialize
|
127
|
+
@bar = "bar"
|
128
|
+
end
|
129
|
+
|
130
|
+
def check_bar
|
131
|
+
bar == "bar"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
Rhino::Ruby::Scriptable.access = Rhino::Ruby::AttributeAccess
|
136
|
+
Rhino::Context.open do |context|
|
137
|
+
context['Foo'] = Foo
|
138
|
+
context.eval('var foo = new Foo()')
|
139
|
+
context.eval('foo.bar') # get property using reader
|
140
|
+
context.eval('foo.bar = null') # set property using writer
|
141
|
+
context.eval('foo.check_bar()') # called like a function
|
142
|
+
end
|
143
|
+
|
96
144
|
=== Safe by default
|
97
145
|
|
98
146
|
The Ruby Rhino is designed to let you evaluate javascript as safely as possible unless you tell it to do something more
|
@@ -135,7 +183,7 @@ exposed by default. E.g.
|
|
135
183
|
|
136
184
|
(The MIT License)
|
137
185
|
|
138
|
-
Copyright (c) 2009 Charles Lowell
|
186
|
+
Copyright (c) 2009-2012 Charles Lowell
|
139
187
|
|
140
188
|
Permission is hereby granted, free of charge, to any person obtaining
|
141
189
|
a copy of this software and associated documentation files (the
|
data/Rakefile
CHANGED
data/lib/rhino/context.rb
CHANGED
@@ -46,9 +46,18 @@ module Rhino
|
|
46
46
|
def eval(javascript)
|
47
47
|
new.eval(javascript)
|
48
48
|
end
|
49
|
-
|
49
|
+
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
|
+
@@default_factory = nil
|
53
|
+
def self.default_factory
|
54
|
+
@@default_factory ||= ContextFactory.new
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.default_factory=(factory)
|
58
|
+
@@default_factory = factory
|
59
|
+
end
|
60
|
+
|
52
61
|
attr_reader :scope
|
53
62
|
|
54
63
|
# Create a new javascript environment for executing javascript and ruby code.
|
@@ -56,8 +65,9 @@ module Rhino
|
|
56
65
|
# * <tt>:with</tt> - use this ruby object as the root scope for all javascript that is evaluated
|
57
66
|
# * <tt>:java</tt> - if true, java packages will be accessible from within javascript
|
58
67
|
def initialize(options = {}) #:nodoc:
|
59
|
-
|
60
|
-
|
68
|
+
factory = options[:factory] ||
|
69
|
+
(options[:restrictable] ? RestrictableContextFactory.instance : self.class.default_factory)
|
70
|
+
factory.call do |context|
|
61
71
|
@native = context
|
62
72
|
@global = @native.initStandardObjects(nil, options[:sealed] == true)
|
63
73
|
if with = options[:with]
|
@@ -73,7 +83,12 @@ module Rhino
|
|
73
83
|
end
|
74
84
|
end
|
75
85
|
end
|
76
|
-
|
86
|
+
|
87
|
+
# Returns the ContextFactory used while creating this context.
|
88
|
+
def factory
|
89
|
+
@native.getFactory
|
90
|
+
end
|
91
|
+
|
77
92
|
# Read a value from the global scope of this context
|
78
93
|
def [](key)
|
79
94
|
@scope[key]
|
@@ -117,14 +132,44 @@ module Rhino
|
|
117
132
|
end
|
118
133
|
end
|
119
134
|
|
135
|
+
# Returns true if this context supports restrictions.
|
136
|
+
def restrictable?
|
137
|
+
@native.is_a?(RestrictableContextFactory::Context)
|
138
|
+
end
|
139
|
+
|
140
|
+
def instruction_limit
|
141
|
+
restrictable? ? @native.instruction_limit : false
|
142
|
+
end
|
143
|
+
|
120
144
|
# Set the maximum number of instructions that this context will execute.
|
121
|
-
# If this instruction limit is exceeded, then a Rhino::RunawayScriptError
|
122
|
-
# will be raised
|
145
|
+
# If this instruction limit is exceeded, then a #Rhino::RunawayScriptError
|
146
|
+
# will be raised.
|
123
147
|
def instruction_limit=(limit)
|
124
|
-
|
125
|
-
|
148
|
+
if restrictable?
|
149
|
+
@native.instruction_limit = limit
|
150
|
+
else
|
151
|
+
raise "setting an instruction_limit has no effect on this context, use " +
|
152
|
+
"Context.open(:restricted => true) to gain a restrictable instance"
|
153
|
+
end
|
126
154
|
end
|
127
155
|
|
156
|
+
def timeout_limit
|
157
|
+
restrictable? ? @native.timeout_limit : false
|
158
|
+
end
|
159
|
+
|
160
|
+
# Set the duration (in seconds e.g. 1.5) this context is allowed to execute.
|
161
|
+
# After the timeout passes (no matter if any JS has been evaluated) and this
|
162
|
+
# context is still attempted to run code, a #Rhino::ScriptTimeoutError will
|
163
|
+
# be raised.
|
164
|
+
def timeout_limit=(limit)
|
165
|
+
if restrictable?
|
166
|
+
@native.timeout_limit = limit
|
167
|
+
else
|
168
|
+
raise "setting an timeout_limit has no effect on this context, use " +
|
169
|
+
"Context.open(:restricted => true) to gain a restrictable instance"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
128
173
|
def optimization_level
|
129
174
|
@native.getOptimizationLevel
|
130
175
|
end
|
@@ -134,7 +179,7 @@ module Rhino
|
|
134
179
|
# By using the -1 optimization level, you tell Rhino to run in interpretative mode,
|
135
180
|
# taking a hit to performance but escaping the Java bytecode limit.
|
136
181
|
def optimization_level=(level)
|
137
|
-
if
|
182
|
+
if JS::Context.isValidOptimizationLevel(level)
|
138
183
|
@native.setOptimizationLevel(level)
|
139
184
|
level
|
140
185
|
else
|
@@ -157,13 +202,12 @@ module Rhino
|
|
157
202
|
def version=(version)
|
158
203
|
const = version.to_s.gsub('.', '_').upcase
|
159
204
|
const = "VERSION_#{const}" if const[0, 7] != 'VERSION'
|
160
|
-
|
161
|
-
|
162
|
-
const_value = js_context.const_get(const)
|
205
|
+
if JS::Context.constants.find { |c| c.to_s == const }
|
206
|
+
const_value = JS::Context.const_get(const)
|
163
207
|
@native.setLanguageVersion(const_value)
|
164
208
|
const_value
|
165
209
|
else
|
166
|
-
@native.setLanguageVersion(
|
210
|
+
@native.setLanguageVersion(JS::Context::VERSION_DEFAULT)
|
167
211
|
nil
|
168
212
|
end
|
169
213
|
end
|
@@ -179,11 +223,11 @@ module Rhino
|
|
179
223
|
private
|
180
224
|
|
181
225
|
def do_open
|
226
|
+
factory.enterContext(@native)
|
182
227
|
begin
|
183
|
-
@factory.enterContext(@native)
|
184
228
|
yield self
|
185
229
|
ensure
|
186
|
-
|
230
|
+
factory.exit
|
187
231
|
end
|
188
232
|
end
|
189
233
|
|
@@ -216,22 +260,115 @@ module Rhino
|
|
216
260
|
|
217
261
|
end
|
218
262
|
|
219
|
-
|
263
|
+
ContextFactory = JS::ContextFactory # :nodoc: backward compatibility
|
220
264
|
|
221
|
-
|
222
|
-
|
265
|
+
class RestrictableContextFactory < ContextFactory # :nodoc:
|
266
|
+
|
267
|
+
@@instance = nil
|
268
|
+
def self.instance
|
269
|
+
@@instance ||= new
|
270
|
+
end
|
271
|
+
|
272
|
+
# protected Context makeContext()
|
273
|
+
def makeContext
|
274
|
+
Context.new(self)
|
223
275
|
end
|
276
|
+
|
277
|
+
# protected void observeInstructionCount(Context context, int instructionCount)
|
278
|
+
def observeInstructionCount(context, count)
|
279
|
+
context.check!(count) if context.is_a?(Context)
|
280
|
+
end
|
281
|
+
|
282
|
+
# protected Object doTopCall(Callable callable, Context context,
|
283
|
+
# Scriptable scope, Scriptable thisObj, Object[] args)
|
284
|
+
def doTopCall(callable, context, scope, this, args)
|
285
|
+
context.reset! if context.is_a?(Context)
|
286
|
+
super
|
287
|
+
end
|
288
|
+
|
289
|
+
class Context < JS::Context # :nodoc:
|
290
|
+
|
291
|
+
def initialize(factory)
|
292
|
+
super(factory)
|
293
|
+
reset!
|
294
|
+
end
|
224
295
|
|
225
|
-
|
226
|
-
|
296
|
+
attr_reader :instruction_limit
|
297
|
+
|
298
|
+
def instruction_limit=(limit)
|
299
|
+
treshold = getInstructionObserverThreshold
|
300
|
+
if limit && (treshold == 0 || treshold > limit)
|
301
|
+
setInstructionObserverThreshold(limit)
|
302
|
+
end
|
303
|
+
@instruction_limit = limit
|
304
|
+
end
|
305
|
+
|
306
|
+
attr_reader :instruction_count
|
307
|
+
|
308
|
+
TIMEOUT_INSTRUCTION_TRESHOLD = 42
|
309
|
+
|
310
|
+
attr_reader :timeout_limit
|
311
|
+
|
312
|
+
def timeout_limit=(limit) # in seconds
|
313
|
+
treshold = getInstructionObserverThreshold
|
314
|
+
if limit && (treshold == 0 || treshold > TIMEOUT_INSTRUCTION_TRESHOLD)
|
315
|
+
setInstructionObserverThreshold(TIMEOUT_INSTRUCTION_TRESHOLD)
|
316
|
+
end
|
317
|
+
@timeout_limit = limit
|
318
|
+
end
|
319
|
+
|
320
|
+
attr_reader :start_time
|
321
|
+
|
322
|
+
def check!(count = nil)
|
323
|
+
@instruction_count += count if count
|
324
|
+
check_instruction_limit!
|
325
|
+
check_timeout_limit!(count)
|
326
|
+
end
|
327
|
+
|
328
|
+
def check_instruction_limit!
|
329
|
+
if instruction_limit && instruction_count > instruction_limit
|
330
|
+
raise RunawayScriptError, "script exceeded allowable instruction count: #{instruction_limit}"
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
def check_timeout_limit!(count = nil)
|
335
|
+
if timeout_limit
|
336
|
+
elapsed_time = Time.now.to_f - start_time.to_f
|
337
|
+
if elapsed_time > timeout_limit
|
338
|
+
raise ScriptTimeoutError, "script exceeded timeout: #{timeout_limit} seconds"
|
339
|
+
end
|
340
|
+
# adapt instruction treshold as needed :
|
341
|
+
if count
|
342
|
+
treshold = getInstructionObserverThreshold
|
343
|
+
if elapsed_time * 2 < timeout_limit
|
344
|
+
next_treshold_guess = treshold * 2
|
345
|
+
if instruction_limit && instruction_limit < next_treshold_guess
|
346
|
+
setInstructionObserverThreshold(instruction_limit)
|
347
|
+
else
|
348
|
+
setInstructionObserverThreshold(next_treshold_guess)
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
def reset!
|
356
|
+
@instruction_count = 0
|
357
|
+
@start_time = Time.now
|
358
|
+
self
|
359
|
+
end
|
360
|
+
|
227
361
|
end
|
228
362
|
|
229
363
|
end
|
230
|
-
|
364
|
+
|
231
365
|
class ContextError < StandardError # :nodoc:
|
232
366
|
end
|
233
367
|
|
234
368
|
class RunawayScriptError < ContextError # :nodoc:
|
235
369
|
end
|
370
|
+
|
371
|
+
class ScriptTimeoutError < ContextError # :nodoc:
|
372
|
+
end
|
236
373
|
|
237
374
|
end
|
data/lib/rhino/error.rb
CHANGED
@@ -5,14 +5,8 @@ module Rhino
|
|
5
5
|
|
6
6
|
def initialize(native)
|
7
7
|
@native = native # NativeException wrapping a Java Throwable
|
8
|
-
|
9
|
-
|
10
|
-
def message
|
11
|
-
cause ? cause.details : @native.to_s
|
12
|
-
end
|
13
|
-
|
14
|
-
def to_s
|
15
|
-
super
|
8
|
+
message = value ? value : ( cause ? cause.details : @native )
|
9
|
+
super(message)
|
16
10
|
end
|
17
11
|
|
18
12
|
# most likely a Rhino::JS::JavaScriptException
|
@@ -20,6 +14,17 @@ module Rhino
|
|
20
14
|
@native.respond_to?(:cause) ? @native.cause : nil
|
21
15
|
end
|
22
16
|
|
17
|
+
def value
|
18
|
+
return @value if defined?(@value)
|
19
|
+
if cause.respond_to?(:value) # e.g. JavaScriptException.getValue
|
20
|
+
@value = cause.value
|
21
|
+
elsif ( unwrap = self.unwrap ) && unwrap.respond_to?(:value)
|
22
|
+
@value = unwrap.value
|
23
|
+
else
|
24
|
+
@value = nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
23
28
|
def unwrap
|
24
29
|
return @unwrap if defined?(@unwrap)
|
25
30
|
cause = self.cause
|
data/lib/rhino/rhino_ext.rb
CHANGED
@@ -87,11 +87,11 @@ class Java::OrgMozillaJavascript::ScriptableObject
|
|
87
87
|
|
88
88
|
# Delegate methods to JS object if possible when called from Ruby.
|
89
89
|
def method_missing(name, *args)
|
90
|
-
|
91
|
-
if
|
92
|
-
self[
|
90
|
+
name_str = name.to_s
|
91
|
+
if name_str[-1, 1] == '=' && args.size == 1 # writer -> JS put
|
92
|
+
self[ name_str[0...-1] ] = args[0]
|
93
93
|
else
|
94
|
-
if property = self[
|
94
|
+
if property = self[name_str]
|
95
95
|
if property.is_a?(Rhino::JS::Function)
|
96
96
|
begin
|
97
97
|
context = Rhino::JS::Context.enter
|
@@ -103,7 +103,7 @@ class Java::OrgMozillaJavascript::ScriptableObject
|
|
103
103
|
end
|
104
104
|
else
|
105
105
|
if args.size > 0
|
106
|
-
raise ArgumentError, "can't #{
|
106
|
+
raise ArgumentError, "can't call '#{name_str}' with args: #{args.inspect} as it's a property"
|
107
107
|
end
|
108
108
|
Rhino.to_ruby property
|
109
109
|
end
|
data/lib/rhino/ruby.rb
CHANGED
@@ -32,7 +32,7 @@ module Rhino
|
|
32
32
|
ids = []
|
33
33
|
unwrap.public_methods(false).each do |name|
|
34
34
|
name = name[0...-1] if name[-1, 1] == '=' # 'foo=' ... 'foo'
|
35
|
-
name = name.to_java # java.lang.String
|
35
|
+
name = name.to_s.to_java # java.lang.String
|
36
36
|
ids << name unless ids.include?(name)
|
37
37
|
end
|
38
38
|
super.each { |id| ids.unshift(id) }
|
@@ -4,7 +4,7 @@ module Rhino
|
|
4
4
|
|
5
5
|
def self.has(object, name, scope)
|
6
6
|
if object.respond_to?(name.to_s) ||
|
7
|
-
object.respond_to?("#{name}=") # might have a writer but no reader
|
7
|
+
object.respond_to?(:"#{name}=") # might have a writer but no reader
|
8
8
|
return true
|
9
9
|
end
|
10
10
|
# try [](name) method :
|
@@ -15,10 +15,12 @@ module Rhino
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def self.get(object, name, scope)
|
18
|
-
|
19
|
-
|
18
|
+
name_sym = name.to_s.to_sym
|
19
|
+
if object.respond_to?(name_sym)
|
20
|
+
method = object.method(name_sym)
|
20
21
|
if method.arity == 0 && # check if it is an attr_reader
|
21
|
-
( object.respond_to?("#{name}=") ||
|
22
|
+
( object.respond_to?(:"#{name}=") ||
|
23
|
+
object.instance_variables.find { |var| var.to_sym == :"@#{name}" } )
|
22
24
|
begin
|
23
25
|
return Rhino.to_javascript(method.call, scope)
|
24
26
|
rescue => e
|
@@ -27,7 +29,7 @@ module Rhino
|
|
27
29
|
else
|
28
30
|
return Function.wrap(method.unbind)
|
29
31
|
end
|
30
|
-
elsif object.respond_to?("#{name}=")
|
32
|
+
elsif object.respond_to?(:"#{name}=")
|
31
33
|
return nil # it does have the property but is non readable
|
32
34
|
end
|
33
35
|
# try [](name) method :
|
@@ -40,7 +42,7 @@ module Rhino
|
|
40
42
|
end
|
41
43
|
|
42
44
|
def self.put(object, name, value)
|
43
|
-
if object.respond_to?(set_name = "#{name}=")
|
45
|
+
if object.respond_to?(set_name = :"#{name}=")
|
44
46
|
return object.send(set_name, Rhino.to_ruby(value))
|
45
47
|
end
|
46
48
|
# try []=(name, value) method :
|
data/lib/rhino/version.rb
CHANGED
data/lib/rhino/wormhole.rb
CHANGED
@@ -43,10 +43,17 @@ module Rhino
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def array_to_javascript(rb_array, scope = nil)
|
46
|
+
# First convert all array elements to their javascript equivalents and
|
47
|
+
# then invoke to_java below in order to create a Java array. This allows
|
48
|
+
# arrays with nested hashes to be converted properly.
|
49
|
+
converted_rb_array = rb_array.map do |rb_element|
|
50
|
+
to_javascript(rb_element, scope)
|
51
|
+
end
|
52
|
+
|
46
53
|
if scope && context = JS::Context.getCurrentContext
|
47
|
-
context.newArray(scope,
|
54
|
+
context.newArray(scope, converted_rb_array.to_java)
|
48
55
|
else
|
49
|
-
JS::NativeArray.new(
|
56
|
+
JS::NativeArray.new(converted_rb_array.to_java)
|
50
57
|
end
|
51
58
|
end
|
52
59
|
|
data/spec/rhino/context_spec.rb
CHANGED
@@ -65,5 +65,79 @@ describe Rhino::Context do
|
|
65
65
|
context.version = '1.7'
|
66
66
|
context.version.should == 1.7
|
67
67
|
end
|
68
|
+
|
69
|
+
it "should have a (shared) factory by default" do
|
70
|
+
context1 = Rhino::Context.new
|
71
|
+
context1.factory.should_not be nil
|
72
|
+
context1.factory.should be_a(Rhino::JS::ContextFactory)
|
73
|
+
|
74
|
+
context1.factory.should be Rhino::Context.default_factory
|
75
|
+
|
76
|
+
context2 = Rhino::Context.new
|
77
|
+
context2.factory.should be context1.factory
|
78
|
+
end
|
79
|
+
|
80
|
+
it "allows limiting instruction count" do
|
81
|
+
context = Rhino::Context.new :restrictable => true
|
82
|
+
context.instruction_limit = 100
|
83
|
+
lambda {
|
84
|
+
context.eval %Q{ for (var i = 0; i < 100; i++) Number(i).toString(); }
|
85
|
+
}.should raise_error(Rhino::RunawayScriptError)
|
86
|
+
|
87
|
+
context.instruction_limit = nil
|
88
|
+
lambda {
|
89
|
+
context.eval %Q{ for (var i = 0; i < 100; i++) Number(i).toString(); }
|
90
|
+
}.should_not raise_error(Rhino::RunawayScriptError)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "allows a timeout limit per context" do
|
94
|
+
context1 = Rhino::Context.new :restrictable => true, :java => true
|
95
|
+
context1.timeout_limit = 0.3
|
96
|
+
|
97
|
+
context2 = Rhino::Context.new :restrictable => true, :java => true
|
98
|
+
context2.timeout_limit = 0.3
|
99
|
+
|
100
|
+
lambda {
|
101
|
+
context2.eval %Q{
|
102
|
+
var notDone = true;
|
103
|
+
(function foo() {
|
104
|
+
if (notDone) {
|
105
|
+
notDone = false;
|
106
|
+
java.lang.Thread.sleep(300);
|
107
|
+
foo();
|
108
|
+
}
|
109
|
+
})();
|
110
|
+
}
|
111
|
+
}.should raise_error(Rhino::ScriptTimeoutError)
|
112
|
+
|
113
|
+
lambda {
|
114
|
+
context1.eval %Q{
|
115
|
+
var notDone = true;
|
116
|
+
(function foo {
|
117
|
+
if (notDone) {
|
118
|
+
notDone = false;
|
119
|
+
java.lang.Thread.sleep(100);
|
120
|
+
foo();
|
121
|
+
}
|
122
|
+
})();
|
123
|
+
}
|
124
|
+
}.should_not raise_error(Rhino::RunawayScriptError)
|
125
|
+
end
|
126
|
+
|
127
|
+
it "allows instruction and timeout limits at the same time" do
|
128
|
+
context = Rhino::Context.new :restrictable => true, :java => true
|
129
|
+
context.timeout_limit = 0.5
|
130
|
+
context.instruction_limit = 10000
|
131
|
+
lambda {
|
132
|
+
context.eval %Q{ for (var i = 0; i < 100; i++) { java.lang.Thread.sleep(100); } }
|
133
|
+
}.should raise_error(Rhino::ScriptTimeoutError)
|
134
|
+
|
135
|
+
context = Rhino::Context.new :restrictable => true, :java => true
|
136
|
+
context.timeout_limit = 0.5
|
137
|
+
context.instruction_limit = 1000
|
138
|
+
lambda {
|
139
|
+
context.eval %Q{ for (var i = 0; i < 100; i++) { java.lang.Thread.sleep(10); } }
|
140
|
+
}.should raise_error(Rhino::RunawayScriptError)
|
141
|
+
end
|
68
142
|
|
69
143
|
end
|
data/spec/rhino/error_spec.rb
CHANGED
@@ -6,9 +6,9 @@ describe Rhino::JSError do
|
|
6
6
|
js_error = Rhino::JSError.new 'an error message'
|
7
7
|
lambda { js_error.to_s && js_error.inspect }.should_not raise_error
|
8
8
|
|
9
|
-
js_error.cause.should
|
9
|
+
js_error.cause.should be nil
|
10
10
|
js_error.message.should == 'an error message'
|
11
|
-
js_error.javascript_backtrace.should
|
11
|
+
js_error.javascript_backtrace.should be nil
|
12
12
|
end
|
13
13
|
|
14
14
|
it "might wrap a RhinoException wrapped in a NativeException like error" do
|
@@ -30,9 +30,34 @@ describe Rhino::JSError do
|
|
30
30
|
js_error = Rhino::JSError.new native_error_class.new(rhino_e)
|
31
31
|
lambda { js_error.to_s && js_error.inspect }.should_not raise_error
|
32
32
|
|
33
|
-
js_error.cause.should be
|
33
|
+
js_error.cause.should be rhino_e
|
34
34
|
js_error.message.should == '42'
|
35
|
-
js_error.javascript_backtrace.should_not
|
35
|
+
js_error.javascript_backtrace.should_not be nil
|
36
|
+
end
|
37
|
+
|
38
|
+
it "keeps the thrown javascript object value" do
|
39
|
+
begin
|
40
|
+
Rhino::Context.eval "throw { foo: 'bar' }"
|
41
|
+
rescue => e
|
42
|
+
e.should be_a(Rhino::JSError)
|
43
|
+
e.value.should be_a(Rhino::JS::NativeObject)
|
44
|
+
e.value['foo'].should == 'bar'
|
45
|
+
e.value.should == e.message
|
46
|
+
else
|
47
|
+
fail "expected to rescue"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it "keeps the thrown javascript string value" do
|
52
|
+
begin
|
53
|
+
Rhino::Context.eval "throw 'mehehehe'"
|
54
|
+
rescue => e
|
55
|
+
e.should be_a(Rhino::JSError)
|
56
|
+
e.value.should == 'mehehehe'
|
57
|
+
e.value.should == e.message
|
58
|
+
else
|
59
|
+
fail "expected to rescue"
|
60
|
+
end
|
36
61
|
end
|
37
62
|
|
38
63
|
end
|
data/spec/rhino/wormhole_spec.rb
CHANGED
@@ -91,6 +91,46 @@ describe Rhino::To do
|
|
91
91
|
h.prototype.should be_nil # this is how Rhino works !
|
92
92
|
end
|
93
93
|
end
|
94
|
+
|
95
|
+
it "converts deeply nested ruby hashes into native objects" do
|
96
|
+
hash = {
|
97
|
+
:array => [
|
98
|
+
{
|
99
|
+
:breed => "Pug"
|
100
|
+
},
|
101
|
+
{
|
102
|
+
:breed => "English Bulldog"
|
103
|
+
},
|
104
|
+
{
|
105
|
+
:breed => [
|
106
|
+
"Pug",
|
107
|
+
"Beagle"
|
108
|
+
]
|
109
|
+
}
|
110
|
+
]
|
111
|
+
}
|
112
|
+
|
113
|
+
Rhino.to_javascript(hash).tap do |h|
|
114
|
+
h.should be_kind_of(Rhino::JS::NativeObject)
|
115
|
+
|
116
|
+
a = h.get("array", h)
|
117
|
+
a.should be_kind_of(Rhino::JS::NativeArray)
|
118
|
+
|
119
|
+
element0 = a.get(0,a)
|
120
|
+
element0.should be_kind_of(Rhino::JS::NativeObject)
|
121
|
+
element0.get("breed", element0).should == "Pug"
|
122
|
+
|
123
|
+
element2 = a.get(2,a)
|
124
|
+
element2.should be_kind_of(Rhino::JS::NativeObject)
|
125
|
+
|
126
|
+
nested_array = element2.get("breed", element2)
|
127
|
+
nested_array.should be_kind_of(Rhino::JS::NativeArray)
|
128
|
+
nested_array.get(0,nested_array).should == "Pug"
|
129
|
+
nested_array.get(1,nested_array).should == "Beagle"
|
130
|
+
|
131
|
+
h.prototype.should be_nil # this is how Rhino works !
|
132
|
+
end
|
133
|
+
end
|
94
134
|
|
95
135
|
describe "with a scope" do
|
96
136
|
|
@@ -132,12 +172,12 @@ describe Rhino::To do
|
|
132
172
|
it "converts procs and methods into native functions" do
|
133
173
|
Rhino.to_javascript(lambda {|lhs,rhs| lhs * rhs}).tap do |f|
|
134
174
|
f.should be_kind_of(Rhino::JS::Function)
|
135
|
-
f.call(nil, nil, nil, [7,6]).should be(42)
|
175
|
+
f.call(nil, nil, nil, [7, 6].to_java).should be(42)
|
136
176
|
end
|
137
177
|
|
138
|
-
Rhino.to_javascript("foo,bar,baz".method(:split)).tap do |
|
139
|
-
|
140
|
-
Rhino.to_ruby(
|
178
|
+
Rhino.to_javascript("foo,bar,baz".method(:split)).tap do |f|
|
179
|
+
f.should be_kind_of(Rhino::JS::Function)
|
180
|
+
Rhino.to_ruby(f.call(nil, nil, nil, [','].to_java)).should == ['foo', 'bar', 'baz']
|
141
181
|
end
|
142
182
|
end
|
143
183
|
|
data/therubyrhino.gemspec
CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |s|
|
|
16
16
|
s.summary = %q{Embed the Rhino JavaScript interpreter into JRuby}
|
17
17
|
|
18
18
|
s.add_development_dependency "rake"
|
19
|
-
s.add_development_dependency "rspec"
|
19
|
+
s.add_development_dependency "rspec", ">= 2.7"
|
20
20
|
s.add_development_dependency "mocha"
|
21
21
|
s.add_development_dependency "jruby-openssl"
|
22
22
|
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: therubyrhino
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 1.73.
|
5
|
+
version: 1.73.2
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Charles Lowell
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2012-
|
13
|
+
date: 2012-04-11 00:00:00 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rake
|
@@ -30,7 +30,7 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: "
|
33
|
+
version: "2.7"
|
34
34
|
requirement: *id002
|
35
35
|
prerelease: false
|
36
36
|
type: :development
|
@@ -67,6 +67,7 @@ extra_rdoc_files:
|
|
67
67
|
files:
|
68
68
|
- .gitignore
|
69
69
|
- .gitmodules
|
70
|
+
- .travis.yml
|
70
71
|
- Gemfile
|
71
72
|
- History.txt
|
72
73
|
- README.rdoc
|
@@ -124,7 +125,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
124
125
|
requirements: []
|
125
126
|
|
126
127
|
rubyforge_project: therubyrhino
|
127
|
-
rubygems_version: 1.8.
|
128
|
+
rubygems_version: 1.8.15
|
128
129
|
signing_key:
|
129
130
|
specification_version: 3
|
130
131
|
summary: Embed the Rhino JavaScript interpreter into JRuby
|