diff --git a/README.md b/README.md index c12ccc0..b6fb724 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,11 @@ proper attention if the experiment works out. The most notable feature is that you can eval things like "while (true) {}" and have the v8 VM actually terminate when it times out. I'm also planning -two-way communication, resetting context etc. +two-way communication, loading scripts etc. ## Building -Git, Subversion, and Python 2.6-2.7 (for GYP) are required to build v8. +Subversion, and Python 2.6-2.7 (by GYP) are required to build v8. Build using make: @@ -27,7 +27,7 @@ Or with rebar: You can run a few tests to verify basic functionality: - make test + make tests ## Usage @@ -37,19 +37,26 @@ Start a VM: Define a function: - {ok, undefined} = erlang_v8:eval(VM, <<"function sum(a, b) { return a + b }">>). + {ok, undefined} = + erlang_v8:eval(VM, <<"function sum(a, b) { return a + b }">>). Run the function: {ok, 2} = erlang_v8:call(VM, <<"sum">>, [1, 1]). +You can reset the VM: + + ok = erlang_v8:reset_vm(VM). + {error, <<"ReferenceError: sum is not defined">>} = + erlang_v8:call(VM, <<"sum">>, [1, 1]). + Stop the VM: ok = erlang_v8:stop_vm(VM). ## TODO -Lots of testing, improve the communication protocol, clean up api, add -features like reset context and stuff, experiment with calling Erlang from JS, -load initial context from args, supervisor strategy, experiment with different -ways of passing args to calls (maybe via the communication protocol) etc. +Lots of testing, improve the communication protocol, clean up api, experiment +with calling Erlang from JS, load initial context from args, supervisor +strategy, experiment with different ways of passing args to calls (maybe via +the communication protocol) etc. diff --git a/c_src/erlang_v8.cc b/c_src/erlang_v8.cc index e8282fc..a5c5244 100644 --- a/c_src/erlang_v8.cc +++ b/c_src/erlang_v8.cc @@ -134,26 +134,48 @@ void eval(Isolate* isolate, string input) { } } -int main(int argc, char* argv[]) { - ios_base::sync_with_stdio(false); +Handle create_global(Isolate* isolate) { + Handle global = ObjectTemplate::New(isolate); + global->Set(String::NewFromUtf8(isolate, "erlang_v8"), + String::NewFromUtf8(isolate, "yes")); + return global; +} +Handle create_context(Isolate* isolate, Handle global) { + return Context::New(isolate, NULL, create_global(isolate)); +} + +bool command_loop() { Isolate* isolate = Isolate::GetCurrent(); + HandleScope handle_scope(isolate); - Handle context = Context::New(isolate); + + Handle global = create_global(isolate); + Handle context = create_context(isolate, global); + Context::Scope context_scope(context); + bool reset = false; Packet packet; - while (next_packet(&packet)) { + while (!reset && next_packet(&packet)) { switch(packet.op) { case EVAL_R: eval(isolate, packet.data); break; + case RESET_VM_R: + reset = true; + break; } - packet = (const struct Packet){ 0 }; } + V8::ContextDisposedNotification(); + return reset; +} + +int main(int argc, char* argv[]) { + ios_base::sync_with_stdio(false); - V8::TerminateExecution(isolate); + while (command_loop()); return 0; } diff --git a/src/erlang_v8.erl b/src/erlang_v8.erl index 058a43d..ae1672b 100644 --- a/src/erlang_v8.erl +++ b/src/erlang_v8.erl @@ -3,6 +3,9 @@ -export([start_vm/0]). -export([stop_vm/1]). +-export([reset_vm/1]). +-export([restart_vm/1]). + -export([eval/2]). -export([eval/3]). -export([call/3]). @@ -14,6 +17,12 @@ start_vm() -> stop_vm(Pid) -> erlang_v8_vm:stop(Pid). +reset_vm(Pid) -> + erlang_v8_vm:reset(Pid). + +restart_vm(Pid) -> + erlang_v8_vm:restart(Pid). + eval(Pid, Source) -> erlang_v8_vm:eval(Pid, Source). diff --git a/src/erlang_v8_vm.erl b/src/erlang_v8_vm.erl index a76cb2b..9c9d87d 100644 --- a/src/erlang_v8_vm.erl +++ b/src/erlang_v8_vm.erl @@ -6,6 +6,7 @@ -export([start_link/0]). -export([stop/1]). -export([reset/1]). +-export([restart/1]). -export([eval/2]). -export([eval/3]). @@ -23,6 +24,9 @@ -define(SPAWN_OPTS, [{packet, 2}, binary]). -define(DEFAULT_TIMEOUT, 5000). +-define(OP_EVAL, 0). +-define(OP_RESET, 2). + -record(state, { port, monitor_pid @@ -51,6 +55,9 @@ call(Pid, FunctionName, Args, Timeout) -> reset(Pid) -> gen_server:call(Pid, reset). +restart(Pid) -> + gen_server:call(Pid, restart). + stop(Pid) -> gen_server:call(Pid, stop). @@ -78,7 +85,11 @@ handle_call({eval, Source, Timeout}, _From, #state{port = Port} = State) -> {reply, {error, Reason}, State} end; -handle_call(reset, _From, State) -> +handle_call(reset, _From, #state{port = Port} = State) -> + Port ! {self(), {command, <>}}, + {reply, ok, State}; + +handle_call(restart, _From, State) -> {reply, ok, start_port(close_port(State))}; handle_call(stop, _From, State) -> @@ -149,7 +160,7 @@ os_kill(OSPid) -> %% @doc Evaluate source on port eval_js(Port, Source, Timeout) -> - Port ! {self(), {command, <<0:8, Source/binary>>}}, + Port ! {self(), {command, <>}}, receive {Port, {data, <<_:8, "undefined">>}} -> {ok, undefined}; diff --git a/test/port_SUITE.erl b/test/port_SUITE.erl index 60f0ac0..23de8fb 100644 --- a/test/port_SUITE.erl +++ b/test/port_SUITE.erl @@ -13,6 +13,7 @@ -export([errors/1]). -export([timeout/1]). -export([reset/1]). +-export([restart/1]). %% Callbacks @@ -24,7 +25,8 @@ all() -> nested_return_type, errors, timeout, - reset + reset, + restart ]. init_per_suite(Config) -> @@ -122,11 +124,50 @@ timeout(_Config) -> reset(_Config) -> {ok, P} = erlang_v8:start_vm(), + {ok, <<"yes">>} = erlang_v8:eval(P, <<"erlang_v8">>), + erlang_v8:reset_vm(P), + {ok, <<"yes">>} = erlang_v8:eval(P, <<"erlang_v8">>), + + {ok, <<"no">>} = erlang_v8:eval(P, <<"erlang_v8 = 'no';">>), + erlang_v8:reset_vm(P), + {ok, <<"yes">>} = erlang_v8:eval(P, <<"erlang_v8">>), + + {ok, <<"test">>} = erlang_v8:eval(P, <<"String.imposter = 'test';">>), + erlang_v8:reset_vm(P), + {ok, undefined} = erlang_v8:eval(P, <<"String.imposter">>), + + {ok, undefined} = + erlang_v8:eval(P, <<"function sum(a, b) { return a + b }">>), + {ok, 2} = erlang_v8:call(P, <<"sum">>, [1, 1]), + + erlang_v8:reset_vm(P), + + {error, <<"ReferenceError: sum", _/binary>>} = + erlang_v8:call(P, <<"sum">>, [1, 1]), + + erlang_v8:stop_vm(P), + ok. + +restart(_Config) -> + {ok, P} = erlang_v8:start_vm(), + + {ok, <<"yes">>} = erlang_v8:eval(P, <<"erlang_v8">>), + erlang_v8:restart_vm(P), + {ok, <<"yes">>} = erlang_v8:eval(P, <<"erlang_v8">>), + + {ok, <<"no">>} = erlang_v8:eval(P, <<"erlang_v8 = 'no';">>), + erlang_v8:restart_vm(P), + {ok, <<"yes">>} = erlang_v8:eval(P, <<"erlang_v8">>), + + {ok, <<"test">>} = erlang_v8:eval(P, <<"String.imposter = 'test';">>), + erlang_v8:restart_vm(P), + {ok, undefined} = erlang_v8:eval(P, <<"String.imposter">>), + {ok, undefined} = erlang_v8:eval(P, <<"function sum(a, b) { return a + b }">>), {ok, 2} = erlang_v8:call(P, <<"sum">>, [1, 1]), - erlang_v8_vm:reset(P), + erlang_v8:restart_vm(P), {error, <<"ReferenceError: sum", _/binary>>} = erlang_v8:call(P, <<"sum">>, [1, 1]),