therubyrhino 1.73.0 → 1.73.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.
data/lib/rhino/object.rb CHANGED
@@ -1,8 +1,17 @@
1
1
 
2
2
  class Object
3
+
4
+ unless method_defined?(:tap)
5
+ def tap # :nodoc:
6
+ yield self
7
+ self
8
+ end
9
+ end
10
+
3
11
  def eval_js(source, options = {})
4
12
  Rhino::Context.open(options.merge(:with => self)) do |cxt|
5
13
  cxt.eval(source)
6
14
  end
7
15
  end
8
- end
16
+
17
+ end
@@ -0,0 +1,237 @@
1
+
2
+ # The base class for all JavaScript objects.
3
+ class Java::OrgMozillaJavascript::ScriptableObject
4
+
5
+ include_package "org.mozilla.javascript"
6
+
7
+ # get a property from this javascript object, where +k+ is a string or symbol
8
+ # corresponding to the property name e.g.
9
+ #
10
+ # jsobject = Context.open do |cxt|
11
+ # cxt.eval('({foo: 'bar', 'Take me to': 'a funky town'})')
12
+ # end
13
+ # jsobject[:foo] # => 'bar'
14
+ # jsobject['foo'] # => 'bar'
15
+ # jsobject['Take me to'] # => 'a funky town'
16
+ #
17
+ def [](name)
18
+ Rhino.to_ruby ScriptableObject.getProperty(self, name.to_s)
19
+ end
20
+
21
+ # set a property on the javascript object, where +k+ is a string or symbol corresponding
22
+ # to the property name, and +v+ is the value to set. e.g.
23
+ #
24
+ # jsobject = eval_js "new Object()"
25
+ # jsobject['foo'] = 'bar'
26
+ # Context.open(:with => jsobject) do |cxt|
27
+ # cxt.eval('foo') # => 'bar'
28
+ # end
29
+ #
30
+ def []=(key, value)
31
+ scope = self
32
+ ScriptableObject.putProperty(self, key.to_s, Rhino.to_javascript(value, scope))
33
+ end
34
+
35
+ # enumerate the key value pairs contained in this javascript object. e.g.
36
+ #
37
+ # eval_js("{foo: 'bar', baz: 'bang'}").each do |key,value|
38
+ # puts "#{key} -> #{value} "
39
+ # end
40
+ #
41
+ # outputs foo -> bar baz -> bang
42
+ #
43
+ def each
44
+ each_raw { |key, val| yield key, Rhino.to_ruby(val) }
45
+ end
46
+
47
+ def each_key
48
+ each_raw { |key, val| yield key }
49
+ end
50
+
51
+ def each_value
52
+ each_raw { |key, val| yield Rhino.to_ruby(val) }
53
+ end
54
+
55
+ def each_raw
56
+ for id in getAllIds do
57
+ yield id, get(id, self)
58
+ end
59
+ end
60
+
61
+ def keys
62
+ keys = []
63
+ each_key { |key| keys << key }
64
+ keys
65
+ end
66
+
67
+ def values
68
+ vals = []
69
+ each_value { |val| vals << val }
70
+ vals
71
+ end
72
+
73
+ # Converts the native object to a hash. This isn't really a stretch since it's
74
+ # pretty much a hash in the first place.
75
+ def to_h
76
+ hash = {}
77
+ each do |key, val|
78
+ hash[key] = val.is_a?(ScriptableObject) ? val.to_h : val
79
+ end
80
+ hash
81
+ end
82
+
83
+ # Convert this javascript object into a json string.
84
+ def to_json(*args)
85
+ to_h.to_json(*args)
86
+ end
87
+
88
+ # Delegate methods to JS object if possible when called from Ruby.
89
+ def method_missing(name, *args)
90
+ s_name = name.to_s
91
+ if s_name[-1, 1] == '=' && args.size == 1 # writer -> JS put
92
+ self[ s_name[0...-1] ] = args[0]
93
+ else
94
+ if property = self[s_name]
95
+ if property.is_a?(Rhino::JS::Function)
96
+ begin
97
+ context = Rhino::JS::Context.enter
98
+ scope = current_scope(context)
99
+ js_args = Rhino.args_to_javascript(args, self) # scope == self
100
+ Rhino.to_ruby property.__call__(context, scope, self, js_args)
101
+ ensure
102
+ Rhino::JS::Context.exit
103
+ end
104
+ else
105
+ if args.size > 0
106
+ raise ArgumentError, "can't #{name}(#{args.join(', ')}) as '#{name}' is a property"
107
+ end
108
+ Rhino.to_ruby property
109
+ end
110
+ else
111
+ super
112
+ end
113
+ end
114
+ end
115
+
116
+ protected
117
+
118
+ def current_scope(context)
119
+ getParentScope || context.initStandardObjects
120
+ end
121
+
122
+ end
123
+
124
+ class Java::OrgMozillaJavascript::NativeObject
125
+
126
+ include_package "org.mozilla.javascript"
127
+
128
+ def [](name)
129
+ value = Rhino.to_ruby(ScriptableObject.getProperty(self, s_name = name.to_s))
130
+ # handle { '5': 5 }.keys() ... [ 5 ] not [ '5' ] !
131
+ if value.nil? && (i_name = s_name.to_i) != 0
132
+ value = Rhino.to_ruby(ScriptableObject.getProperty(self, i_name))
133
+ end
134
+ value
135
+ end
136
+
137
+ # re-implement unsupported Map#put
138
+ def []=(key, value)
139
+ scope = self
140
+ ScriptableObject.putProperty(self, key.to_s, Rhino.to_javascript(value, scope))
141
+ end
142
+
143
+ end
144
+
145
+ # The base class for all JavaScript function objects.
146
+ class Java::OrgMozillaJavascript::BaseFunction
147
+
148
+ # Object call(Context context, Scriptable scope, Scriptable this, Object[] args)
149
+ alias_method :__call__, :call
150
+
151
+ # make JavaScript functions callable Ruby style e.g. `fn.call('42')`
152
+ #
153
+ # NOTE: That invoking #call does not have the same semantics as
154
+ # JavaScript's Function#call but rather as Ruby's Method#call !
155
+ # Use #apply or #bind before calling to achieve the same effect.
156
+ def call(*args)
157
+ context = Rhino::JS::Context.enter; scope = current_scope(context)
158
+ # calling as a (var) stored function - no this === undefined "use strict"
159
+ # TODO can't pass Undefined.instance as this - it's not a Scriptable !?
160
+ this = Rhino::JS::ScriptRuntime.getGlobal(context)
161
+ __call__(context, scope, this, Rhino.args_to_javascript(args, scope))
162
+ ensure
163
+ Rhino::JS::Context.exit
164
+ end
165
+
166
+ # bind a JavaScript function into the given (this) context
167
+ def bind(this, *args)
168
+ context = Rhino::JS::Context.enter; scope = current_scope(context)
169
+ args = Rhino.args_to_javascript(args, scope)
170
+ Rhino::JS::BoundFunction.new(context, scope, self, Rhino.to_javascript(this), args)
171
+ ensure
172
+ Rhino::JS::Context.exit
173
+ end
174
+
175
+ # use JavaScript functions constructors from Ruby as `fn.new`
176
+ def new(*args)
177
+ context = Rhino::JS::Context.enter; scope = current_scope(context)
178
+ construct(context, scope, Rhino.args_to_javascript(args, scope))
179
+ ensure
180
+ Rhino::JS::Context.exit
181
+ end
182
+
183
+ # apply a function with the given context and (optional) arguments
184
+ # e.g. `fn.apply(obj, 1, 2)`
185
+ #
186
+ # NOTE: That #call from Ruby does not have the same semantics as
187
+ # JavaScript's Function#call but rather as Ruby's Method#call !
188
+ def apply(this, *args)
189
+ context = Rhino::JS::Context.enter; scope = current_scope(context)
190
+ args = Rhino.args_to_javascript(args, scope)
191
+ __call__(context, scope, Rhino.to_javascript(this), args)
192
+ ensure
193
+ Rhino::JS::Context.exit
194
+ end
195
+ alias_method :methodcall, :apply # V8::Function compatibility
196
+
197
+ end
198
+
199
+ class Java::OrgMozillaJavascript::Context
200
+
201
+ def reset_cache!
202
+ @cache = java.util.WeakHashMap.new
203
+ end
204
+
205
+ def enable_cache!
206
+ @cache = nil unless @cache
207
+ end
208
+
209
+ def disable_cache!
210
+ @cache = false
211
+ end
212
+
213
+ # Support for caching JS data per context.
214
+ # e.g. to get === comparison's working ...
215
+ #
216
+ # NOTE: the cache only works correctly for keys following Java identity !
217
+ # (implementing #equals & #hashCode e.g. RubyStrings will work ...)
218
+ #
219
+ def cache(key)
220
+ return yield if @cache == false
221
+ reset_cache! unless @cache
222
+ fetch(key) || store(key, yield)
223
+ end
224
+
225
+ private
226
+
227
+ def fetch(key)
228
+ ref = @cache.get(key)
229
+ ref ? ref.get : nil
230
+ end
231
+
232
+ def store(key, value)
233
+ @cache.put(key, java.lang.ref.WeakReference.new(value))
234
+ value
235
+ end
236
+
237
+ end
data/lib/rhino/ruby.rb ADDED
@@ -0,0 +1,225 @@
1
+
2
+ module Rhino
3
+ module Ruby
4
+
5
+ def self.wrap_error(e)
6
+ JS::WrappedException.new(org.jruby.exceptions.RaiseException.new(e))
7
+ end
8
+
9
+ # shared JS::Scriptable implementation
10
+ module Scriptable
11
+
12
+ # override Object Scriptable#get(String name, Scriptable start);
13
+ # override Object Scriptable#get(int index, Scriptable start);
14
+ def get(name, start)
15
+ access.get(unwrap, name, self) { super }
16
+ end
17
+
18
+ # override boolean Scriptable#has(String name, Scriptable start);
19
+ # override boolean Scriptable#has(int index, Scriptable start);
20
+ def has(name, start)
21
+ access.has(unwrap, name, self) { super }
22
+ end
23
+
24
+ # override void Scriptable#put(String name, Scriptable start, Object value);
25
+ # override void Scriptable#put(int index, Scriptable start, Object value);
26
+ def put(name, start, value)
27
+ access.put(unwrap, name, value) { super }
28
+ end
29
+
30
+ # override Object[] Scriptable#getIds();
31
+ def getIds
32
+ ids = []
33
+ unwrap.public_methods(false).each do |name|
34
+ name = name[0...-1] if name[-1, 1] == '=' # 'foo=' ... 'foo'
35
+ name = name.to_java # java.lang.String
36
+ ids << name unless ids.include?(name)
37
+ end
38
+ super.each { |id| ids.unshift(id) }
39
+ ids.to_java
40
+ end
41
+
42
+ @@access = nil
43
+
44
+ def self.access=(access)
45
+ @@access = access
46
+ end
47
+
48
+ def self.access
49
+ @@access ||= Ruby::DefaultAccess
50
+ end
51
+
52
+ private
53
+
54
+ def access
55
+ Scriptable.access
56
+ end
57
+
58
+ end
59
+
60
+ class Object < JS::ScriptableObject
61
+ include JS::Wrapper
62
+ include Scriptable
63
+
64
+ # wrap an arbitrary (ruby) object
65
+ def self.wrap(object, scope = nil)
66
+ Ruby.cache(object) { new(object, scope) }
67
+ end
68
+
69
+ TYPE = JS::TopLevel::Builtins::Object
70
+
71
+ def initialize(object, scope)
72
+ super()
73
+ @ruby = object
74
+ JS::ScriptRuntime.setBuiltinProtoAndParent(self, scope, TYPE) if scope
75
+ end
76
+
77
+ # abstract Object Wrapper#unwrap();
78
+ def unwrap
79
+ @ruby
80
+ end
81
+
82
+ # abstract String Scriptable#getClassName();
83
+ def getClassName
84
+ @ruby.class.to_s # to_s handles 'nameless' classes as well
85
+ end
86
+
87
+ def toString
88
+ "[ruby #{getClassName}]" # [object User]
89
+ end
90
+
91
+ # protected Object ScriptableObject#equivalentValues(Object value)
92
+ def equivalentValues(other) # JS == operator
93
+ other.is_a?(Object) && unwrap.eql?(other.unwrap)
94
+ end
95
+ alias_method :'==', :equivalentValues
96
+
97
+ end
98
+
99
+ class Function < JS::BaseFunction
100
+ include JS::Wrapper
101
+ include Scriptable
102
+
103
+ # wrap a callable (Method/Proc)
104
+ def self.wrap(callable, scope = nil)
105
+ # NOTE: include JS::Wrapper & Ruby.cache(callable.to_s) guarantees ===
106
+ # in Rhino although if a bind Method gets passed it might get confusing
107
+ Ruby.cache(callable.to_s) { new(callable, scope) }
108
+ end
109
+
110
+ def initialize(callable, scope)
111
+ super()
112
+ @callable = callable
113
+ JS::ScriptRuntime.setFunctionProtoAndParent(self, scope) if scope
114
+ end
115
+
116
+ def unwrap
117
+ @callable
118
+ end
119
+
120
+ # override int BaseFunction#getLength()
121
+ def getLength
122
+ arity = @callable.arity
123
+ arity < 0 ? ( arity + 1 ).abs : arity
124
+ end
125
+
126
+ # #deprecated int BaseFunction#getArity()
127
+ def getArity
128
+ getLength
129
+ end
130
+
131
+ # override String BaseFunction#getFunctionName()
132
+ def getFunctionName
133
+ @callable.is_a?(Proc) ? "" : @callable.name
134
+ end
135
+
136
+ # protected Object ScriptableObject#equivalentValues(Object value)
137
+ def equivalentValues(other) # JS == operator
138
+ return false unless other.is_a?(Function)
139
+ return true if unwrap == other.unwrap
140
+ # Method.== does check if their bind to the same object
141
+ # JS == means they might be bind to different objects :
142
+ unwrap.to_s == other.unwrap.to_s # "#<Method: Foo#bar>"
143
+ end
144
+ alias_method :'==', :equivalentValues
145
+
146
+ # override Object BaseFunction#call(Context context, Scriptable scope,
147
+ # Scriptable thisObj, Object[] args)
148
+ def call(context, scope, this, args)
149
+ args = args.to_a # java.lang.Object[] -> Array
150
+ # JS function style :
151
+ if ( arity = @callable.arity ) != -1 # (a1, *a).arity == -2
152
+ if arity > -1 && args.size > arity # omit 'redundant' arguments
153
+ args = args.slice(0, arity)
154
+ elsif arity > args.size || # fill 'missing' arguments
155
+ ( arity < -1 && (arity = arity.abs - 1) > args.size )
156
+ (arity - args.size).times { args.push(nil) }
157
+ end
158
+ end
159
+ rb_args = Rhino.args_to_ruby(args)
160
+ begin
161
+ callable =
162
+ if @callable.is_a?(UnboundMethod)
163
+ @callable.bind(Rhino.to_ruby(this))
164
+ else
165
+ @callable
166
+ end
167
+ result = callable.call(*rb_args)
168
+ rescue => e
169
+ raise Ruby.wrap_error(e) # thus `try { } catch (e)` works in JS
170
+ end
171
+ Rhino.to_javascript(result, scope)
172
+ end
173
+
174
+ # make sure redefined :call is aliased not the one "inherited" from
175
+ # JS::BaseFunction#call when invoking __call__ (@see rhino_ext.rb)
176
+ alias_method :__call__, :call
177
+
178
+ end
179
+
180
+ class Constructor < Function
181
+ include JS::Wrapper
182
+
183
+ # wrap a ruby class as as constructor function
184
+ def self.wrap(klass, scope = nil)
185
+ # NOTE: caching here seems redundant since we implemented JS::Wrapper
186
+ # and a ruby class objects seems always the same ref under JRuby ...
187
+ Ruby.cache(klass) { new(klass, scope) }
188
+ end
189
+
190
+ def initialize(klass, scope)
191
+ super(klass.method(:new), scope)
192
+ @klass = klass
193
+ end
194
+
195
+ def unwrap
196
+ @klass
197
+ end
198
+
199
+ # override int BaseFunction#getLength()
200
+ def getLength
201
+ arity = @klass.instance_method(:initialize).arity
202
+ arity < 0 ? ( arity + 1 ).abs : arity
203
+ end
204
+
205
+ # override boolean Scriptable#hasInstance(Scriptable instance);
206
+ def hasInstance(instance)
207
+ return false unless instance
208
+ return true if instance.is_a?(@klass)
209
+ instance.is_a?(Object) && instance.unwrap.is_a?(@klass)
210
+ end
211
+
212
+ end
213
+
214
+ def self.cache(key, &block)
215
+ context = JS::Context.getCurrentContext
216
+ context ? context.cache(key, &block) : yield
217
+ end
218
+
219
+ end
220
+
221
+ RubyObject = Ruby::Object
222
+ RubyFunction = Ruby::Function
223
+ RubyConstructor = Ruby::Constructor
224
+
225
+ end