Skip to content

Instantly share code, notes, and snippets.

@bnoordhuis
Created November 21, 2012 14:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bnoordhuis/4125212 to your computer and use it in GitHub Desktop.
Save bnoordhuis/4125212 to your computer and use it in GitHub Desktop.
V8 context GC cleanup failure
#include "v8.h"
#include <assert.h>
#include <unistd.h>
using namespace v8;
struct WrappedContext
{
WrappedContext(Handle<Object> obj)
{
HandleScope scope;
obj->SetPointerInInternalField(0, this);
context_ = Context::New();
wrapper_ = Persistent<Object>::New(obj);
wrapper_.MakeWeak(this, WeakCallback);
}
~WrappedContext()
{
context_.Dispose();
wrapper_.Dispose();
}
static void Init(Handle<Object> g)
{
HandleScope scope;
Local<FunctionTemplate> tpl = FunctionTemplate::New(WrappedContext::New);
tpl->SetClassName(String::New("Context"));
tpl->InstanceTemplate()->SetInternalFieldCount(1);
tpl->PrototypeTemplate()->Set(String::New("eval"),
FunctionTemplate::New(Eval)->GetFunction());
g->Set(String::New("Context"), tpl->GetFunction());
constructor_template = Persistent<FunctionTemplate>::New(tpl);
}
static Handle<Value> New(const Arguments& args)
{
HandleScope scope;
new WrappedContext(args.This());
return args.This();
}
static Handle<Value> Eval(const Arguments& args)
{
HandleScope scope;
WrappedContext* self = static_cast<WrappedContext*>(
args.Holder()->GetPointerFromInternalField(0));
Local<Script> script = Script::Compile(args[0].As<String>());
Context::Scope context_scope(self->context_);
Copy(args.This(), self->wrapper_, self->context_->Global()->GetPrototype());
Local<Value> rc = script->Run();
Copy(args.This(), self->context_->Global()->GetPrototype(), self->wrapper_);
assert(!rc.IsEmpty());
return scope.Close(rc);
}
static void Copy(Handle<Object> target,
Handle<Value> source,
Handle<Value> context)
{
HandleScope scope;
Handle<Value> argv[] = { source, context };
Local<Function> fn = target->Get(String::New("copy")).As<Function>();
TryCatch try_catch;
fn->Call(target, 2, argv);
assert(!try_catch.HasCaught());
}
static void WeakCallback(Persistent<Value>, void* arg)
{
delete static_cast<WrappedContext*>(arg);
}
static Persistent<FunctionTemplate> constructor_template;
Persistent<Context> context_;
Persistent<Object> wrapper_;
};
Persistent<FunctionTemplate> WrappedContext::constructor_template;
int main(int argc, char** argv)
{
V8::SetFlagsFromCommandLine(&argc, argv, true);
HandleScope scope;
Persistent<Context> context = Context::New();
Context::Scope context_scope(context);
WrappedContext::Init(context->Global());
Local<Script> script = Script::New(String::New(
" Context.prototype.copy = function(a, b) { \n"
" Object.getOwnPropertyNames(a).forEach(function(k) { \n"
" var d = Object.getOwnPropertyDescriptor(a, k); \n"
" if (d.value === a) d.value = b; \n"
" Object.defineProperty(b, k, d); \n"
" }); \n"
" }; \n"
" function f() { \n"
" var ctx = new Context; \n"
" var obj = { ctx: ctx }; \n"
" ctx.obj = ctx; \n"
" ctx.eval(''); \n"
" } \n"
" for (var i = 0; i < 1e5; i++) f(); \n"
));
assert(!script.IsEmpty());
Local<Value> rc = script->Run();
assert(!rc.IsEmpty());
}
@bnoordhuis
Copy link
Author

$ g++ -Wall -Wextra -Iinclude context-1.cc out/native/obj.target/tools/gyp/libv8_{base,nosnapshot}.a -lm -pthread
$ ./a.out --trace-gc --max_old_space_size=300

max_old_space_size is optional but otherwise it takes ages before you hit the OOM error.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment