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_template.h"
#include "v8_try_catch.h"
#include "v8_arguments.h"
#include "converters.h"
#include "v8_callbacks.h"
#include <stdio.h>
@ -29,6 +28,6 @@ extern "C" {
rr_init_v8_array();
rr_init_msg();
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) {
HandleScope handles;
unwrap(self)->Enter();
return self;
}
VALUE Exit(VALUE self) {
HandleScope handles;
unwrap(self)->Exit();
return self;
}
VALUE IsEntered(VALUE self) {
HandleScope handles;
if (Context::InContext()) {
return rr_v82rb(unwrap(self) == Context::GetEntered());
} else {

View file

@ -30,7 +30,14 @@ namespace {
cxt.Dispose();
}
} 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;
Local<Value> arguments[f_argc];

View file

@ -22,22 +22,27 @@ namespace {
if (rb_obj_is_kind_of(key, rb_cNumeric)) {
return rr_v82rb(obj->Get(NUM2UINT(key)));
} 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) {
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());
}
VALUE Set(VALUE self, VALUE key, VALUE value) {
HandleScope handles;
Local<Object> obj = unwrap(self);
VALUE keystr = rb_funcall(key, rb_intern("to_s"), 0);
obj->Set(rr_rb2v8(keystr), RB2V8(value));
return Qnil;
if (rb_obj_is_kind_of(key, rb_cNumeric)) {
return rr_v82rb(obj->Set(NUM2UINT(key), RB2V8(value)));
} else {
return rr_v82rb(obj->Set(rr_rb2v8(key), rr_rb2v8(value)));
}
}
VALUE GetPropertyNames(VALUE self) {

View file

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

View file

@ -3,22 +3,70 @@
#include "v8_ref.h"
#include "v8_func.h"
#include "v8_template.h"
#include "converters.h"
#include "v8_callbacks.h"
#include "callbacks.h"
#include "converters.h"
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() {
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);
rb_define_singleton_method(ObjectTemplate, "new", (VALUE(*)(...))v8_ObjectTemplate_New, 0);
VALUE FunctionTemplate = rr_define_class("FunctionTemplate", Template);
rb_define_singleton_method(FunctionTemplate, "new", (VALUE(*)(...))v8_FunctionTemplate_New, -1);
rb_define_method(FunctionTemplate, "GetFunction", (VALUE(*)(...))v8_FunctionTemplate_GetFunction, 0);
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);
}

View file

@ -7,11 +7,6 @@ void rr_init_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);
VALUE v8_FunctionTemplate_New(int argc, VALUE *argv, VALUE self);
VALUE v8_FunctionTemplate_GetFunction(VALUE self);
#endif

View file

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

View file

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

View file

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

View file

@ -4,28 +4,28 @@ include V8
describe C::Function do
it "is callable" do
Context.new.open do |cxt|
Context.new do |cxt|
f = cxt.eval('(function() {return "Hello World"})', '<eval>');
f.call(nil).should == "Hello World"
end
end
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.call(nil,1, 2, 3).should == 3
end
end
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.call(nil, 1,2,3).should == 6
end
end
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|
f = cxt.eval('(function() {return this})', 'eval')
f.call(this).should be(this)
@ -34,9 +34,16 @@ describe C::Function do
end
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')
end
@f.call(nil).should == "Call Me"
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

View file

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

View file

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