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:
parent
f7ec907547
commit
2f484fe1b9
6 changed files with 99 additions and 25 deletions
|
@ -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
|
||||
|
||||
|
|
19
README.md
19
README.md
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
module MiniRacer
|
||||
VERSION = "0.1.9"
|
||||
VERSION = "0.1.10"
|
||||
end
|
||||
|
|
|
@ -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"
|
||||
#
|
||||
|
|
Loading…
Add table
Reference in a new issue