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:
parent
09868b94c7
commit
f3811a48c2
12 changed files with 186 additions and 29 deletions
|
@ -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>();
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
15
spec/v8/to_spec.rb
Normal 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
|
Loading…
Add table
Reference in a new issue