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

move callbacks into ruby code. make sure handle scopes are everywhere. raise exception when context needs to be open rather than segfault in certain cases.

This commit is contained in:
Charles Lowell 2010-05-19 15:55:11 +03:00
parent cffef332b1
commit 5b2c88002c
15 changed files with 246 additions and 35 deletions

View file

@ -8,8 +8,7 @@
#include "v8_script.h" #include "v8_script.h"
#include "v8_template.h" #include "v8_template.h"
#include "v8_try_catch.h" #include "v8_try_catch.h"
#include "v8_arguments.h" #include "v8_callbacks.h"
#include "converters.h"
#include <stdio.h> #include <stdio.h>
@ -29,6 +28,6 @@ extern "C" {
rr_init_v8_array(); rr_init_v8_array();
rr_init_msg(); rr_init_msg();
rr_init_v8_try_catch(); rr_init_v8_try_catch();
rr_init_v8_arguments(); rr_init_v8_callbacks();
} }
} }

125
ext/v8/v8_callbacks.cpp Normal file
View file

@ -0,0 +1,125 @@
#include "v8_callbacks.h"
#include "rr.h"
using namespace v8;
namespace {
VALUE ArgumentsClass;
VALUE AccessorInfoClass;
struct Wrap {
Persistent<Object> thisObj;
Persistent<Object> holder;
Persistent<Value> data;
Wrap(Handle<Object> thisObj, Handle<Object> holder, Handle<Value> data);
virtual ~Wrap();
};
Wrap::Wrap(Handle<Object> thisObj_, Handle<Object> holder_, Handle<Value> data_) : thisObj(thisObj_), holder(holder_), data(data_) {}
Wrap::~Wrap() {
thisObj.Dispose();
holder.Dispose();
data.Dispose();
}
void gc_mark(Wrap *wrapper) {}
void gc_free(Wrap *wrapper) {
delete wrapper;
}
struct WrapArguments : Wrap {
int length;
Persistent<Function> callee;
bool isConstructCall;
Persistent<Array> values;
WrapArguments(const Arguments& arguments);
virtual ~WrapArguments();
};
WrapArguments::WrapArguments(const Arguments& arguments) : Wrap(arguments.This(), arguments.Holder(), arguments.Data()), values(*Array::New(arguments.Length())) {
length = arguments.Length();
callee = *arguments.Callee();
isConstructCall = arguments.IsConstructCall();
for (int i = 0; i < arguments.Length(); i++) {
values->Set(Integer::New(i), arguments[i]);
}
}
WrapArguments::~WrapArguments() {
callee.Dispose();
values.Dispose();
}
Wrap *info(VALUE value) {
Wrap *wrap = 0;
Data_Get_Struct(value, struct Wrap, wrap);
return wrap;
}
WrapArguments* args(VALUE value) {
WrapArguments *wrap = 0;
Data_Get_Struct(value, struct WrapArguments, wrap);
return wrap;
}
VALUE Length(VALUE self) {
return rr_v82rb(args(self)->length);
}
VALUE Get(VALUE self, VALUE index) {
int i = NUM2INT(rb_to_int(index));
return rr_v82rb(args(self)->values->Get(i));
}
VALUE Callee(VALUE self) {
return rr_v82rb(args(self)->callee);
}
VALUE This(VALUE self) {
return rr_v82rb(info(self)->thisObj);
}
VALUE Holder(VALUE self) {
return rr_v82rb(info(self)->holder);
}
VALUE IsConstructCall(VALUE self) {
return rr_v82rb(args(self)->isConstructCall);
}
VALUE _Data(VALUE self) {
return rr_v82rb(info(self)->data);
}
struct WrapAccessorInfo : Wrap {
WrapAccessorInfo(const AccessorInfo& info);
};
WrapAccessorInfo::WrapAccessorInfo(const AccessorInfo& info) : Wrap(info.This(), info.Holder(), info.Data()) {}
}
void rr_init_v8_callbacks() {
AccessorInfoClass = rr_define_class("AccessorInfo");
rr_define_method(AccessorInfoClass, "This", This, 0);
rr_define_method(AccessorInfoClass, "Holder", Holder, 0);
rr_define_method(AccessorInfoClass, "Data", _Data, 0);
ArgumentsClass = rr_define_class("Arguments");
rr_define_method(ArgumentsClass, "This", This, 0);
rr_define_method(ArgumentsClass, "Holder", Holder, 0);
rr_define_method(ArgumentsClass, "Data", _Data, 0);
rr_define_method(ArgumentsClass, "Length", Length, 0);
rr_define_method(ArgumentsClass, "Callee", Callee, 0);
rr_define_method(ArgumentsClass, "IsConstructCall", IsConstructCall, 0);
rr_define_method(ArgumentsClass, "[]", Get, 1);
}
VALUE rr_v82rb(const AccessorInfo& info) {
return Data_Wrap_Struct(AccessorInfoClass, gc_mark, gc_free, new WrapAccessorInfo(info));
}
VALUE rr_v82rb(const Arguments& arguments) {
return Data_Wrap_Struct(ArgumentsClass, gc_mark, gc_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);
}

9
ext/v8/v8_callbacks.h Normal file
View file

@ -0,0 +1,9 @@
#ifndef _RR_V8_CALLBACKS_
#define _RR_V8_CALLBACKS_
#include "v8.h"
void rr_init_v8_callbacks();
// VALUE rr_v82rb(v8::AccessorInfo& info);
// VALUE rr_v82rb(v8::Arguments& arguments);
v8::Handle<v8::Value> RubyInvocationCallback(const v8::Arguments& args);
#endif

View file

@ -57,16 +57,19 @@ namespace {
} }
VALUE Enter(VALUE self) { VALUE Enter(VALUE self) {
HandleScope handles;
unwrap(self)->Enter(); unwrap(self)->Enter();
return self; return self;
} }
VALUE Exit(VALUE self) { VALUE Exit(VALUE self) {
HandleScope handles;
unwrap(self)->Exit(); unwrap(self)->Exit();
return self; return self;
} }
VALUE IsEntered(VALUE self) { VALUE IsEntered(VALUE self) {
HandleScope handles;
if (Context::InContext()) { if (Context::InContext()) {
return rr_v82rb(unwrap(self) == Context::GetEntered()); return rr_v82rb(unwrap(self) == Context::GetEntered());
} else { } else {

View file

@ -30,7 +30,14 @@ namespace {
cxt.Dispose(); cxt.Dispose();
} }
} else { } else {
thisObject = rr_rb2v8(recv)->ToObject(); if (!Context::InContext()) {
Persistent<Context> cxt = Context::New();
cxt->Enter();
thisObject = rr_rb2v8(recv)->ToObject();
cxt->Exit();
} else {
thisObject = rr_rb2v8(recv)->ToObject();
}
} }
int f_argc = argc - 1; int f_argc = argc - 1;
Local<Value> arguments[f_argc]; Local<Value> arguments[f_argc];

View file

@ -22,22 +22,27 @@ namespace {
if (rb_obj_is_kind_of(key, rb_cNumeric)) { if (rb_obj_is_kind_of(key, rb_cNumeric)) {
return rr_v82rb(obj->Get(NUM2UINT(key))); return rr_v82rb(obj->Get(NUM2UINT(key)));
} else { } else {
return rr_v82rb(obj->Get(rr_rb2v8(rb_str_to_str(key)))); return rr_v82rb(obj->Get(rr_rb2v8(key)->ToString()));
} }
} }
VALUE New(VALUE clazz) { VALUE New(VALUE clazz) {
HandleScope handles; HandleScope handles;
if (!Context::InContext()) {
rb_raise(rb_eScriptError, "tried to allocate an Object, but no context was open");
return Qnil;
}
return V8_Ref_Create(clazz, Object::New()); return V8_Ref_Create(clazz, Object::New());
} }
VALUE Set(VALUE self, VALUE key, VALUE value) { VALUE Set(VALUE self, VALUE key, VALUE value) {
HandleScope handles; HandleScope handles;
Local<Object> obj = unwrap(self); Local<Object> obj = unwrap(self);
if (rb_obj_is_kind_of(key, rb_cNumeric)) {
VALUE keystr = rb_funcall(key, rb_intern("to_s"), 0); return rr_v82rb(obj->Set(NUM2UINT(key), RB2V8(value)));
obj->Set(rr_rb2v8(keystr), RB2V8(value)); } else {
return Qnil; return rr_v82rb(obj->Set(rr_rb2v8(key), rr_rb2v8(value)));
}
} }
VALUE GetPropertyNames(VALUE self) { VALUE GetPropertyNames(VALUE self) {

View file

@ -12,10 +12,12 @@ namespace {
return V8_Ref_Get<String>(value); return V8_Ref_Get<String>(value);
} }
VALUE New(VALUE klazz, VALUE data) { VALUE New(VALUE klazz, VALUE data) {
HandleScope handles;
VALUE str = rb_funcall(data, rb_intern("to_s"), 0); VALUE str = rb_funcall(data, rb_intern("to_s"), 0);
return V8_Ref_Create(StringClass, String::New(RSTRING_PTR(str), RSTRING_LEN(str))); return V8_Ref_Create(StringClass, String::New(RSTRING_PTR(str), RSTRING_LEN(str)));
} }
VALUE Utf8Value(VALUE self) { VALUE Utf8Value(VALUE self) {
HandleScope handles;
return rb_str_new2(*String::Utf8Value(unwrap(self))); return rb_str_new2(*String::Utf8Value(unwrap(self)));
} }
VALUE Utf16Value(VALUE self) { VALUE Utf16Value(VALUE self) {
@ -23,6 +25,7 @@ namespace {
return Qnil; return Qnil;
} }
VALUE AsciiValue(VALUE self) { VALUE AsciiValue(VALUE self) {
HandleScope handles;
return rb_str_new2(*String::AsciiValue(unwrap(self))); return rb_str_new2(*String::AsciiValue(unwrap(self)));
} }
} }
@ -36,7 +39,6 @@ VALUE rr_reflect_v8_string(Handle<Value> value) {
void rr_init_str() { void rr_init_str() {
StringClass = rr_define_class("String"); StringClass = rr_define_class("String");
rr_define_singleton_method(StringClass, "New", New, 1); rr_define_singleton_method(StringClass, "New", New, 1);
rr_define_singleton_method(StringClass, "new", New, 1);
rr_define_method(StringClass, "Utf8Value", Utf8Value, 0); rr_define_method(StringClass, "Utf8Value", Utf8Value, 0);
rr_define_method(StringClass, "Utf16Value", Utf16Value, 0); rr_define_method(StringClass, "Utf16Value", Utf16Value, 0);
rr_define_method(StringClass, "AsciiValue", AsciiValue, 0); rr_define_method(StringClass, "AsciiValue", AsciiValue, 0);

View file

@ -3,22 +3,70 @@
#include "v8_ref.h" #include "v8_ref.h"
#include "v8_func.h" #include "v8_func.h"
#include "v8_template.h" #include "v8_template.h"
#include "converters.h" #include "v8_callbacks.h"
#include "callbacks.h" #include "callbacks.h"
#include "converters.h"
using namespace v8; using namespace v8;
namespace {
Local<Template> tmpl(VALUE self) {
return V8_Ref_Get<Template>(self);
}
Local<ObjectTemplate> obj(VALUE self) {
return V8_Ref_Get<ObjectTemplate>(self);
}
Local<FunctionTemplate> func(VALUE self) {
return V8_Ref_Get<FunctionTemplate>(self);
}
VALUE Set(VALUE self, VALUE name, VALUE value) {
HandleScope handles;
Local<String> key = rr_rb2v8(name)->ToString();
Local<Data> data = V8_Ref_Get<Data>(value);
tmpl(self)->Set(key, data);
return Qnil;
}
namespace Obj {
}
namespace Func {
VALUE New(VALUE function_template) {
HandleScope handles;
rb_need_block();
VALUE code = rb_block_proc();
if (NIL_P(code)) {
return Qnil;
}
Local<FunctionTemplate> templ = FunctionTemplate::New(RubyInvocationCallback, External::Wrap((void *)code));
return V8_Ref_Create(function_template,templ,code);
}
VALUE GetFunction(VALUE self) {
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());
}
}
}
void rr_init_template() { void rr_init_template() {
VALUE Template = rr_define_class("Template"); VALUE Template = rr_define_class("Template");
rb_define_method(Template, "Set", (VALUE(*)(...))v8_Template_Set, 2); rr_define_method(Template, "Set", Set, 2);
VALUE ObjectTemplate = rr_define_class("ObjectTemplate", Template); VALUE ObjectTemplate = rr_define_class("ObjectTemplate", Template);
rb_define_singleton_method(ObjectTemplate, "new", (VALUE(*)(...))v8_ObjectTemplate_New, 0); rb_define_singleton_method(ObjectTemplate, "new", (VALUE(*)(...))v8_ObjectTemplate_New, 0);
VALUE FunctionTemplate = rr_define_class("FunctionTemplate", Template); VALUE FunctionTemplate = rr_define_class("FunctionTemplate", Template);
rb_define_singleton_method(FunctionTemplate, "new", (VALUE(*)(...))v8_FunctionTemplate_New, -1); rr_define_singleton_method(FunctionTemplate, "New", Func::New, 0);
rb_define_method(FunctionTemplate, "GetFunction", (VALUE(*)(...))v8_FunctionTemplate_GetFunction, 0); // rb_define_singleton_method(FunctionTemplate, "new", (VALUE(*)(...))v8_FunctionTemplate_New, -1);
rr_define_method(FunctionTemplate, "GetFunction", Func::GetFunction, 0);
} }

View file

@ -7,11 +7,6 @@ void rr_init_template();
v8::Local<v8::ObjectTemplate> Racer_Create_V8_ObjectTemplate(VALUE object); 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); VALUE v8_ObjectTemplate_New(VALUE clazz);
VALUE v8_FunctionTemplate_New(int argc, VALUE *argv, VALUE self);
VALUE v8_FunctionTemplate_GetFunction(VALUE self);
#endif #endif

View file

@ -5,6 +5,7 @@ module V8
attr_reader :native attr_reader :native
def initialize(opts = {}) def initialize(opts = {})
@native = C::Context::New(opts[:with]) @native = C::Context::New(opts[:with])
yield(self) if block_given?
end end
def open def open
@ -20,9 +21,9 @@ module V8
C::TryCatch.try do |try| C::TryCatch.try do |try|
@native.enter do @native.enter do
script = C::Script::Compile(To.v8(javascript.to_s), To.v8(filename.to_s)) script = C::Script::Compile(To.v8(javascript.to_s), To.v8(filename.to_s))
raise JavascriptError.new(try) if try.HasCaught() JavascriptError.check try
result = script.Run() result = script.Run()
raise JavascriptError.new(try) if try.HasCaught() JavascriptError.check try
To.ruby(result) To.ruby(result)
end end
end end
@ -46,9 +47,9 @@ module V8
def []=(key, value) def []=(key, value)
value.tap do value.tap do
open do open do
@native.Global().tap do |scope| @native.Global().tap do |scope|
scope.Set(key.to_s, value) scope.Set(To.v8(key.to_s), To.v8(value))
end end
end end
end end
@ -78,6 +79,10 @@ module V8
@javascript_stacktrace = To.ruby(try.StackTrace()) @javascript_stacktrace = To.ruby(try.StackTrace())
super("#{To.ruby(msg.Get())}: #{@source_name}:#{@line_number}") super("#{To.ruby(msg.Get())}: #{@source_name}:#{@line_number}")
end end
def self.check(try)
raise JavascriptError.new(try) if try.HasCaught()
end
end end

View file

@ -9,14 +9,14 @@ module V8
def [](key) def [](key)
@native.context.enter do @native.context.enter do
To.ruby(@native.Get(key.to_s)) To.ruby(@native.Get(To.v8(key)))
end end
end end
def []=(key, value) def []=(key, value)
value.tap do value.tap do
@native.context.enter do @native.context.enter do
@native.Set(key.to_s, value) @native.Set(To.v8(key), To.v8(value))
end end
end end
end end

View file

@ -15,10 +15,16 @@ module V8
def v8(value) def v8(value)
case value case value
when String then C::String.new(value) when String, Symbol then C::String::New(value.to_s)
when Proc then C::FunctionTemplate.new(&value).GetFunction() when Proc,Method
when Method then C::FunctionTemplate.new(&value.to_proc).GetFunction() C::FunctionTemplate::New() do |arguments|
when V8::Object then value.instance_variable_get(:@native) 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 else
value value
end end

View file

@ -4,28 +4,28 @@ include V8
describe C::Function do describe C::Function do
it "is callable" do it "is callable" do
Context.new.open do |cxt| Context.new do |cxt|
f = cxt.eval('(function() {return "Hello World"})', '<eval>'); f = cxt.eval('(function() {return "Hello World"})', '<eval>');
f.call(nil).should == "Hello World" f.call(nil).should == "Hello World"
end end
end end
it "receives proper argument length from ruby" do it "receives proper argument length from ruby" do
Context.new.open do |cxt| Context.new do |cxt|
f = cxt.eval('(function() {return arguments.length})', 'eval') f = cxt.eval('(function() {return arguments.length})', 'eval')
f.call(nil,1, 2, 3).should == 3 f.call(nil,1, 2, 3).should == 3
end end
end end
it "maps all arguments from ruby" do it "maps all arguments from ruby" do
Context.new.open do |cxt| Context.new do |cxt|
f = cxt.eval('(function(one, two, three) {return one + two + three})', 'eval') f = cxt.eval('(function(one, two, three) {return one + two + three})', 'eval')
f.call(nil, 1,2,3).should == 6 f.call(nil, 1,2,3).should == 6
end end
end end
it "properly maps ruby objects back and forth from arguments to return value" do it "properly maps ruby objects back and forth from arguments to return value" do
Context.new.open do |cxt| Context.new do |cxt|
Object.new.tap do |this| Object.new.tap do |this|
f = cxt.eval('(function() {return this})', 'eval') f = cxt.eval('(function() {return this})', 'eval')
f.call(this).should be(this) f.call(this).should be(this)
@ -34,9 +34,16 @@ describe C::Function do
end end
it "can be called outside of a context" do it "can be called outside of a context" do
Context.new.open do |cxt| Context.new do |cxt|
@f = cxt.eval('(function() {return "Call Me"})', 'eval') @f = cxt.eval('(function() {return "Call Me"})', 'eval')
end end
@f.call(nil).should == "Call Me" @f.call(nil).should == "Call Me"
end end
it "is reflected properly" do
Context.new do |cxt|
cxt['say'] = lambda {|word, times| word * times}
cxt.eval('say("Hello", 3)').should == "HelloHelloHello"
end
end
end end

View file

@ -16,4 +16,5 @@ require 'v8'
require 'erb' require 'erb'
def rputs(msg) def rputs(msg)
puts "<div>#{ERB::Util.h(msg)}</div>" puts "<div>#{ERB::Util.h(msg)}</div>"
$stdout.flush
end end

View file

@ -11,5 +11,4 @@ describe V8::To do
To.perl_case("XMLDocument").should == "xml_document" To.perl_case("XMLDocument").should == "xml_document"
end end
end end