1
0
Fork 0
mirror of https://github.com/rubyjs/therubyracer synced 2023-03-27 23:21:42 -04:00

Merge pull request #374 from cowboyd/4.5/referential-integrity

add back in the memory map + documentation
This commit is contained in:
Charles Lowell 2015-07-31 15:23:31 +03:00
commit 39a6cd6ce3
6 changed files with 143 additions and 67 deletions

View file

@ -6,7 +6,6 @@ require 'v8/context'
# require 'v8/error'
# require 'v8/stack'
require 'v8/conversion/fundamental'
# require 'v8/conversion/indentity'
# require 'v8/conversion/reference'
# require 'v8/conversion/primitive'
# require 'v8/conversion/code'

View file

@ -1,23 +1,85 @@
##
# An extensible conversion mechanism for converting objects to and
# from their V8 representations.
#
# The Ruby Racer has two levels of representation for JavaScript
# objects: the low-level C++ objects which are just thin wrappers
# native counterparts. These are the objects in the `V8::C::*`
# namespace. It also has high-level Ruby objects which can
# either be instances of `V8::Object`, `V8::Date`, or just plain Ruby
# objects that have representations in the JavaScript runtime.
#
# The conversion object held by the context captures this transition
# from one object type to the other. It is implemented as a "middleware"
# stack where at the base is the `Fundamental` conversion which does
# basic conversion and identity mapping.
#
# In order to "extend" or override the conversion mechanism, you can
# extend this object to add behaviors. For example, the following
# extension will add a `__fromRuby__` property to every ruby object
# that is embedded into this context.
#
# module TagRubyObjects
# def to_v8(context, ruby_object)
# super.tap do |v8_object|
# v8_object.Set(V8::C::String::NewFromUtf8("__fromRuby__"), V8::C::Boolean::New(true))
# end
# end
# end
# context.conversion.extend TagRubyObjects
#
# @see V8::Conversion::Fundamental for the basic conversion operation.
class V8::Conversion
include Fundamental
# include Identity
##
# Convert a low-level instance of `V8::C::Value` or one of its
# subclasses into a Ruby object.
#
# The `Fundamental` conversion will call `v8_object.to_ruby`, but
# any number of middlewares can be inserted between then.
#
# @param [V8::C::Value] the object to convert
# @return [Object] the corresponding Ruby value
def to_ruby(v8_object)
super v8_object
end
##
# Convert a Ruby Object into a low-level `V8::C::Value` or one of
# its subclasses. Note here that things like `V8::Object` are
# considered Ruby objects and *not* V8 objects. So, for example, the
# fundamental conversion for `V8::Object` will return a
# `V8::C::Object`
#
# The `Fundamental` conversion will call
# `ruby_object.to_v8(context)` optionally storing the result in an
# identity map in the case where the result is a `V8::C::Object`
#
# @param [V8::Context] the Ruby context in the conversion happens
# @param [Object] Ruby object to convert
# @return [V8::C::Value] the v8 representation
def to_v8(context, ruby_object)
super context, ruby_object
end
end
##
# The folowing represent the default conversions from instances of
# `V8::C::Value` into their Ruby counterparts.
module V8::C
class String
alias_method :to_ruby, :Utf8Value
def to_ruby
self.Utf8Value()
end
end
class Number
alias_method :to_ruby, :Value
def to_ruby
self.Value()
end
end
class Undefined
@ -51,6 +113,9 @@ module V8::C
end
end
##
# The following are the default conversions from Ruby objects into
# low-level C++ objects.
class String
def to_v8(context)
V8::C::String::NewFromUtf8(context.isolate.native, self)
@ -69,25 +134,3 @@ class Symbol
V8::C::Symbol::For(context.isolate.native, V8::C::String::NewFromUtf8(isolate, to_s))
end
end
# for type in [TrueClass, FalseClass, NilClass, Float] do
# type.class_eval do
# include V8::Conversion::Primitive
# end
# end
# for type in [Class, Object, Array, Hash, String, Symbol, Time, Proc, Method, Fixnum] do
# type.class_eval do
# include V8::Conversion.const_get(type.name)
# end
# end
# class UnboundMethod
# include V8::Conversion::Method
# end
# for type in [:Object, :String, :Date] do
# V8::C::const_get(type).class_eval do
# include V8::Conversion::const_get("Native#{type}")
# end
# end

View file

@ -1,11 +1,77 @@
require 'v8/weak'
class V8::Conversion
##
# This is the fundamental conversion for from Ruby objects into
# `V8::C::Value`s and vice-versa. For instances of `V8::C::Object`,
# the result is stored in an identity map, so that subsequent
# conversions return the same object both to and from ruby.
#
# It sits at the top of the conversion "middleware stack"
module Fundamental
##
# Convert a low-level `V8::C::Value` into a Ruby object,
# optionally storing the result in an identity map so that it can
# be re-used for subsequent conversions.
#
# @param [V8::C::Value] low-level C++ object.
# @return [Object] Ruby object counterpart
def to_ruby(v8_object)
# Only objects can be reliably identified. If this is an
# object, then we want to see if there is already a ruby object
# associated with it.
if v8_object.kind_of? V8::C::Object
if rb_object = v8_idmap[v8_object.GetIdentityHash()]
# it was in the id map, so return the existing instance.
rb_object
else
# it was not in the id map, so we run the default conversion
# and store it in the id map
v8_object.to_ruby.tap do |object|
equate object, v8_object
end
end
else
# not an object, just do the default conversion
v8_object.to_ruby
end
end
##
# Convert a Ruby object into a low-level C++ `V8::C::Value`.
#
# First it checks to see if there is an entry in the id map for
# this object. Otherwise, it will run the default conversion.
def to_v8(context, ruby_object)
ruby_object.to_v8 context
rb_idmap[ruby_object.object_id] || ruby_object.to_v8(context)
end
##
# Mark a ruby object and a low-level V8 C++ object as being
# equivalent in this context.
#
# After being equated the two objects are like mirrors of each
# other, where one exists in the Ruby world, and the other exists
# in the V8 world. It's all very through the looking glass.
#
# Whenever `ruby_object` is reflected into the V8 runtime, then
# `v8_object` will be used in its stead, and whenever `v8_object`
# is reflected into the Ruby runtime, then `ruby_object` will be
# used in *its* stead.
#
# @param [Object] the ruby object
# @param [V8::C::Value] the v8 object
def equate(ruby_object, v8_object)
v8_idmap[v8_object.GetIdentityHash()] = ruby_object
rb_idmap[ruby_object.object_id] = v8_object
end
def v8_idmap
@v8_idmap ||= V8::Weak::WeakValueMap.new
end
def rb_idmap
@ruby_idmap ||= V8::Weak::WeakValueMap.new
end
end
end

View file

@ -1,31 +0,0 @@
require 'ref'
class V8::Conversion
module Identity
def to_ruby(v8_object)
if v8_object.class <= V8::C::Object
v8_idmap[v8_object.GetIdentityHash()] || super(v8_object)
else
super(v8_object)
end
end
def to_v8(ruby_object)
return super(ruby_object) if ruby_object.is_a?(String) || ruby_object.is_a?(Primitive)
rb_idmap[ruby_object.object_id] || super(ruby_object)
end
def equate(ruby_object, v8_object)
v8_idmap[v8_object.GetIdentityHash()] = ruby_object
rb_idmap[ruby_object.object_id] = v8_object
end
def v8_idmap
@v8_idmap ||= V8::Weak::WeakValueMap.new
end
def rb_idmap
@ruby_idmap ||= V8::Weak::WeakValueMap.new
end
end
end

View file

@ -4,8 +4,7 @@ class V8::Object
def initialize(native = nil)
@context = V8::Context.current or fail "tried to initialize a #{self.class} without being in an entered V8::Context"
@native = block_given? ? yield : native || V8::C::Object::New()
#@context.link self, @native
@native = native || V8::C::Object::New(@context.isolate.native)
end
def [](key)

View file

@ -100,12 +100,12 @@ describe "V8::Context" do
# end
# end
# xit "always returns the same ruby object for a single javascript object" do
# obj = @cxt.eval('obj = {}')
# obj.should be(@cxt['obj'])
# @cxt.eval('obj').should be(@cxt['obj'])
# @cxt['obj'].should be(@cxt['obj'])
# end
it "always returns the same ruby object for a single javascript object" do
obj = @cxt.eval('obj = {}')
obj.should be(@cxt['obj'])
@cxt.eval('obj').should be(@cxt['obj'])
@cxt['obj'].should be(@cxt['obj'])
end
# xit "converts arrays to javascript" do
# @cxt['a'] = [1,2,4]