minimal viable version, all tests are passing

This commit is contained in:
Sam 2016-05-10 17:25:42 +10:00
parent 47e0c20010
commit 9830ef0b6c
3 changed files with 121 additions and 70 deletions

View File

@ -34,8 +34,8 @@ LIBV8_COMPATIBILITY = '~> 5.0.71.35.0'
#
# Libv8.configure_makefile
#NODE_PATH = "/home/sam/Source/libv8"
NODE_PATH = "/Users/sam/Source/libv8"
NODE_PATH = "/home/sam/Source/libv8"
#NODE_PATH = "/Users/sam/Source/libv8"
#
NODE_LIBS = NODE_PATH + "/vendor/v8/out/x64.release"
NODE_INCLUDE = NODE_PATH + "/vendor/v8/include"

View File

@ -22,7 +22,6 @@ typedef struct {
Isolate* isolate;
Persistent<Context>* context;
ArrayBufferAllocator* allocator;
Persistent<ObjectTemplate>* globals;
} ContextInfo;
typedef struct {
@ -49,11 +48,6 @@ static void init_v8() {
}
}
void shutdown_v8() {
}
void* breaker(void *d) {
EvalParams* data = (EvalParams*)d;
usleep(data->timeout*1000);
@ -70,12 +64,10 @@ nogvl_context_eval(void* arg) {
Isolate::Scope isolate_scope(eval_params->context_info->isolate);
HandleScope handle_scope(eval_params->context_info->isolate);
TryCatch trycatch(eval_params->context_info->isolate);
Local<Context> context = eval_params->context_info->context->Get(eval_params->context_info->isolate);
// Local<Context> context = Context::New(eval_params->context_info->isolate, NULL,
// eval_params->context_info->globals->Get(eval_params->context_info->isolate));
Context::Scope context_scope(context);
MaybeLocal<Script> parsed_script = Script::Compile(context, *eval_params->eval);
@ -83,6 +75,15 @@ nogvl_context_eval(void* arg) {
result->executed = false;
result->value = NULL;
if (!result->parsed) {
Local<Value> exception = trycatch.Exception();
String::Utf8Value exception_str(exception);
Local<Value> stack = trycatch.StackTrace();
String::Utf8Value stack_str(stack);
printf("\nCan not Parse Exception: %s\n%s\n\n", *exception_str , *stack_str);
}
if (result->parsed) {
@ -101,6 +102,16 @@ nogvl_context_eval(void* arg) {
result->executed = !maybe_value.IsEmpty();
if (!result->executed) {
Local<Value> exception = trycatch.Exception();
String::Utf8Value exception_str(exception);
Local<Value> stack = trycatch.StackTrace();
String::Utf8Value stack_str(stack);
printf("\nRuntime err: %s\n%s\n\n", *exception_str , *stack_str);
}
if (result->executed) {
Persistent<Value>* persistent = new Persistent<Value>();
persistent->Reset(eval_params->context_info->isolate, maybe_value.ToLocalChecked());
@ -131,8 +142,40 @@ static VALUE convert_v8_to_ruby(Handle<Value> &value) {
static Handle<Value> convert_ruby_to_v8(Isolate* isolate, VALUE value) {
EscapableHandleScope scope(isolate);
Local<String> result = String::NewFromUtf8(isolate, "hello");
return scope.Escape(result);
switch (TYPE(value)) {
case T_FIXNUM:
return scope.Escape(Integer::New(isolate, NUM2INT(value)));
case T_FLOAT:
return scope.Escape(Number::New(isolate, NUM2DBL(value)));
case T_STRING:
return scope.Escape(String::NewFromUtf8(isolate, RSTRING_PTR(value), NewStringType::kNormal, (int)RSTRING_LEN(value)).ToLocalChecked());
case T_NIL:
return scope.Escape(Null(isolate));
case T_TRUE:
return scope.Escape(True(isolate));
case T_FALSE:
return scope.Escape(False(isolate));
case T_DATA:
case T_OBJECT:
case T_CLASS:
case T_ICLASS:
case T_MODULE:
case T_REGEXP:
case T_MATCH:
case T_ARRAY:
case T_HASH:
case T_STRUCT:
case T_BIGNUM:
case T_FILE:
case T_SYMBOL:
case T_UNDEF:
case T_NODE:
default:
// rb_warn("unknown conversion to V8 for: %s", RSTRING_PTR(rb_inspect(value)));
return scope.Escape(String::NewFromUtf8(isolate, "Undefined Conversion"));
}
}
static VALUE rb_context_eval(VALUE self, VALUE str) {
@ -144,24 +187,28 @@ static VALUE rb_context_eval(VALUE self, VALUE str) {
Data_Get_Struct(self, ContextInfo, context_info);
Locker lock(context_info->isolate);
Isolate::Scope isolate_scope(context_info->isolate);
HandleScope handle_scope(context_info->isolate);
{
Locker lock(context_info->isolate);
Isolate::Scope isolate_scope(context_info->isolate);
HandleScope handle_scope(context_info->isolate);
Local<String> eval = String::NewFromUtf8(context_info->isolate, RSTRING_PTR(str),
NewStringType::kNormal, (int)RSTRING_LEN(str)).ToLocalChecked();
Local<String> eval = String::NewFromUtf8(context_info->isolate, RSTRING_PTR(str),
NewStringType::kNormal, (int)RSTRING_LEN(str)).ToLocalChecked();
eval_params.context_info = context_info;
eval_params.eval = &eval;
eval_params.result = &eval_result;
eval_params.timeout = 0;
VALUE timeout = rb_iv_get(self, "@timeout");
if (timeout != Qnil) {
eval_params.timeout = (useconds_t)NUM2LONG(timeout);
eval_params.context_info = context_info;
eval_params.eval = &eval;
eval_params.result = &eval_result;
eval_params.timeout = 0;
VALUE timeout = rb_iv_get(self, "@timeout");
if (timeout != Qnil) {
eval_params.timeout = (useconds_t)NUM2LONG(timeout);
}
rb_thread_call_without_gvl(nogvl_context_eval, &eval_params, RUBY_UBF_IO, 0);
}
rb_thread_call_without_gvl(nogvl_context_eval, &eval_params, RUBY_UBF_IO, 0);
// NOTE: this is very important, we can not do an rb_raise from within
// a v8 scope, if we do the scope is never cleaned up properly and we leak
if (!eval_result.parsed) {
// exception report about what happened
rb_raise(rb_eStandardError, "Error Parsing JS");
@ -172,11 +219,18 @@ static VALUE rb_context_eval(VALUE self, VALUE str) {
rb_raise(rb_eStandardError, "JavaScript Error");
}
Local<Value> tmp = Local<Value>::New(context_info->isolate, *eval_result.value);
result = convert_v8_to_ruby(tmp);
// New scope for return value
{
Locker lock(context_info->isolate);
Isolate::Scope isolate_scope(context_info->isolate);
HandleScope handle_scope(context_info->isolate);
eval_result.value->Reset();
delete eval_result.value;
Local<Value> tmp = Local<Value>::New(context_info->isolate, *eval_result.value);
result = convert_v8_to_ruby(tmp);
eval_result.value->Reset();
delete eval_result.value;
}
return result;
}
@ -185,33 +239,37 @@ void*
gvl_ruby_callback(void* data) {
FunctionCallbackInfo<Value>* args = (FunctionCallbackInfo<Value>*)data;
HandleScope scope(args->GetIsolate());
Handle<External> external = Handle<External>::Cast(args->Data());
VALUE* self_pointer = (VALUE*)(external->Value());
VALUE callback = rb_iv_get(*self_pointer, "@callback");
int length = args->Length();
VALUE* ruby_args;
if (length > 0) {
ruby_args = ALLOC_N(VALUE, length);
int length = args->Length();
VALUE callback;
{
HandleScope scope(args->GetIsolate());
Handle<External> external = Handle<External>::Cast(args->Data());
VALUE* self_pointer = (VALUE*)(external->Value());
callback = rb_iv_get(*self_pointer, "@callback");
if (length > 0) {
ruby_args = ALLOC_N(VALUE, length);
}
for (int i = 0; i < length; i++) {
Local<Value> value = ((*args)[i]).As<Value>();
ruby_args[i] = convert_v8_to_ruby(value);
}
}
// may raise exception stay clear of handle scope
VALUE result = rb_funcall2(callback, rb_intern("call"), length, ruby_args);
for (int i = 0; i < length; i++) {
Local<Value> value = ((*args)[i]).As<Value>();
ruby_args[i] = convert_v8_to_ruby(value);
{
HandleScope scope(args->GetIsolate());
Handle<Value> v8_result = convert_ruby_to_v8(args->GetIsolate(), result);
args->GetReturnValue().Set(v8_result);
}
return NULL;
VALUE result = rb_funcall(callback, rb_intern("call"), length, ruby_args);
Handle<Value> v8_result = convert_ruby_to_v8(args->GetIsolate(), result);
args->GetReturnValue().Set(v8_result);
if (length > 0) {
xfree(ruby_args);
}
@ -220,7 +278,7 @@ gvl_ruby_callback(void* data) {
}
static void ruby_callback(const FunctionCallbackInfo<Value>& args) {
rb_thread_call_with_gvl(gvl_ruby_callback, (void*)(&args));
rb_thread_call_with_gvl(gvl_ruby_callback, (void*)(&args));
}
@ -237,17 +295,20 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
Isolate::Scope isolate_scope(context_info->isolate);
HandleScope handle_scope(context_info->isolate);
Local<Context> context = context_info->context->Get(context_info->isolate);
Context::Scope context_scope(context);
Local<String> v8_str = String::NewFromUtf8(context_info->isolate, RSTRING_PTR(name),
NewStringType::kNormal, (int)RSTRING_LEN(name)).ToLocalChecked();
// copy self so we can access from v8 external
VALUE* self_copy;
Data_Get_Struct(self, VALUE, self_copy);
*self_copy = self;
Local<ObjectTemplate> globals = context_info->globals->Get(context_info->isolate);
Local<Value> external = External::New(context_info->isolate, self_copy);
globals->Set(v8_str, FunctionTemplate::New(context_info->isolate, ruby_callback, external));
context->Global()->Set(v8_str, FunctionTemplate::New(context_info->isolate, ruby_callback, external)->GetFunction());
return Qnil;
}
@ -256,15 +317,6 @@ void deallocate(void * data) {
ContextInfo* context_info = (ContextInfo*)data;
{
Locker lock(context_info->isolate);
Isolate::Scope isolate_scope(context_info->isolate);
HandleScope handle_scope(context_info->isolate);
Local<Context> context = context_info->context->Get(context_info->isolate);
Local<String> source = String::NewFromUtf8(context_info->isolate, "for(;;);");
MaybeLocal<Script> script = Script::Compile(context, source);
//V8::TerminateExecution(context_info->isolate);
//script.ToLocalChecked()->Run();
}
{
@ -273,7 +325,7 @@ void deallocate(void * data) {
}
{
//context_info->isolate->Dispose();
context_info->isolate->Dispose();
}
delete context_info->allocator;
@ -305,11 +357,7 @@ VALUE allocate(VALUE klass) {
Isolate::Scope isolate_scope(context_info->isolate);
HandleScope handle_scope(context_info->isolate);
Local<ObjectTemplate> globals = ObjectTemplate::New(context_info->isolate);
context_info->globals = new Persistent<ObjectTemplate>();
context_info->globals->Reset(context_info->isolate, globals);
Local<Context> context = Context::New(context_info->isolate, NULL, globals);
Local<Context> context = Context::New(context_info->isolate);
context_info->context = new Persistent<Context>();
context_info->context->Reset(context_info->isolate, context);

View File

@ -1,6 +1,7 @@
require 'test_helper'
class MiniRacerTest < Minitest::Test
def test_that_it_has_a_version_number
refute_nil ::MiniRacer::VERSION
end
@ -20,6 +21,8 @@ class MiniRacerTest < Minitest::Test
assert_raises do
context.eval('var x=function(){boom;}; x()')
end
# context should not be dead
assert_equal 2, context.eval('1+1')
end
def test_it_can_stop