mirror of
https://github.com/rubyjs/therubyracer
synced 2023-03-27 23:21:42 -04:00
refactor value converter to be per-context.
This commit is contained in:
parent
c22bbd9177
commit
1be713d53d
8 changed files with 163 additions and 245 deletions
|
@ -66,6 +66,15 @@ namespace {
|
|||
return Qfalse;
|
||||
}
|
||||
}
|
||||
VALUE GetData(VALUE self) {
|
||||
HandleScope scope;
|
||||
return rr_v82rb(unwrap(self)->GetData());
|
||||
}
|
||||
VALUE SetData(VALUE self, VALUE data) {
|
||||
HandleScope scope;
|
||||
unwrap(self)->SetData(rr_rb2v8(data));
|
||||
return Qnil;
|
||||
}
|
||||
}
|
||||
|
||||
void rr_init_cxt() {
|
||||
|
@ -77,5 +86,7 @@ void rr_init_cxt() {
|
|||
rr_define_method(ContextClass, "Enter", Enter, 0);
|
||||
rr_define_method(ContextClass, "Exit", Exit, 0);
|
||||
rr_define_method(ContextClass, "IsEntered", IsEntered, 0);
|
||||
rr_define_method(ContextClass, "GetData", GetData, 0);
|
||||
rr_define_method(ContextClass, "SetData", SetData, 1);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ $:.unshift(File.dirname(__FILE__)) unless
|
|||
module V8
|
||||
VERSION = '0.7.6.pre'
|
||||
require 'v8/v8' #native glue
|
||||
require 'v8/to'
|
||||
require 'v8/portal'
|
||||
require 'v8/context'
|
||||
require 'v8/object'
|
||||
require 'v8/array'
|
||||
|
|
186
lib/v8/access.rb
186
lib/v8/access.rb
|
@ -1,39 +1,23 @@
|
|||
require 'set'
|
||||
module V8
|
||||
|
||||
#TODO each context should get its own access rules instead of sharing them across
|
||||
# contetxts
|
||||
###### --cowboyd 07/07/2010
|
||||
class Access
|
||||
def self.[](cls)
|
||||
@access ||= Access.new
|
||||
@access[cls]
|
||||
end
|
||||
|
||||
def initialize
|
||||
@classes = {}
|
||||
end
|
||||
|
||||
def [](cls)
|
||||
@classes ||= {}
|
||||
if ref = @classes[cls.object_id]
|
||||
if ref.weakref_alive?
|
||||
ref.__getobj__
|
||||
else
|
||||
@classes.delete(cls.object_id)
|
||||
self[cls]
|
||||
end
|
||||
else
|
||||
template(cls).tap do |t|
|
||||
Access.setuptemplate(t.InstanceTemplate())
|
||||
def initialize(portal)
|
||||
@classes = Hash.new do |h, cls|
|
||||
h[cls] = template(cls).tap do |t|
|
||||
portal.setuptemplate(t.InstanceTemplate())
|
||||
if cls.name && cls.name =~ /(::)?(\w+?)$/
|
||||
t.SetClassName(C::String::NewSymbol("rb::" + $2))
|
||||
else
|
||||
t.SetClassName("Ruby")
|
||||
end
|
||||
@classes[cls.object_id] = WeakRef.new(t)
|
||||
end
|
||||
end
|
||||
@impl = RubyAccess.new
|
||||
end
|
||||
|
||||
def [](cls)
|
||||
@classes[cls]
|
||||
end
|
||||
|
||||
def template(cls)
|
||||
|
@ -48,43 +32,8 @@ module V8
|
|||
end
|
||||
end
|
||||
|
||||
def self.rubyobject
|
||||
@rubyobject ||= C::ObjectTemplate::New().tap do |t|
|
||||
setuptemplate(t)
|
||||
end
|
||||
end
|
||||
|
||||
def self.setuptemplate(t)
|
||||
t.SetNamedPropertyHandler(
|
||||
NamedPropertyGetter,
|
||||
NamedPropertySetter,
|
||||
nil,
|
||||
nil,
|
||||
NamedPropertyEnumerator
|
||||
)
|
||||
t.SetIndexedPropertyHandler(
|
||||
IndexedPropertyGetter,
|
||||
IndexedPropertySetter,
|
||||
nil,
|
||||
nil,
|
||||
IndexedPropertyEnumerator
|
||||
)
|
||||
end
|
||||
end
|
||||
module AccessibleMethods
|
||||
def ruby
|
||||
@ruby ||= RubyAccess.new
|
||||
end
|
||||
def access(info, retval = nil, &code)
|
||||
obj = To.rb(info.This())
|
||||
intercepts = true
|
||||
result = Function.rubyprotect do
|
||||
dontintercept = proc do
|
||||
intercepts = false
|
||||
end
|
||||
code.call(obj, dontintercept)
|
||||
end
|
||||
intercepts ? (retval || result) : C::Empty
|
||||
def method_missing(name, *args)
|
||||
@impl.send(name, *args)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -166,6 +115,8 @@ module V8
|
|||
obj.respond_to?(:length) ? (0..obj.length).to_a : yield
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def accessible_methods(obj)
|
||||
obj.public_methods(false).map {|m| m.to_s}.to_set.tap do |methods|
|
||||
ancestors = obj.class.ancestors.dup
|
||||
|
@ -200,115 +151,4 @@ module V8
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
class NamedPropertyGetter
|
||||
extend AccessibleMethods
|
||||
def self.call(property, info)
|
||||
access(info) do |obj, dontintercept|
|
||||
ruby.get(obj, To.rb(property), &dontintercept)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class NamedPropertySetter
|
||||
extend AccessibleMethods
|
||||
def self.call(property, value, info)
|
||||
access(info, value) do |obj, dontintercept|
|
||||
ruby.set(obj, To.rb(property), To.rb(value), &dontintercept)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class NamedPropertyQuery
|
||||
extend AccessibleMethods
|
||||
class Attrs
|
||||
def initialize
|
||||
@flags = 0
|
||||
end
|
||||
|
||||
def read_only
|
||||
tap do
|
||||
@flags |= V8::C::ReadOnly
|
||||
end
|
||||
end
|
||||
|
||||
def dont_enum
|
||||
tap do
|
||||
@flags |= V8::C::DontEnum
|
||||
end
|
||||
end
|
||||
|
||||
def dont_delete
|
||||
tap do
|
||||
@flags |= V8::C::DontDelete
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.call(property, info)
|
||||
attrs = Attrs.new
|
||||
intercepts = true
|
||||
access_query(To.rb(info.This()), To.rb(property), attrs) do
|
||||
intercepts = false
|
||||
end
|
||||
intercepts ? C::Integer::New(attrs.flags) : C::Empty
|
||||
end
|
||||
|
||||
def self.access_attributes(obj, name, attrs)
|
||||
unless obj.respond_to?(name)
|
||||
attrs.dont_enum
|
||||
attrs.dont_delete
|
||||
end
|
||||
unless obj.respond_to?("name=")
|
||||
attrs.read_only
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class NamedPropertyEnumerator
|
||||
extend AccessibleMethods
|
||||
def self.call(info)
|
||||
obj = To.rb(info.This())
|
||||
methods = ruby.accessible_methods(obj)
|
||||
methods.reject! {|m| m.to_s =~ /=$/}
|
||||
names = V8::C::Array::New(methods.length)
|
||||
methods.each_with_index do |name, i|
|
||||
names.Set(i, C::String::New(name))
|
||||
end
|
||||
return names
|
||||
end
|
||||
end
|
||||
|
||||
class IndexedPropertyGetter
|
||||
extend AccessibleMethods
|
||||
def self.call(index, info)
|
||||
access(info) do |obj, dontintercept|
|
||||
ruby.iget(obj, index, &dontintercept)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class IndexedPropertySetter
|
||||
extend AccessibleMethods
|
||||
def self.call(index, value, info)
|
||||
access(info, value) do |obj, dontintercept|
|
||||
ruby.iset(obj, index, To.rb(value), &dontintercept)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class IndexedPropertyEnumerator
|
||||
def self.call(info)
|
||||
obj = To.rb(info.This())
|
||||
if obj.respond_to?(:length)
|
||||
C::Array::New(obj.length).tap do |indices|
|
||||
for index in 0..obj.length - 1
|
||||
indices.Set(index,index)
|
||||
end
|
||||
end
|
||||
else
|
||||
C::Array::New()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,9 +3,9 @@ module V8
|
|||
class Array < V8::Object
|
||||
|
||||
def each
|
||||
@context.enter do
|
||||
@portal.open do |to|
|
||||
for i in 0..(@native.Length() - 1)
|
||||
yield To.rb(@native.Get(i))
|
||||
yield to.rb(@native.Get(i))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,12 +2,13 @@ require 'stringio'
|
|||
|
||||
module V8
|
||||
class Context
|
||||
attr_reader :native, :scope
|
||||
attr_reader :native, :scope, :access
|
||||
def initialize(opts = {})
|
||||
@to = Convert.new(self)
|
||||
@to = Portal.new(self)
|
||||
@access = Access.new(@to)
|
||||
@native = opts[:with] ? C::Context::New(Access.rubyobject) : C::Context::New()
|
||||
@native.enter do
|
||||
@scope = To.rb(@native.Global())
|
||||
@scope = @to.rb(@native.Global())
|
||||
@native.Global().SetHiddenValue(C::String::New("TheRubyRacer::RubyObject"), C::External::New(opts[:with])) if opts[:with]
|
||||
end
|
||||
yield(self) if block_given?
|
||||
|
@ -21,7 +22,7 @@ module V8
|
|||
value = nil
|
||||
C::TryCatch.try do |try|
|
||||
@native.enter do
|
||||
script = C::Script::Compile(To.v8(javascript.to_s), To.v8(filename.to_s))
|
||||
script = C::Script::Compile(@to.v8(javascript.to_s), @to.v8(filename.to_s))
|
||||
if try.HasCaught()
|
||||
err = JSError.new(try)
|
||||
else
|
||||
|
@ -29,7 +30,7 @@ module V8
|
|||
if try.HasCaught()
|
||||
err = JSError.new(try)
|
||||
else
|
||||
value = To.rb(result)
|
||||
value = @to.rb(result)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -25,35 +25,35 @@ module V8
|
|||
end
|
||||
end
|
||||
|
||||
def self.rubyprotect(&blk)
|
||||
To.v8(rubyprotect2(&blk))
|
||||
end
|
||||
|
||||
def self.rubyprotect2
|
||||
begin
|
||||
yield
|
||||
rescue Exception => e
|
||||
case e
|
||||
when SystemExit, NoMemoryError
|
||||
raise e
|
||||
else
|
||||
error = V8::C::Exception::Error(V8::C::String::New(e.message))
|
||||
error.SetHiddenValue("TheRubyRacer::Cause", C::External::New(e))
|
||||
V8::C::ThrowException(error)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.rubycall(rubycode, *args, &block)
|
||||
rubyprotect do
|
||||
rubycode.call(*args, &block)
|
||||
end
|
||||
end
|
||||
|
||||
def self.rubysend(obj, message, *args, &block)
|
||||
rubyprotect do
|
||||
obj.send(message, *args, &block)
|
||||
end
|
||||
end
|
||||
# def self.rubyprotect(&blk)
|
||||
# To.v8(rubyprotect2(&blk))
|
||||
# end
|
||||
#
|
||||
# def self.rubyprotect2
|
||||
# begin
|
||||
# yield
|
||||
# rescue Exception => e
|
||||
# case e
|
||||
# when SystemExit, NoMemoryError
|
||||
# raise e
|
||||
# else
|
||||
# error = V8::C::Exception::Error(V8::C::String::New(e.message))
|
||||
# error.SetHiddenValue("TheRubyRacer::Cause", C::External::New(e))
|
||||
# V8::C::ThrowException(error)
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# def self.rubycall(rubycode, *args, &block)
|
||||
# rubyprotect do
|
||||
# rubycode.call(*args, &block)
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# def self.rubysend(obj, message, *args, &block)
|
||||
# rubyprotect do
|
||||
# obj.send(message, *args, &block)
|
||||
# end
|
||||
# end
|
||||
end
|
||||
end
|
|
@ -3,35 +3,33 @@ module V8
|
|||
class Object
|
||||
include Enumerable
|
||||
|
||||
def initialize(native, context = nil)
|
||||
@native = native
|
||||
@context = context || C::Context::GetEntered()
|
||||
raise ScriptError, "V8::Object.new called without an open V8 context" unless @context
|
||||
def initialize(native, portal)
|
||||
@native, @portal = native, portal
|
||||
end
|
||||
|
||||
def [](key)
|
||||
@context.enter do
|
||||
To.rb(@native.Get(To.v8(key)))
|
||||
@portal.open do |to|
|
||||
to.rb(@native.Get(to.v8(key)))
|
||||
end
|
||||
end
|
||||
|
||||
def []=(key, value)
|
||||
value.tap do
|
||||
@context.enter do
|
||||
@native.Set(To.v8(key), To.v8(value))
|
||||
@portal.open do |to|
|
||||
@native.Set(to.v8(key), to.v8(value))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def to_s
|
||||
@context.enter do
|
||||
To.rb(@native.ToString())
|
||||
to.rb(@native.ToString())
|
||||
end
|
||||
end
|
||||
|
||||
def each
|
||||
@context.enter do
|
||||
for prop in To.rb(@native.GetPropertyNames())
|
||||
for prop in to.rb(@native.GetPropertyNames())
|
||||
yield prop, self[prop]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,18 +1,26 @@
|
|||
|
||||
module V8
|
||||
class Convert
|
||||
class Portal
|
||||
|
||||
def initialize(context)
|
||||
@named_property_getter = NamedPropertyGetter.new(self, context.access)
|
||||
@named_property_setter = NamedPropertySetter.new(self, context.access)
|
||||
@context = context
|
||||
@named_property_getter = Interceptor(NamedPropertyGetter)
|
||||
@named_property_setter = Interceptor(NamedPropertySetter)
|
||||
@named_property_query = nil
|
||||
@named_property_deleter = nil
|
||||
@named_property_enumerator = NamedPropertyEnumerator.new(self, context.access)
|
||||
@named_property_enumerator = Interceptor(NamedPropertyEnumerator)
|
||||
|
||||
@indexed_property_getter = IndexedPropertyGetter.new(self, context.access)
|
||||
@indexed_property_setter = IndexedPropertySetter.new(self, context.access)
|
||||
@indexed_property_getter = Interceptor(IndexedPropertyGetter)
|
||||
@indexed_property_setter = Interceptor(IndexedPropertySetter)
|
||||
@indexed_property_query = nil
|
||||
@indexed_property_deleter = nil
|
||||
@indexed_property_enumerator = IndexedPropertyEnumerator.new(self, context.access)
|
||||
@indexed_property_enumerator = Interceptor(IndexedPropertyEnumerator)
|
||||
end
|
||||
|
||||
def open
|
||||
@context.native.enter do
|
||||
yield(self)
|
||||
end if block_given?
|
||||
end
|
||||
|
||||
def rb(value)
|
||||
|
@ -42,7 +50,7 @@ module V8
|
|||
for i in 0..arguments.Length() - 1
|
||||
rbargs << rb(arguments[i])
|
||||
end
|
||||
V8::Function.rubycall(value, *rbargs)
|
||||
rubycall(value, *rbargs)
|
||||
end
|
||||
return template.GetFunction()
|
||||
when ::Array
|
||||
|
@ -68,30 +76,82 @@ module V8
|
|||
else
|
||||
args = C::Array::New(1)
|
||||
args.Set(0, C::External::New(value))
|
||||
obj = Access[value.class].GetFunction().NewInstance(args)
|
||||
obj = @context.access[value.class].GetFunction().NewInstance(args)
|
||||
return obj
|
||||
end
|
||||
end
|
||||
|
||||
################################
|
||||
def rubyprotect(&blk)
|
||||
self.v8(rubyprotect2(&blk))
|
||||
end
|
||||
|
||||
def rubyprotect2
|
||||
begin
|
||||
yield
|
||||
rescue Exception => e
|
||||
case e
|
||||
when SystemExit, NoMemoryError
|
||||
raise e
|
||||
else
|
||||
error = V8::C::Exception::Error(V8::C::String::New(e.message))
|
||||
error.SetHiddenValue("TheRubyRacer::Cause", C::External::New(e))
|
||||
V8::C::ThrowException(error)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def rubycall(rubycode, *args, &block)
|
||||
rubyprotect do
|
||||
rubycode.call(*args, &block)
|
||||
end
|
||||
end
|
||||
|
||||
def rubysend(obj, message, *args, &block)
|
||||
rubyprotect do
|
||||
obj.send(message, *args, &block)
|
||||
end
|
||||
end
|
||||
|
||||
def setuptemplate(t)
|
||||
t.SetNamedPropertyHandler(
|
||||
@named_property_getter,
|
||||
@named_property_setter,
|
||||
nil,
|
||||
nil,
|
||||
@named_property_enumerator
|
||||
)
|
||||
t.SetIndexedPropertyHandler(
|
||||
@indexed_property_getter,
|
||||
@indexed_property_setter,
|
||||
nil,
|
||||
nil,
|
||||
@indexed_property_enumerator
|
||||
)
|
||||
end
|
||||
################################
|
||||
|
||||
private
|
||||
|
||||
def peer(value)
|
||||
external = value.GetHiddenValue(C::String::NewSymbol("TheRubyRacer::RubyObject"))
|
||||
if external && !external.IsEmpty()
|
||||
external.Value()
|
||||
else
|
||||
yield.new(value)
|
||||
yield.new(value, self)
|
||||
end
|
||||
end
|
||||
|
||||
class Interceptor
|
||||
def initialize(convert, access)
|
||||
@to = convert
|
||||
@access = access
|
||||
def initialize(portal, context)
|
||||
@to = portal
|
||||
@context = context
|
||||
end
|
||||
|
||||
def intercept(info, retval = nil, &code)
|
||||
obj = @to.rb(info.This())
|
||||
intercepts = true
|
||||
result = Function.rubyprotect do
|
||||
result = @to.rubyprotect do
|
||||
dontintercept = proc do
|
||||
intercepts = false
|
||||
end
|
||||
|
@ -99,6 +159,14 @@ module V8
|
|||
end
|
||||
intercepts ? (retval || result) : C::Empty
|
||||
end
|
||||
|
||||
def access
|
||||
@context.access
|
||||
end
|
||||
end
|
||||
|
||||
def Interceptor(cls)
|
||||
cls.new self, @context
|
||||
end
|
||||
|
||||
class PropertyAttributes
|
||||
|
@ -129,7 +197,7 @@ module V8
|
|||
class NamedPropertyGetter < Interceptor
|
||||
def call(property, info)
|
||||
intercept(info) do |obj, dontintercept|
|
||||
@access.get(obj, @to.rb(property), &dontintercept)
|
||||
access.get(obj, @to.rb(property), &dontintercept)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -137,7 +205,7 @@ module V8
|
|||
class NamedPropertySetter < Interceptor
|
||||
def call(property, value, info)
|
||||
intercept(info, value) do |obj, dontintercept|
|
||||
@access.set(obj, @to.rb(property), @to.rb(value), &dontintercept)
|
||||
access.set(obj, @to.rb(property), @to.rb(value), &dontintercept)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -146,7 +214,7 @@ module V8
|
|||
def call(property, info)
|
||||
attributes = PropertyAttributes.new
|
||||
result = intercept(info) do |obj, dontintercept|
|
||||
@access.query(obj, @to.rb(property), attributes, &dontintercept)
|
||||
access.query(obj, @to.rb(property), attributes, &dontintercept)
|
||||
end
|
||||
return result == C::Empty ? result : C::Integer::New(attributes.flags)
|
||||
end
|
||||
|
@ -155,7 +223,7 @@ module V8
|
|||
class NamedPropertyEnumerator < Interceptor
|
||||
def call(info)
|
||||
intercept(info) do |obj, dontintercept|
|
||||
@access.names(obj, &dontintercept)
|
||||
access.names(obj, &dontintercept)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -163,7 +231,7 @@ module V8
|
|||
class NamedPropertyDeleter < Interceptor
|
||||
def call(property, info)
|
||||
intercept(info) do |obj, dontintercept|
|
||||
@access.delete(obj, property, &dontintercept)
|
||||
access.delete(obj, property, &dontintercept)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -171,7 +239,7 @@ module V8
|
|||
class IndexedPropertyGetter < Interceptor
|
||||
def call(index, info)
|
||||
intercept(info) do |obj, dontintercept|
|
||||
@access.iget(obj, index, &dontintercept)
|
||||
access.iget(obj, index, &dontintercept)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -179,7 +247,7 @@ module V8
|
|||
class IndexedPropertySetter < Interceptor
|
||||
def call(index, value, info)
|
||||
intercept(info, value) do |obj, dontintercept|
|
||||
@access.iset(obj, index, @to.rb(value), &dontintercept)
|
||||
access.iset(obj, index, @to.rb(value), &dontintercept)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -188,7 +256,7 @@ module V8
|
|||
def call(property, info)
|
||||
attributes = PropertyAttributes.new
|
||||
result = intercept(info) do |obj, dontintercept|
|
||||
@access.indices(obj, &dontintercept)
|
||||
access.indices(obj, &dontintercept)
|
||||
end
|
||||
result == C::Empty ? C::Empty : C::Integer::New(attributes.flags)
|
||||
end
|
||||
|
@ -197,7 +265,7 @@ module V8
|
|||
class IndexedPropertyDeleter < Interceptor
|
||||
def call(index, info)
|
||||
intercept(info) do |obj, dontintercept|
|
||||
@access.idelete(obj, index, &dontintercept)
|
||||
access.idelete(obj, index, &dontintercept)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -205,7 +273,7 @@ module V8
|
|||
class IndexedPropertyEnumerator < Interceptor
|
||||
def call(info)
|
||||
intercept(info) do |obj, dontintercept|
|
||||
@access.indices(obj, &dontintercept)
|
||||
access.indices(obj, &dontintercept)
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Add table
Reference in a new issue