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

Dynamic handling of ruby properties. Invoking ruby setters from javascript. Removed a bunch of segfaulting. Now objects have references to the context that created them.

This commit is contained in:
Charles Lowell 2010-01-18 00:25:40 +02:00
parent 09868b94c7
commit f3811a48c2
12 changed files with 186 additions and 29 deletions

View file

@ -5,6 +5,39 @@
using namespace v8;
namespace {
VALUE unwrap(const AccessorInfo& info) {
return (VALUE)External::Unwrap(info.Data());
}
Local<Array> TO_ARRAY(Arguments& args) {
Local<Array> array = Array::New(args.Length());
for (int i = 0; i < args.Length(); i++) {
array->Set(Integer::New(i), args[i]);
}
}
Local<Value> Racer_Call_Ruby_Method(VALUE object, VALUE method, Local<Array> args) {
VALUE * arguments = new VALUE[args->Length()];
for (unsigned int i = 0; i < args->Length(); i++) {
Handle<Value> val = args->Get(Integer::New(i));
arguments[i] = V82RB(val);
}
VALUE result = rb_funcall2(object, rb_to_id(method), args->Length(), arguments);
Local<Value> converted = RB2V8(result);
return converted;
}
Local<Value> Racer_Access_Ruby_Property(VALUE object, VALUE name) {
VALUE method = rb_obj_method(object, name);
if (FIX2INT(rb_funcall(method, rb_intern("arity"), 0)) == 0) {
return Racer_Call_Ruby_Method(object, name, Array::New(0));
} else {
return RB2V8(method);
}
}
}
Handle<Value> RacerRubyInvocationCallback(const Arguments& args) {
VALUE code = (VALUE)External::Unwrap(args.Data());
if (NIL_P(code)) {
@ -22,4 +55,98 @@ Handle<Value> RacerRubyInvocationCallback(const Arguments& args) {
Handle<Value> convertedResult = RB2V8(result);
return convertedResult ;
}
}
}
/**
* NamedProperty[Getter|Setter] are used as interceptors on object.
* See ObjectTemplate::SetNamedPropertyHandler.
*/
Handle<Value> RacerRubyNamedPropertyGetter(Local<String> property, const AccessorInfo& info) {
// printf("Getter '%s'<br/>", *String::AsciiValue(property));
if (property->Length() == 0) {
return Handle<Value>();
}
VALUE object = unwrap(info);
VALUE camel_name = V82RB((Local<Value>&)property);
VALUE perl_name = rb_funcall(V8_To, rb_intern("perl_case"), 1, camel_name);
VALUE methods = rb_funcall(object, rb_intern("public_methods"), 1, Qfalse);
if (RTEST(rb_ary_includes(methods, perl_name))) {
return Racer_Access_Ruby_Property(object, perl_name);
}
if (RTEST(rb_ary_includes(methods, camel_name))) {
return Racer_Access_Ruby_Property(object, camel_name);
}
return Handle<Value>();
}
/**
* Returns the value if the setter intercepts the request.
* Otherwise, returns an empty handle.
*/
Handle<Value> RacerRubyNamedPropertySetter(Local<String> property, Local<Value> value, const AccessorInfo& info) {
if (property->Length() == 0) {
return Handle<Value>();
}
// printf("Setter: '%s'<br/>", *String::AsciiValue(property));
std::string setter = V82String((Handle<Value>&)property);
setter += "=";
Local<String> setter_name = String::New(setter.c_str());
VALUE object = unwrap(info);
VALUE camel_name = V82RB((Local<Value>&)setter_name);
VALUE perl_name = rb_funcall(V8_To, rb_intern("perl_case"), 1, camel_name);
VALUE methods = rb_funcall(object, rb_intern("public_methods"), 1, Qfalse);
Local<Array> args = Array::New(1);
args->Set(Integer::New(0), value);
if (RTEST(rb_ary_includes(methods, perl_name))) {
Racer_Call_Ruby_Method(object, perl_name, args);
return value;
}
if (RTEST(rb_ary_includes(methods, camel_name))) {
Racer_Call_Ruby_Method(object, camel_name, args);
return value;
}
return Handle<Value>();
}
/**
* Returns a non-empty handle if the interceptor intercepts the request.
* The result is true if the property exists and false otherwise.
*/
Handle<Boolean> RacerRubyNamedPropertyQuery(Local<String> property, const AccessorInfo& info) {
printf("Query: '%s'<br/>", *String::AsciiValue(property));
if (property->Length() == 0) {
return False();
}
VALUE object = unwrap(info);
VALUE methods = rb_funcall(object, rb_intern("public_methods"), 1, Qfalse);
VALUE attr_name = V82RB((Local<Value>&)property);
VALUE perl_name = rb_funcall(V8_To, rb_intern("perl_case"), 1, attr_name);
if (RTEST(rb_ary_includes(methods, attr_name)) || RTEST(rb_ary_includes(methods, perl_name))) {
return True();
} else {
return Handle<Boolean>();
}
}
/**
* 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> RacerRubyNamedPropertyDeleter(Local<String> property, const AccessorInfo& info) {
return False();
}
/**
* Returns an array containing the names of the properties the named
* property getter intercepts.
*/
Handle<Array> RacerRubyNamedPropertyEnumerator(const AccessorInfo& info) {
return Local<Array>();
}

View file

@ -5,4 +5,10 @@
v8::Handle<v8::Value> RacerRubyInvocationCallback(const v8::Arguments& args);
v8::Handle<v8::Value> RacerRubyNamedPropertyGetter(v8::Local<v8::String> property, const v8::AccessorInfo& info);
v8::Handle<v8::Value> RacerRubyNamedPropertySetter(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::AccessorInfo& info);
v8::Handle<v8::Boolean> RacerRubyNamedPropertyQuery(v8::Local<v8::String> property, const v8::AccessorInfo& info);
v8::Handle<v8::Boolean> RacerRubyNamedPropertyDeleter(v8::Local<v8::String> property, const v8::AccessorInfo& info);
v8::Handle<v8::Array> RacerRubyNamedPropertyEnumerator(const v8::AccessorInfo& info);
#endif /* end of include guard: CALLBACKS_H_8VK3LWBG */

View file

@ -3,6 +3,7 @@
#include "v8_ref.h"
#include "v8_obj.h"
#include "v8_cxt.h"
#include "v8_template.h"
using namespace v8;
@ -32,8 +33,10 @@ VALUE V82RB(Handle<Value>& value) {
if (value->IsObject()) {
Local<Object> object(Object::Cast(*value));
Local<Value> peer = object->GetHiddenValue(String::New("TheRubyRacer::RubyObject"));
// Local<Value> peer = object->GetInternalField(0);
if (peer.IsEmpty()) {
VALUE context_ref = V8_Ref_Create(V8_C_Context, Context::GetCurrent());
// object->SetPointerInInternalField(1, (void *)context_ref);
object->SetHiddenValue(String::New("TheRubyRacer::Context"), External::Wrap((void *)context_ref));
return V8_Ref_Create(V8_C_Object, value, context_ref);
} else {
@ -55,10 +58,10 @@ Local<Value> RB2V8(VALUE value) {
if (convert(value, result)) {
return result;
}
Local<ObjectTemplate> tmpl = RB_VALUE_2_V8_ObjectTemplate(value);
Local<Object> object = tmpl->NewInstance();
object->SetHiddenValue(String::New("TheRubyRacer::RubyObject"), External::Wrap((void *)value));
return object;
Local<Object> o = Racer_Create_V8_ObjectTemplate(value)->NewInstance();
o->SetHiddenValue(String::New("TheRubyRacer::RubyObject"), External::Wrap((void *) value));
// o->SetPointerInInternalField(0, (void*)value);
return o;
}
std::string V82String(Handle<Value>& value) {
@ -77,21 +80,4 @@ std::string V82String(Handle<Value>& value) {
}
return UNDEFINED_STR;
}
Local<ObjectTemplate> RB_VALUE_2_V8_ObjectTemplate(VALUE value) {
Local<ObjectTemplate> tmpl = ObjectTemplate::New();
VALUE methods = rb_funcall(value, rb_intern("public_methods"), 1, Qfalse);
int len = RARRAY_LEN(methods);
for (int i = 0; i < len; i++) {
VALUE method_name = RARRAY_PTR(methods)[i];
VALUE camel_method_name = rb_funcall(V8_To, rb_intern("camelcase"), 1, method_name);
VALUE method = rb_funcall(value, rb_intern("method"), 1, method_name);
Local<String> keystr = (String *)*RB2V8(method_name);
Local<String> camelstr = (String *)*RB2V8(camel_method_name);
Local<FunctionTemplate> fun = FunctionTemplate::New(RacerRubyInvocationCallback, External::Wrap((void *)method));
tmpl->Set(keystr, fun);
tmpl->Set(camelstr, fun);
}
return tmpl;
}
}

View file

@ -20,6 +20,4 @@ v8::Local<v8::Value> RB2V8(VALUE value);
std::string RB2String(VALUE value);
std::string V82String(v8::Handle<v8::Value>& value);
v8::Local<v8::ObjectTemplate> RB_VALUE_2_V8_ObjectTemplate(VALUE value);
#endif

View file

@ -1,5 +1,6 @@
#include "v8_cxt.h"
#include "v8_msg.h"
#include "v8_template.h"
#include "converters.h"
using namespace v8;
@ -16,9 +17,10 @@ VALUE v8_Context_New(int argc, VALUE *argv, VALUE self) {
if (NIL_P(scope)) {
return V8_Ref_Create(self, Context::New());
} else {
Persistent<Context> context = Context::New(0, RB_VALUE_2_V8_ObjectTemplate(scope));
Persistent<Context> context = Context::New(0, Racer_Create_V8_ObjectTemplate(scope));
Context::Scope enter(context);
context->Global()->SetHiddenValue(String::New("TheRubyRacer::RubyObject"), External::Wrap((void *)scope));
// context->Global()->SetPointerInInternalField(0, (void*)scope);
VALUE ref = V8_Ref_Create(self, context, scope);
context.Dispose();
return ref;

View file

@ -47,5 +47,6 @@ VALUE v8_Object_context(VALUE self) {
HandleScope handles;
Local<Object> object = unwrap(self);
Local<Value> cxt = object->GetHiddenValue(String::New("TheRubyRacer::Context"));
// Local<Value> cxt = object->GetInternalField(1);
return cxt.IsEmpty() ? Qnil : (VALUE)External::Unwrap(cxt);
}

View file

@ -7,6 +7,22 @@
#include "callbacks.h"
using namespace v8;
Local<ObjectTemplate> Racer_Create_V8_ObjectTemplate(VALUE value) {
Local<ObjectTemplate> tmpl = ObjectTemplate::New();
// tmpl->SetInternalFieldCount(2);
tmpl->SetNamedPropertyHandler(
RacerRubyNamedPropertyGetter,
RacerRubyNamedPropertySetter,
0, // RacerRubyNamedPropertyQuery,
0, // RacerRubyNamedPropertyDeleter,
0, // RacerRubyNamedPropertyEnumerator,
External::Wrap((void *)value)
);
return tmpl;
}
VALUE v8_Template_Set(VALUE self, VALUE name, VALUE value) {
HandleScope handles;

View file

@ -1,6 +1,8 @@
#ifndef _RUBY_V8_TEMPLATE_
#define _RUBY_V8_TEMPLATE_
v8::Local<v8::ObjectTemplate> Racer_Create_V8_ObjectTemplate(VALUE object);
VALUE v8_Template_Set(VALUE self, VALUE name, VALUE value);
VALUE v8_ObjectTemplate_New(VALUE clazz);

View file

@ -22,7 +22,7 @@ module V8
if IO === javascript || StringIO === javascript
javascript = javascript.read()
end
@native.open do
@native.open do
@native.eval(javascript).tap do |result|
raise JavascriptError.new(result) if result.kind_of?(C::Message)
return To.ruby(result)

View file

@ -23,7 +23,11 @@ module V8
def camelcase(str)
str.to_s.gsub(/_(\w)/) {$1.upcase}
end
end
def perl_case(str)
str.gsub(/([A-Z])([a-z])/) {"_#{$1.downcase}#{$2}"}.downcase
end
end
end
end

@ -1 +1 @@
Subproject commit 0acbbe951d4cf2b8be2fc3ce2b941c132f83aa26
Subproject commit 26e3054452beb85bdb5ab6f42f1d9531f654a7d8

15
spec/v8/to_spec.rb Normal file
View file

@ -0,0 +1,15 @@
require File.dirname(__FILE__) + '/../spec_helper'
include V8
describe V8::To do
it "can convert into perl case" do
To.perl_case("foo").should == "foo"
To.perl_case("fooBar").should == "foo_bar"
To.perl_case("fooBarBaz").should == "foo_bar_baz"
To.perl_case("XMLDocument").should == "xml_document"
end
end