2011-12-12 16:17:42 -05:00
|
|
|
|
|
|
|
module Rhino
|
2011-12-13 06:35:57 -05:00
|
|
|
module Ruby
|
2011-12-12 16:17:42 -05:00
|
|
|
|
2011-12-13 07:43:07 -05:00
|
|
|
# shared JS::Scriptable implementation
|
|
|
|
module Scriptable
|
|
|
|
|
2012-04-21 13:56:05 -04:00
|
|
|
@@access = nil
|
|
|
|
def self.access=(access)
|
|
|
|
@@access = ( access.respond_to?(:get) && access.respond_to?(:put) ) ? access :
|
|
|
|
begin
|
|
|
|
access =
|
|
|
|
if access && ! access.is_a?(Class) # Scriptable.access = :attribute
|
|
|
|
name = access.to_s.chomp('_access')
|
|
|
|
name = name[0, 1].capitalize << name[1..-1]
|
|
|
|
name = :"#{name}Access"
|
|
|
|
if Ruby.const_defined?(name)
|
|
|
|
Ruby.const_get(name) # e.g. Rhino::Ruby::AttributeAccess
|
|
|
|
else
|
|
|
|
const_get(name) # e.g. Rhino::Ruby::Scriptable::FooAccess
|
|
|
|
end
|
|
|
|
else # nil, false, Class
|
|
|
|
access
|
|
|
|
end
|
|
|
|
access.is_a?(Class) ? access.new : access
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.access
|
|
|
|
@@access ||= Ruby::DefaultAccess.new
|
|
|
|
end
|
|
|
|
|
2011-12-13 06:35:57 -05:00
|
|
|
# override Object Scriptable#get(String name, Scriptable start);
|
|
|
|
# override Object Scriptable#get(int index, Scriptable start);
|
|
|
|
def get(name, start)
|
2012-04-21 13:56:05 -04:00
|
|
|
return nil if exclude?(name)
|
2012-01-07 11:01:38 -05:00
|
|
|
access.get(unwrap, name, self) { super }
|
2011-12-12 16:17:42 -05:00
|
|
|
end
|
|
|
|
|
2011-12-13 06:35:57 -05:00
|
|
|
# override boolean Scriptable#has(String name, Scriptable start);
|
|
|
|
# override boolean Scriptable#has(int index, Scriptable start);
|
|
|
|
def has(name, start)
|
2012-04-21 13:56:05 -04:00
|
|
|
return nil if exclude?(name)
|
2012-01-07 11:01:38 -05:00
|
|
|
access.has(unwrap, name, self) { super }
|
2011-12-12 16:17:42 -05:00
|
|
|
end
|
|
|
|
|
2011-12-13 06:35:57 -05:00
|
|
|
# override void Scriptable#put(String name, Scriptable start, Object value);
|
|
|
|
# override void Scriptable#put(int index, Scriptable start, Object value);
|
|
|
|
def put(name, start, value)
|
2012-04-21 13:56:05 -04:00
|
|
|
return nil if exclude?(name)
|
2012-01-06 12:29:22 -05:00
|
|
|
access.put(unwrap, name, value) { super }
|
2011-12-12 16:17:42 -05:00
|
|
|
end
|
2011-12-13 07:43:07 -05:00
|
|
|
|
2011-12-13 06:35:57 -05:00
|
|
|
# override Object[] Scriptable#getIds();
|
|
|
|
def getIds
|
|
|
|
ids = []
|
2011-12-13 07:43:07 -05:00
|
|
|
unwrap.public_methods(false).each do |name|
|
2012-04-21 13:56:05 -04:00
|
|
|
next unless name = convert(name)
|
2012-02-15 06:36:16 -05:00
|
|
|
name = name.to_s.to_java # java.lang.String
|
2011-12-13 06:35:57 -05:00
|
|
|
ids << name unless ids.include?(name)
|
|
|
|
end
|
|
|
|
super.each { |id| ids.unshift(id) }
|
|
|
|
ids.to_java
|
|
|
|
end
|
2011-12-13 07:43:07 -05:00
|
|
|
|
2012-04-21 13:56:05 -04:00
|
|
|
private
|
2012-01-06 12:29:22 -05:00
|
|
|
|
2012-04-21 13:56:05 -04:00
|
|
|
def convert(name)
|
|
|
|
if exclude?(name)
|
|
|
|
nil
|
|
|
|
elsif name[-1, 1] == '='
|
|
|
|
name[0...-1]
|
|
|
|
else
|
|
|
|
name
|
|
|
|
end
|
2012-01-06 12:29:22 -05:00
|
|
|
end
|
|
|
|
|
2012-04-21 13:56:05 -04:00
|
|
|
FETCH = '[]'.freeze
|
|
|
|
STORE = '[]='.freeze
|
2012-01-06 12:29:22 -05:00
|
|
|
|
2012-04-21 13:56:05 -04:00
|
|
|
def exclude?(name)
|
|
|
|
name == FETCH || name == STORE
|
|
|
|
end
|
2012-01-06 12:29:22 -05:00
|
|
|
|
2012-04-21 13:56:05 -04:00
|
|
|
def access
|
|
|
|
Scriptable.access
|
|
|
|
end
|
2012-01-06 12:29:22 -05:00
|
|
|
|
2011-12-13 07:43:07 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
class Object < JS::ScriptableObject
|
|
|
|
include JS::Wrapper
|
|
|
|
include Scriptable
|
|
|
|
|
|
|
|
# wrap an arbitrary (ruby) object
|
|
|
|
def self.wrap(object, scope = nil)
|
2012-01-10 11:24:12 -05:00
|
|
|
Ruby.cache(object) { new(object, scope) }
|
2011-12-13 07:43:07 -05:00
|
|
|
end
|
|
|
|
|
2012-01-07 11:01:38 -05:00
|
|
|
TYPE = JS::TopLevel::Builtins::Object
|
|
|
|
|
2011-12-13 07:43:07 -05:00
|
|
|
def initialize(object, scope)
|
|
|
|
super()
|
|
|
|
@ruby = object
|
2012-01-07 11:01:38 -05:00
|
|
|
JS::ScriptRuntime.setBuiltinProtoAndParent(self, scope, TYPE) if scope
|
2011-12-13 07:43:07 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
# abstract Object Wrapper#unwrap();
|
|
|
|
def unwrap
|
|
|
|
@ruby
|
|
|
|
end
|
2012-04-20 14:24:43 -04:00
|
|
|
|
2011-12-13 07:43:07 -05:00
|
|
|
# abstract String Scriptable#getClassName();
|
|
|
|
def getClassName
|
2012-01-06 10:45:20 -05:00
|
|
|
@ruby.class.to_s # to_s handles 'nameless' classes as well
|
2011-12-13 07:43:07 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def toString
|
|
|
|
"[ruby #{getClassName}]" # [object User]
|
|
|
|
end
|
2011-12-13 06:35:57 -05:00
|
|
|
|
|
|
|
# protected Object ScriptableObject#equivalentValues(Object value)
|
|
|
|
def equivalentValues(other) # JS == operator
|
|
|
|
other.is_a?(Object) && unwrap.eql?(other.unwrap)
|
|
|
|
end
|
2012-01-06 10:45:20 -05:00
|
|
|
alias_method :'==', :equivalentValues
|
2011-12-13 07:43:07 -05:00
|
|
|
|
2011-12-12 16:34:27 -05:00
|
|
|
end
|
2011-12-13 06:35:57 -05:00
|
|
|
|
|
|
|
class Function < JS::BaseFunction
|
2012-01-10 11:24:12 -05:00
|
|
|
include JS::Wrapper
|
2011-12-13 07:43:07 -05:00
|
|
|
include Scriptable
|
|
|
|
|
2011-12-13 06:35:57 -05:00
|
|
|
# wrap a callable (Method/Proc)
|
2012-01-06 10:45:20 -05:00
|
|
|
def self.wrap(callable, scope = nil)
|
2012-01-10 11:24:12 -05:00
|
|
|
# NOTE: include JS::Wrapper & Ruby.cache(callable.to_s) guarantees ===
|
|
|
|
# in Rhino although if a bind Method gets passed it might get confusing
|
|
|
|
Ruby.cache(callable.to_s) { new(callable, scope) }
|
2011-12-13 06:35:57 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def initialize(callable, scope)
|
|
|
|
super()
|
|
|
|
@callable = callable
|
|
|
|
JS::ScriptRuntime.setFunctionProtoAndParent(self, scope) if scope
|
|
|
|
end
|
|
|
|
|
2011-12-13 11:46:04 -05:00
|
|
|
def unwrap
|
|
|
|
@callable
|
|
|
|
end
|
|
|
|
|
2011-12-13 06:35:57 -05:00
|
|
|
# override int BaseFunction#getLength()
|
|
|
|
def getLength
|
2011-12-14 07:22:46 -05:00
|
|
|
arity = @callable.arity
|
2012-01-06 10:45:20 -05:00
|
|
|
arity < 0 ? ( arity + 1 ).abs : arity
|
2011-12-13 06:35:57 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
# #deprecated int BaseFunction#getArity()
|
|
|
|
def getArity
|
|
|
|
getLength
|
|
|
|
end
|
|
|
|
|
|
|
|
# override String BaseFunction#getFunctionName()
|
|
|
|
def getFunctionName
|
|
|
|
@callable.is_a?(Proc) ? "" : @callable.name
|
|
|
|
end
|
|
|
|
|
|
|
|
# protected Object ScriptableObject#equivalentValues(Object value)
|
|
|
|
def equivalentValues(other) # JS == operator
|
|
|
|
return false unless other.is_a?(Function)
|
|
|
|
return true if unwrap == other.unwrap
|
|
|
|
# Method.== does check if their bind to the same object
|
|
|
|
# JS == means they might be bind to different objects :
|
|
|
|
unwrap.to_s == other.unwrap.to_s # "#<Method: Foo#bar>"
|
|
|
|
end
|
2012-01-06 10:45:20 -05:00
|
|
|
alias_method :'==', :equivalentValues
|
2011-12-13 06:35:57 -05:00
|
|
|
|
|
|
|
# override Object BaseFunction#call(Context context, Scriptable scope,
|
|
|
|
# Scriptable thisObj, Object[] args)
|
2012-09-08 09:39:57 -04:00
|
|
|
def call(*args)
|
|
|
|
unless args.first.is_a?(JS::Context)
|
|
|
|
return super # assume a Ruby #call
|
|
|
|
end
|
|
|
|
_, scope, this, args = *args # Java Function#call dispatch
|
2011-12-14 07:22:46 -05:00
|
|
|
args = args.to_a # java.lang.Object[] -> Array
|
|
|
|
# JS function style :
|
2012-01-06 10:45:20 -05:00
|
|
|
if ( arity = @callable.arity ) != -1 # (a1, *a).arity == -2
|
|
|
|
if arity > -1 && args.size > arity # omit 'redundant' arguments
|
2011-12-14 07:22:46 -05:00
|
|
|
args = args.slice(0, arity)
|
2012-01-06 10:45:20 -05:00
|
|
|
elsif arity > args.size || # fill 'missing' arguments
|
|
|
|
( arity < -1 && (arity = arity.abs - 1) > args.size )
|
2011-12-14 07:22:46 -05:00
|
|
|
(arity - args.size).times { args.push(nil) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
rb_args = Rhino.args_to_ruby(args)
|
2011-12-13 06:35:57 -05:00
|
|
|
begin
|
2012-01-10 11:24:12 -05:00
|
|
|
callable =
|
|
|
|
if @callable.is_a?(UnboundMethod)
|
2012-04-17 02:00:03 -04:00
|
|
|
@callable.bind(Rhino.to_ruby(this)) # might end up as TypeError
|
2012-01-10 11:24:12 -05:00
|
|
|
else
|
|
|
|
@callable
|
|
|
|
end
|
|
|
|
result = callable.call(*rb_args)
|
2012-04-23 15:26:22 -04:00
|
|
|
rescue StandardError, ScriptError => e
|
2012-01-10 11:24:12 -05:00
|
|
|
raise Ruby.wrap_error(e) # thus `try { } catch (e)` works in JS
|
2011-12-13 06:35:57 -05:00
|
|
|
end
|
|
|
|
Rhino.to_javascript(result, scope)
|
|
|
|
end
|
|
|
|
|
2012-01-13 06:31:01 -05:00
|
|
|
# make sure redefined :call is aliased not the one "inherited" from
|
|
|
|
# JS::BaseFunction#call when invoking __call__ (@see rhino_ext.rb)
|
|
|
|
alias_method :__call__, :call
|
|
|
|
|
2011-12-12 16:34:27 -05:00
|
|
|
end
|
2011-12-13 06:35:57 -05:00
|
|
|
|
|
|
|
class Constructor < Function
|
|
|
|
include JS::Wrapper
|
|
|
|
|
|
|
|
# wrap a ruby class as as constructor function
|
|
|
|
def self.wrap(klass, scope = nil)
|
2012-01-10 11:24:12 -05:00
|
|
|
# NOTE: caching here seems redundant since we implemented JS::Wrapper
|
|
|
|
# and a ruby class objects seems always the same ref under JRuby ...
|
|
|
|
Ruby.cache(klass) { new(klass, scope) }
|
2011-12-13 06:35:57 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def initialize(klass, scope)
|
|
|
|
super(klass.method(:new), scope)
|
|
|
|
@klass = klass
|
|
|
|
end
|
|
|
|
|
|
|
|
def unwrap
|
|
|
|
@klass
|
|
|
|
end
|
|
|
|
|
2011-12-13 11:46:04 -05:00
|
|
|
# override int BaseFunction#getLength()
|
|
|
|
def getLength
|
2011-12-14 07:22:46 -05:00
|
|
|
arity = @klass.instance_method(:initialize).arity
|
2012-01-06 10:45:20 -05:00
|
|
|
arity < 0 ? ( arity + 1 ).abs : arity
|
2011-12-13 11:46:04 -05:00
|
|
|
end
|
|
|
|
|
2011-12-13 06:35:57 -05:00
|
|
|
# override boolean Scriptable#hasInstance(Scriptable instance);
|
|
|
|
def hasInstance(instance)
|
|
|
|
return false unless instance
|
|
|
|
return true if instance.is_a?(@klass)
|
|
|
|
instance.is_a?(Object) && instance.unwrap.is_a?(@klass)
|
|
|
|
end
|
|
|
|
|
2011-12-12 16:34:27 -05:00
|
|
|
end
|
2011-12-12 16:17:42 -05:00
|
|
|
|
2012-01-10 04:52:53 -05:00
|
|
|
def self.cache(key, &block)
|
|
|
|
context = JS::Context.getCurrentContext
|
|
|
|
context ? context.cache(key, &block) : yield
|
|
|
|
end
|
2011-12-12 16:17:42 -05:00
|
|
|
|
2012-04-17 02:00:03 -04:00
|
|
|
# "hack" for ruby errors so that they act as JS thrown objects
|
|
|
|
class Exception < JS::JavaScriptException
|
|
|
|
|
|
|
|
def initialize(value)
|
|
|
|
super wrap_value(value)
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def wrap_value(value)
|
|
|
|
value.is_a?(Object) ? value : Object.wrap(value)
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.wrap_error(e)
|
|
|
|
Exception.new(e)
|
|
|
|
end
|
|
|
|
|
2011-12-12 16:17:42 -05:00
|
|
|
end
|
|
|
|
|
2012-12-03 11:21:23 -05:00
|
|
|
RubyObject = Ruby::Object # :nodoc
|
|
|
|
RubyFunction = Ruby::Function # :nodoc
|
|
|
|
RubyConstructor = Ruby::Constructor # :nodoc
|
2011-12-12 16:17:42 -05:00
|
|
|
|
|
|
|
end
|