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:
parent
cffef332b1
commit
5b2c88002c
15 changed files with 246 additions and 35 deletions
|
@ -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
125
ext/v8/v8_callbacks.cpp
Normal 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
9
ext/v8/v8_callbacks.h
Normal 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
|
|
@ -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 {
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
14
lib/v8/to.rb
14
lib/v8/to.rb
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -16,4 +16,5 @@ require 'v8'
|
|||
require 'erb'
|
||||
def rputs(msg)
|
||||
puts "<div>#{ERB::Util.h(msg)}</div>"
|
||||
$stdout.flush
|
||||
end
|
||||
|
|
|
@ -11,5 +11,4 @@ describe V8::To do
|
|||
To.perl_case("XMLDocument").should == "xml_document"
|
||||
end
|
||||
|
||||
|
||||
end
|
Loading…
Add table
Reference in a new issue