1
0
Fork 0
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:
Charles Lowell 2010-05-22 09:23:22 +03:00
parent 6a5d27ee1d
commit 14edf2b0ff
13 changed files with 261 additions and 65 deletions

View file

@ -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);
}

View file

@ -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

View file

@ -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;

View file

@ -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) {

View file

@ -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);

View file

@ -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;

View file

@ -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());
}

View file

@ -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);
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -1,3 +1,4 @@
begin
require 'spec'
rescue LoadError