therubyrhino 1.73.0 → 1.73.1

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