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

FEATURE: filename support for #eval

also fixes heap_stats so it returns all 0s when context is disposed
This commit is contained in:
Sam 2017-07-17 11:05:33 -04:00
parent f7ec907547
commit 2f484fe1b9
6 changed files with 99 additions and 25 deletions

View file

@ -2,9 +2,10 @@
- 0.1.10
- Fix leak: minor memory leak when disposing a context (20 bytes per context)
- Fix leak: memory leak when disposing a context (20 bytes per context)
- Feature: added #heap_stats so you can get visibility from context to actual memory usage of isolate
- Feature: added #dispose so you reclaim all v8 memory right away as opposed to waiting for GC
- Feature: you can now specify filename in an eval eg: eval('a = 1', filename: 'my_awesome.js')
09-03-2017

View file

@ -68,6 +68,19 @@ context.eval 'while(true){}'
# => exception is raised
```
### Rich debugging with "filename" support
```ruby
context = MiniRacer::Context.new
context.eval('var foo = function() {bar();}', filename: 'a/foo.js')
context.eval('bar()', filename: 'a/bar.js')
# MiniRacer::RuntimeError is raised containing the filenames you specified for evals in backtrace
```
### Threadsafe
Context usage is threadsafe
@ -226,6 +239,12 @@ A list of all V8 runtime flags can be found using `node --v8-options`, or else b
Note that runtime flags must be set before any other operation (e.g. creating a context, a snapshot or an isolate), otherwise an exception will be thrown.
Flags:
- :expose_gc : Will expose `gc()` which you can run in JavaScript to issue a gc
- :max_old_space_size : defaults to 1400 (megs) on 64 bit, you can restric memory usage by limiting this.
- **NOTE TO READER** our documentation could be awesome we could be properly documenting all the flags, they are hugely useful, if you feel like documenting a few more, PLEASE DO, PRs are welcome.
## Controlling memory
When hosting v8 you may want to keep track of memory usage, use #heap_stats to get memory usage:

View file

@ -64,6 +64,7 @@ typedef struct {
typedef struct {
ContextInfo* context_info;
Local<String>* eval;
Local<String>* filename;
useconds_t timeout;
EvalResult* result;
} EvalParams;
@ -131,13 +132,25 @@ nogvl_context_eval(void* arg) {
TryCatch trycatch(isolate);
Local<Context> context = eval_params->context_info->context->Get(isolate);
Context::Scope context_scope(context);
v8::ScriptOrigin *origin = NULL;
// in gvl flag
isolate->SetData(0, (void*)false);
// terminate ASAP
isolate->SetData(1, (void*)false);
MaybeLocal<Script> parsed_script = Script::Compile(context, *eval_params->eval);
MaybeLocal<Script> parsed_script;
if (eval_params->filename) {
origin = new v8::ScriptOrigin(*eval_params->filename);
}
parsed_script = Script::Compile(context, *eval_params->eval, origin);
if (origin) {
delete origin;
}
result->parsed = !parsed_script.IsEmpty();
result->executed = false;
result->terminated = false;
@ -507,7 +520,7 @@ static VALUE rb_context_init_with_isolate(VALUE self, VALUE isolate) {
return Qnil;
}
static VALUE rb_context_eval_unsafe(VALUE self, VALUE str) {
static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
EvalParams eval_params;
EvalResult eval_result;
@ -526,7 +539,17 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str) {
HandleScope handle_scope(isolate);
Local<String> eval = String::NewFromUtf8(isolate, RSTRING_PTR(str),
NewStringType::kNormal, (int)RSTRING_LEN(str)).ToLocalChecked();
NewStringType::kNormal, (int)RSTRING_LEN(str)).ToLocalChecked();
Local<String> local_filename;
if (filename != Qnil) {
local_filename = String::NewFromUtf8(isolate, RSTRING_PTR(filename),
NewStringType::kNormal, (int)RSTRING_LEN(filename)).ToLocalChecked();
eval_params.filename = &local_filename;
} else {
eval_params.filename = NULL;
}
eval_params.context_info = context_info;
eval_params.eval = &eval;
@ -937,28 +960,30 @@ rb_heap_stats(VALUE self) {
ContextInfo* context_info;
Data_Get_Struct(self, ContextInfo, context_info);
if (!context_info->isolate_info) {
return Qnil;
}
Isolate* isolate = context_info->isolate_info->isolate;
if (!isolate) {
return Qnil;
}
Isolate* isolate;
v8::HeapStatistics stats;
isolate->GetHeapStatistics(&stats);
isolate = context_info->isolate_info ? context_info->isolate_info->isolate : NULL;
VALUE rval = rb_hash_new();
rb_hash_aset(rval, ID2SYM(rb_intern("total_physical_size")), ULONG2NUM(stats.total_physical_size()));
rb_hash_aset(rval, ID2SYM(rb_intern("total_heap_size_executable")), ULONG2NUM(stats.total_heap_size_executable()));
rb_hash_aset(rval, ID2SYM(rb_intern("total_heap_size")), ULONG2NUM(stats.total_heap_size()));
rb_hash_aset(rval, ID2SYM(rb_intern("used_heap_size")), ULONG2NUM(stats.used_heap_size()));
rb_hash_aset(rval, ID2SYM(rb_intern("heap_size_limit")), ULONG2NUM(stats.heap_size_limit()));
if (!isolate) {
rb_hash_aset(rval, ID2SYM(rb_intern("total_physical_size")), ULONG2NUM(0));
rb_hash_aset(rval, ID2SYM(rb_intern("total_heap_size_executable")), ULONG2NUM(0));
rb_hash_aset(rval, ID2SYM(rb_intern("total_heap_size")), ULONG2NUM(0));
rb_hash_aset(rval, ID2SYM(rb_intern("used_heap_size")), ULONG2NUM(0));
rb_hash_aset(rval, ID2SYM(rb_intern("heap_size_limit")), ULONG2NUM(0));
} else {
isolate->GetHeapStatistics(&stats);
rb_hash_aset(rval, ID2SYM(rb_intern("total_physical_size")), ULONG2NUM(stats.total_physical_size()));
rb_hash_aset(rval, ID2SYM(rb_intern("total_heap_size_executable")), ULONG2NUM(stats.total_heap_size_executable()));
rb_hash_aset(rval, ID2SYM(rb_intern("total_heap_size")), ULONG2NUM(stats.total_heap_size()));
rb_hash_aset(rval, ID2SYM(rb_intern("used_heap_size")), ULONG2NUM(stats.used_heap_size()));
rb_hash_aset(rval, ID2SYM(rb_intern("heap_size_limit")), ULONG2NUM(stats.heap_size_limit()));
}
return rval;
}
@ -1029,7 +1054,7 @@ extern "C" {
rb_define_alloc_func(rb_cSnapshot, allocate_snapshot);
rb_define_alloc_func(rb_cIsolate, allocate_isolate);
rb_define_private_method(rb_cContext, "eval_unsafe",(VALUE(*)(...))&rb_context_eval_unsafe, 1);
rb_define_private_method(rb_cContext, "eval_unsafe",(VALUE(*)(...))&rb_context_eval_unsafe, 2);
rb_define_private_method(rb_cContext, "init_with_isolate",(VALUE(*)(...))&rb_context_init_with_isolate, 1);
rb_define_private_method(rb_cExternalFunction, "notify_v8", (VALUE(*)(...))&rb_external_function_notify_v8, 0);
rb_define_alloc_func(rb_cExternalFunction, allocate_external_function);

View file

@ -164,14 +164,16 @@ module MiniRacer
eval(File.read(filename))
end
def eval(str)
def eval(str, options=nil)
raise(ContextDisposedError, 'attempted to call eval on a disposed context!') if @disposed
filename = options && options[:filename].to_s
@eval_thread = Thread.current
isolate.with_lock do
@current_exception = nil
timeout do
eval_unsafe(str)
eval_unsafe(str, filename)
end
end
ensure

View file

@ -1,3 +1,3 @@
module MiniRacer
VERSION = "0.1.9"
VERSION = "0.1.10"
end

View file

@ -637,6 +637,33 @@ raise FooError, "I like foos"
assert(stats.values.all?{|v| v > 0}, "expecting the isolate to have values for all the vals")
end
def test_eval_with_filename
context = MiniRacer::Context.new()
context.eval("var foo = function(){baz();}", filename: 'b/c/foo1.js')
got_error = false
begin
context.eval("foo()", filename: 'baz1.js')
rescue MiniRacer::RuntimeError => e
assert_match(/foo1.js/, e.backtrace[0])
assert_match(/baz1.js/, e.backtrace[1])
got_error = true
end
assert(got_error, "should raise")
end
def test_estimated_size_when_disposed
context = MiniRacer::Context.new(timeout: 5)
context.eval("let a='testing';")
context.dispose
stats = context.heap_stats
assert(stats.values.all?{|v| v==0}, "should have 0 values once disposed")
end
def test_can_dispose
skip "takes too long"
#