mirror of
https://github.com/rubyjs/therubyracer
synced 2023-03-27 23:21:42 -04:00
move ruby function invocation and property lookup into ruby
This commit is contained in:
parent
6a5d27ee1d
commit
14edf2b0ff
13 changed files with 261 additions and 65 deletions
|
@ -119,11 +119,3 @@ VALUE rr_v82rb(const AccessorInfo& info) {
|
|||
VALUE rr_v82rb(const Arguments& arguments) {
|
||||
return Data_Wrap_Struct(ArgumentsClass, gc_wrap_mark, gc_wrap_free, new WrapArguments(arguments));
|
||||
}
|
||||
|
||||
Handle<Value> RubyInvocationCallback(const Arguments& args) {
|
||||
HandleScope handles;
|
||||
VALUE code = (VALUE)External::Unwrap(args.Data());
|
||||
VALUE rb_args = rr_v82rb(args);
|
||||
VALUE result = rb_funcall(code, rb_intern("call"), 1, rb_args);
|
||||
return rr_rb2v8(result);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
#ifndef _RR_V8_CALLBACKS_
|
||||
#define _RR_V8_CALLBACKS_
|
||||
#include "v8.h"
|
||||
#include "rr.h"
|
||||
void rr_init_v8_callbacks();
|
||||
// VALUE rr_v82rb(v8::AccessorInfo& info);
|
||||
// VALUE rr_v82rb(v8::Arguments& arguments);
|
||||
VALUE rr_v82rb(const v8::AccessorInfo& info);
|
||||
VALUE rr_v82rb(const v8::Arguments& arguments);
|
||||
|
||||
v8::Handle<v8::Value> RubyInvocationCallback(const v8::Arguments& args);
|
||||
#endif
|
|
@ -18,6 +18,7 @@ namespace {
|
|||
return V8_Ref_Get<Context>(value);
|
||||
}
|
||||
|
||||
//TODO: make this scriptable and less static
|
||||
VALUE New(int argc, VALUE *argv, VALUE self) {
|
||||
HandleScope handles;
|
||||
VALUE scope;
|
||||
|
|
|
@ -29,7 +29,7 @@ namespace {
|
|||
VALUE New(VALUE clazz) {
|
||||
HandleScope handles;
|
||||
if (!Context::InContext()) {
|
||||
rb_raise(rb_eScriptError, "tried to allocate an Object, but no context was open");
|
||||
rb_raise(rb_eScriptError, "Object::New() called without an entered Context");
|
||||
return Qnil;
|
||||
}
|
||||
return V8_Ref_Create(clazz, Object::New());
|
||||
|
@ -39,7 +39,7 @@ namespace {
|
|||
HandleScope handles;
|
||||
Local<Object> obj = unwrap(self);
|
||||
if (rb_obj_is_kind_of(key, rb_cNumeric)) {
|
||||
return rr_v82rb(obj->Set(NUM2UINT(key), RB2V8(value)));
|
||||
return rr_v82rb(obj->Set(NUM2UINT(key), rr_rb2v8(value)));
|
||||
} else {
|
||||
return rr_v82rb(obj->Set(rr_rb2v8(key), rr_rb2v8(value)));
|
||||
}
|
||||
|
@ -50,7 +50,18 @@ namespace {
|
|||
Local<Object> object = unwrap(self);
|
||||
Local<Value> names = object->GetPropertyNames();
|
||||
return rr_v82rb(names);
|
||||
}
|
||||
}
|
||||
VALUE SetHiddenValue(VALUE self, VALUE key, VALUE value) {
|
||||
HandleScope scope;
|
||||
if (Context::InContext()) {
|
||||
unwrap(self)->SetHiddenValue(rr_rb2v8(key)->ToString(), rr_rb2v8(value));
|
||||
} else {
|
||||
rb_raise(rb_eScriptError, "Object::SetHiddenValue() called without an entered Context");
|
||||
}
|
||||
//TODO: need to store a reference here? what's the best way
|
||||
// rr_v8_ref_setref(self, "RubyPeer", )
|
||||
return Qnil;
|
||||
}
|
||||
}
|
||||
|
||||
void rr_init_obj() {
|
||||
|
@ -60,6 +71,7 @@ void rr_init_obj() {
|
|||
rr_define_method(rr_cV8_C_Object, "Get", Get, 1);
|
||||
rr_define_method(rr_cV8_C_Object, "Set", Set, 2);
|
||||
rr_define_method(rr_cV8_C_Object, "GetPropertyNames", GetPropertyNames, 0);
|
||||
rr_define_method(rr_cV8_C_Object, "SetHiddenValue", SetHiddenValue, 2);
|
||||
}
|
||||
|
||||
VALUE rr_reflect_v8_object(Handle<Value> value) {
|
||||
|
|
|
@ -31,6 +31,10 @@ VALUE V8_Ref_Create(VALUE ruby_class, v8::Handle<void> handle, VALUE ref) {
|
|||
return Data_Wrap_Struct(ruby_class, gc_mark, gc_free, new v8_ref(handle, ref));
|
||||
}
|
||||
|
||||
VALUE rr_v8_ref_create(VALUE rbclass, v8::Handle<void> handle) {
|
||||
return Data_Wrap_Struct(rbclass, gc_mark, gc_free, new v8_ref(handle));
|
||||
}
|
||||
|
||||
void rr_v8_ref_setref(VALUE handle, const char *name, VALUE newref) {
|
||||
v8_ref *ref = 0;
|
||||
Data_Get_Struct(handle, struct v8_ref, ref);
|
||||
|
|
|
@ -18,7 +18,7 @@ struct v8_ref {
|
|||
|
||||
void rr_v8_ref_setref(VALUE handle, const char *name, VALUE ref);
|
||||
VALUE V8_Ref_Create(VALUE ruby_class, v8::Handle<void> handle, VALUE ref = 0);
|
||||
VALUE rr_wrap(VALUE ruby_class, void *value);
|
||||
VALUE rr_v8_ref_create(VALUE rbclass, v8::Handle<void> handle);
|
||||
|
||||
template <class T> v8::Local<T> V8_Ref_Get(VALUE object) {
|
||||
v8_ref* ref = 0;
|
||||
|
|
|
@ -10,6 +10,15 @@
|
|||
using namespace v8;
|
||||
|
||||
namespace {
|
||||
|
||||
VALUE rb_hash_lookup(VALUE hash, const char *key) {
|
||||
return ::rb_hash_lookup(hash, rb_str_new2(key));
|
||||
}
|
||||
|
||||
VALUE rb_hash_aset(VALUE hash, const char *key, VALUE value) {
|
||||
return ::rb_hash_aset(hash, rb_str_new2(key), value);
|
||||
}
|
||||
|
||||
Local<Template> tmpl(VALUE self) {
|
||||
return V8_Ref_Get<Template>(self);
|
||||
}
|
||||
|
@ -30,9 +39,125 @@ namespace {
|
|||
|
||||
namespace Obj {
|
||||
|
||||
/**
|
||||
* NamedProperty[Getter|Setter] are used as interceptors on object.
|
||||
* See ObjectTemplate::SetNamedPropertyHandler.
|
||||
*/
|
||||
Handle<Value> RubyNamedPropertyGetter(Local<String> property, const AccessorInfo& info) {
|
||||
VALUE code = (VALUE)External::Unwrap(info.Data());
|
||||
VALUE getter = rb_hash_lookup(code, "getter");
|
||||
return rr_rb2v8(rb_funcall(getter, rb_intern("call"), 2, rr_v82rb(property), rr_v82rb(info)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value if the setter intercepts the request.
|
||||
* Otherwise, returns an empty handle.
|
||||
*/
|
||||
Handle<Value> RubyNamedPropertySetter(Local<String> property, Local<Value> value, const AccessorInfo& info) {
|
||||
HandleScope handles;
|
||||
VALUE code = (VALUE)External::Unwrap(info.Data());
|
||||
VALUE setter = rb_hash_lookup(code, "setter");
|
||||
VALUE result = rb_funcall(setter, rb_intern("call"), 3, rr_v82rb(property), rr_v82rb(value), rr_v82rb(info));
|
||||
return rr_rb2v8(result);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a non-empty handle if the interceptor intercepts the request.
|
||||
* The result is true if the property exists and false otherwise.
|
||||
*/
|
||||
Handle<Boolean> RubyNamedPropertyQuery(Local<String> property, const AccessorInfo& info) {
|
||||
HandleScope handles;
|
||||
VALUE code = (VALUE)External::Unwrap(info.Data());
|
||||
VALUE query = rb_hash_lookup(code, "query");
|
||||
VALUE result = rb_funcall(query, rb_intern("call"), 2, rr_v82rb(property), rr_v82rb(info));
|
||||
Handle<Value> intercepts = rr_rb2v8(result);
|
||||
return intercepts.IsEmpty() ? Handle<Boolean>() : intercepts->ToBoolean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a non-empty handle if the deleter intercepts the request.
|
||||
* The return value is true if the property could be deleted and false
|
||||
* otherwise.
|
||||
*/
|
||||
Handle<Boolean> RubyNamedPropertyDeleter(Local<String> property, const AccessorInfo& info) {
|
||||
HandleScope handles;
|
||||
VALUE code = (VALUE)External::Unwrap(info.Data());
|
||||
VALUE deleter = rb_hash_lookup(code, "deleter");
|
||||
VALUE result = rb_funcall(deleter, rb_intern("call"), 2, rr_v82rb(property), rr_v82rb(info));
|
||||
Handle<Value> intercepts = rr_rb2v8(result);
|
||||
return intercepts.IsEmpty() ? Handle<Boolean>() : intercepts->ToBoolean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array containing the names of the properties the named
|
||||
* property getter intercepts.
|
||||
*/
|
||||
Handle<Array> RubyNamedPropertyEnumerator(const AccessorInfo& info) {
|
||||
HandleScope handles;
|
||||
VALUE code = (VALUE)External::Unwrap(info.Data());
|
||||
VALUE enumerator = rb_hash_lookup(code, "enumerator");
|
||||
VALUE result = rb_funcall(enumerator, rb_intern("call"), 1, rr_v82rb(info));
|
||||
Handle<Value> v(rr_rb2v8(result));
|
||||
if (v.IsEmpty()) {
|
||||
return Array::New();
|
||||
} else if (!v->IsArray()) {
|
||||
Local<Array> a = Array::New();
|
||||
a->Set(Integer::New(0), v->ToString());
|
||||
return a;
|
||||
} else {
|
||||
return (Handle<Array>)Array::Cast(*v);
|
||||
}
|
||||
}
|
||||
|
||||
VALUE New(VALUE clazz) {
|
||||
HandleScope handles;
|
||||
return V8_Ref_Create(clazz, ObjectTemplate::New());
|
||||
}
|
||||
VALUE NewInstance(VALUE self) {
|
||||
HandleScope scope;
|
||||
if (!Context::InContext()) {
|
||||
rb_raise(rb_eScriptError, "ObjectTemplate::NewInstance() called without an entered Context");
|
||||
return Qnil;
|
||||
}
|
||||
Local<Object> object(obj(self)->NewInstance());
|
||||
return rr_v82rb(object);
|
||||
}
|
||||
VALUE SetNamedPropertyHandler(VALUE self, VALUE getter, VALUE setter, VALUE query, VALUE deleter, VALUE enumerator) {
|
||||
HandleScope handles;
|
||||
if (!RTEST(getter)) {
|
||||
rb_raise(rb_eArgError, "you must supply at least a getter to V8::C::ObjectTemplate#SetNamedPropertyHandler()");
|
||||
return Qnil;
|
||||
}
|
||||
VALUE data = rb_hash_new();
|
||||
rb_hash_aset(data, "getter", getter);
|
||||
rb_hash_aset(data, "setter", setter);
|
||||
rb_hash_aset(data, "query", query);
|
||||
rb_hash_aset(data, "deleter", deleter);
|
||||
rb_hash_aset(data, "enumerator", enumerator);
|
||||
rr_v8_ref_setref(self, "data", data);
|
||||
obj(self)->SetNamedPropertyHandler(
|
||||
RubyNamedPropertyGetter,
|
||||
RTEST(setter) ? RubyNamedPropertySetter : 0,
|
||||
RTEST(query) ? RubyNamedPropertyQuery : 0,
|
||||
RTEST(deleter) ? RubyNamedPropertyDeleter : 0,
|
||||
RTEST(enumerator) ? RubyNamedPropertyEnumerator : 0,
|
||||
External::Wrap((void *)data)
|
||||
);
|
||||
return Qnil;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Func {
|
||||
|
||||
Handle<Value> RubyInvocationCallback(const Arguments& args) {
|
||||
HandleScope handles;
|
||||
VALUE code = (VALUE)External::Unwrap(args.Data());
|
||||
VALUE rb_args = rr_v82rb(args);
|
||||
VALUE result = rb_funcall(code, rb_intern("call"), 1, rb_args);
|
||||
return rr_rb2v8(result);
|
||||
}
|
||||
|
||||
VALUE New(VALUE function_template) {
|
||||
HandleScope handles;
|
||||
rb_need_block();
|
||||
|
@ -44,13 +169,11 @@ namespace {
|
|||
return V8_Ref_Create(function_template,templ,code);
|
||||
}
|
||||
VALUE GetFunction(VALUE self) {
|
||||
HandleScope handles;
|
||||
if (!Context::InContext()) {
|
||||
rb_raise(rb_eScriptError, "calls to FunctionTemplate::GetFunction() require a Context to be entered");
|
||||
return Qnil;
|
||||
}
|
||||
HandleScope handles;
|
||||
Local<FunctionTemplate> templ = func(self);
|
||||
Local<Function> fun = templ->GetFunction();
|
||||
return rr_v82rb(func(self)->GetFunction());
|
||||
}
|
||||
}
|
||||
|
@ -59,18 +182,22 @@ namespace {
|
|||
void rr_init_template() {
|
||||
VALUE Template = rr_define_class("Template");
|
||||
rr_define_method(Template, "Set", Set, 2);
|
||||
|
||||
|
||||
VALUE ObjectTemplate = rr_define_class("ObjectTemplate", Template);
|
||||
rb_define_singleton_method(ObjectTemplate, "new", (VALUE(*)(...))v8_ObjectTemplate_New, 0);
|
||||
|
||||
rr_define_singleton_method(ObjectTemplate, "New", Obj::New, 0);
|
||||
rr_define_method(ObjectTemplate, "NewInstance", Obj::NewInstance, 0);
|
||||
rr_define_method(ObjectTemplate, "SetNamedPropertyHandler", Obj::SetNamedPropertyHandler, 5);
|
||||
|
||||
VALUE FunctionTemplate = rr_define_class("FunctionTemplate", Template);
|
||||
rr_define_singleton_method(FunctionTemplate, "New", Func::New, 0);
|
||||
// rb_define_singleton_method(FunctionTemplate, "new", (VALUE(*)(...))v8_FunctionTemplate_New, -1);
|
||||
rr_define_method(FunctionTemplate, "GetFunction", Func::GetFunction, 0);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Local<ObjectTemplate> Racer_Create_V8_ObjectTemplate(VALUE value) {
|
||||
printf("legacy!!!\n");
|
||||
rb_raise(rb_eScriptError, "wrapped legacy template");
|
||||
Local<ObjectTemplate> tmpl = ObjectTemplate::New();
|
||||
// tmpl->SetInternalFieldCount(2);
|
||||
tmpl->SetNamedPropertyHandler(
|
||||
|
@ -83,33 +210,3 @@ Local<ObjectTemplate> Racer_Create_V8_ObjectTemplate(VALUE value) {
|
|||
);
|
||||
return tmpl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
VALUE v8_Template_Set(VALUE self, VALUE name, VALUE value) {
|
||||
HandleScope handles;
|
||||
Local<Template> tmpl = V8_Ref_Get<Template>(self);
|
||||
Local<Data> data = V8_Ref_Get<Data>(value);
|
||||
tmpl->Set(RSTRING_PTR(name), data);
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
VALUE v8_ObjectTemplate_New(VALUE clazz) {
|
||||
HandleScope handles;
|
||||
return V8_Ref_Create(clazz, ObjectTemplate::New());
|
||||
}
|
||||
|
||||
VALUE v8_FunctionTemplate_New(int argc, VALUE *argv, VALUE self) {
|
||||
VALUE code;
|
||||
rb_scan_args(argc, argv, "00&", &code);
|
||||
HandleScope handles;
|
||||
Local<FunctionTemplate> t = FunctionTemplate::New(RacerRubyInvocationCallback, External::Wrap((void *)code));
|
||||
return V8_Ref_Create(self,t,code);
|
||||
}
|
||||
|
||||
VALUE v8_FunctionTemplate_GetFunction(VALUE self) {
|
||||
HandleScope handles;
|
||||
Local<FunctionTemplate> t = V8_Ref_Get<FunctionTemplate>(self);
|
||||
return rr_v82rb(t->GetFunction());
|
||||
}
|
||||
|
||||
|
|
|
@ -95,10 +95,12 @@ namespace {
|
|||
}
|
||||
|
||||
VALUE rr_cV8_C_Value;
|
||||
VALUE rr_cV8_C_Empty;
|
||||
|
||||
void rr_init_value() {
|
||||
rr_cV8_C_Value = rr_define_class("Value");
|
||||
|
||||
rr_cV8_C_Empty = rr_define_const("Empty", rr_v8_ref_create(rr_cV8_C_Value, Handle<Value>()));
|
||||
|
||||
rr_define_method(rr_cV8_C_Value, "IsUndefined", IsUndefined, 0);
|
||||
rr_define_method(rr_cV8_C_Value, "IsNull", IsNull, 0);
|
||||
rr_define_method(rr_cV8_C_Value, "IsTrue", IsTrue, 0);
|
||||
|
@ -122,10 +124,10 @@ void rr_init_value() {
|
|||
rr_define_method(rr_cV8_C_Value, "ToInteger", ToInteger, 0);
|
||||
rr_define_method(rr_cV8_C_Value, "ToUint32", ToUint32, 0);
|
||||
rr_define_method(rr_cV8_C_Value, "ToArrayIndex", ToArrayIndex, 0);
|
||||
|
||||
|
||||
rr_define_method(rr_cV8_C_Value, "Equals", Equals, 1);
|
||||
rr_define_method(rr_cV8_C_Value, "StrictEquals", StrictEquals, 1);
|
||||
|
||||
|
||||
rr_define_method(rr_cV8_C_Value, "BooleanValue", BooleanValue, 0);
|
||||
rr_define_method(rr_cV8_C_Value, "NumberValue", NumberValue, 0);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "rr.h"
|
||||
|
||||
extern VALUE rr_cV8_C_Value;
|
||||
extern VALUE rr_cV8_C_Empty;
|
||||
void rr_init_value();
|
||||
VALUE rr_wrap_v8_value(v8::Handle<v8::Value>& value);
|
||||
#endif
|
|
@ -2,7 +2,8 @@ module V8
|
|||
class Function < V8::Object
|
||||
|
||||
def call(thisObject, *args)
|
||||
To.ruby(@native.Call(To.v8(thisObject), *args.map {|a| To.v8(a)}))
|
||||
this = To.v8(thisObject)
|
||||
To.ruby(@native.Call(this, *args.map {|a| To.v8(a)}))
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -28,7 +28,7 @@ module V8
|
|||
end
|
||||
|
||||
def each
|
||||
for prop in To.ruby(@native.GetPropertyNames())
|
||||
for prop in To.rb(@native.GetPropertyNames())
|
||||
yield prop, self[prop]
|
||||
end
|
||||
end
|
||||
|
|
98
lib/v8/to.rb
98
lib/v8/to.rb
|
@ -12,24 +12,110 @@ module V8
|
|||
value
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
alias_method :rb, :ruby
|
||||
|
||||
def v8(value)
|
||||
case value
|
||||
when String, Symbol then C::String::New(value.to_s)
|
||||
when V8::Object
|
||||
value.instance_eval {@native}
|
||||
when String, Symbol
|
||||
C::String::New(value.to_s)
|
||||
when Proc,Method
|
||||
C::FunctionTemplate::New() do |arguments|
|
||||
template = C::FunctionTemplate::New() do |arguments|
|
||||
rbargs = []
|
||||
for i in 0..arguments.Length() - 1
|
||||
rbargs << To.ruby(arguments[i])
|
||||
end
|
||||
To.v8(value.call(*rbargs))
|
||||
end.GetFunction()
|
||||
when V8::Object then value.instance_eval {@native}
|
||||
else
|
||||
end
|
||||
return template.GetFunction()
|
||||
when nil,Numeric
|
||||
value
|
||||
else
|
||||
rubyobject = C::ObjectTemplate::New()
|
||||
rubyobject.SetNamedPropertyHandler(
|
||||
NamedPropertyGetter,
|
||||
NamedPropertySetter,
|
||||
nil,
|
||||
nil,
|
||||
NamedPropertyEnumerator
|
||||
)
|
||||
obj = nil
|
||||
unless C::Context::InContext()
|
||||
cxt = C::Context::New()
|
||||
cxt.Enter()
|
||||
begin
|
||||
obj = rubyobject.NewInstance()
|
||||
obj.SetHiddenValue(C::String::New("TheRubyRacer::RubyObject"), C::External::Wrap(value))
|
||||
ensure
|
||||
cxt.Exit()
|
||||
end
|
||||
else
|
||||
obj = rubyobject.NewInstance()
|
||||
obj.SetHiddenValue(C::String::New("TheRubyRacer::RubyObject"), C::External::Wrap(value))
|
||||
end
|
||||
return obj
|
||||
end
|
||||
end
|
||||
|
||||
class NamedPropertyGetter
|
||||
def self.call(property, info)
|
||||
name = To.rb(property)
|
||||
obj = To.rb(info.This())
|
||||
perl_name = To.perl_case(name)
|
||||
methods = obj.public_methods(false).map(&:to_s)
|
||||
method_name = if methods.include?(name)
|
||||
name
|
||||
elsif methods.include?(perl_name)
|
||||
perl_name
|
||||
end
|
||||
if method_name
|
||||
method = obj.method(method_name)
|
||||
if method.arity == 0
|
||||
To.v8(method.call())
|
||||
else
|
||||
To.v8(method)
|
||||
end
|
||||
else
|
||||
C::Empty
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class NamedPropertySetter
|
||||
def self.call(property, value, info)
|
||||
obj = To.rb(info.This())
|
||||
setter = To.rb(property) + "="
|
||||
camel_name = To.camel_case(setter)
|
||||
perl_name = To.perl_case(setter)
|
||||
methods = obj.public_methods(false).map(&:to_s)
|
||||
if methods.include?(perl_name)
|
||||
obj.send(perl_name, To.rb(value))
|
||||
elsif methods.include?(camel_name)
|
||||
obj.send(camel_name, To.rb(value))
|
||||
else
|
||||
C::Empty
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class NamedPropertyEnumerator
|
||||
def self.call(info)
|
||||
obj = To.rb(info.This())
|
||||
camel_methods = obj.public_methods(false).inject(Set.new) do |set, name|
|
||||
set.tap do
|
||||
set << To.camel_case(name)
|
||||
end
|
||||
end
|
||||
names = V8::C::Array::New(camel_methods.length)
|
||||
camel_methods.each_with_index do |name, i|
|
||||
names.Set(i, C::String::New(name))
|
||||
end
|
||||
return names
|
||||
end
|
||||
end
|
||||
|
||||
def camel_case(str)
|
||||
str.to_s.gsub(/_(\w)/) {$1.upcase}
|
||||
end
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
begin
|
||||
require 'spec'
|
||||
rescue LoadError
|
||||
|
|
Loading…
Reference in a new issue