Skip to content

Commit

Permalink
Added erlang_v8:reset_vm/1.
Browse files Browse the repository at this point in the history
  • Loading branch information
Gustaf Sjöberg committed Mar 23, 2014
1 parent be28f8b commit dbcc004
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 18 deletions.
23 changes: 15 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -27,7 +27,7 @@ Or with rebar:

You can run a few tests to verify basic functionality:

make test
make tests

## Usage

Expand All @@ -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.
34 changes: 28 additions & 6 deletions c_src/erlang_v8.cc
Original file line number Diff line number Diff line change
Expand Up @@ -134,26 +134,48 @@ void eval(Isolate* isolate, string input) {
}
}

int main(int argc, char* argv[]) {
ios_base::sync_with_stdio(false);
Handle<ObjectTemplate> create_global(Isolate* isolate) {
Handle<ObjectTemplate> global = ObjectTemplate::New(isolate);
global->Set(String::NewFromUtf8(isolate, "erlang_v8"),
String::NewFromUtf8(isolate, "yes"));
return global;
}

Handle<Context> create_context(Isolate* isolate, Handle<ObjectTemplate> global) {
return Context::New(isolate, NULL, create_global(isolate));
}

bool command_loop() {
Isolate* isolate = Isolate::GetCurrent();

HandleScope handle_scope(isolate);
Handle<Context> context = Context::New(isolate);

Handle<ObjectTemplate> global = create_global(isolate);
Handle<Context> 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;
}
9 changes: 9 additions & 0 deletions src/erlang_v8.erl
Original file line number Diff line number Diff line change
Expand Up @@ -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]).
Expand All @@ -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).

Expand Down
15 changes: 13 additions & 2 deletions src/erlang_v8_vm.erl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
-export([start_link/0]).
-export([stop/1]).
-export([reset/1]).
-export([restart/1]).

-export([eval/2]).
-export([eval/3]).
Expand All @@ -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
Expand Down Expand Up @@ -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).

Expand Down Expand Up @@ -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, <<?OP_RESET:8>>}},
{reply, ok, State};

handle_call(restart, _From, State) ->
{reply, ok, start_port(close_port(State))};

handle_call(stop, _From, State) ->
Expand Down Expand Up @@ -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, <<?OP_EVAL:8, Source/binary>>}},
receive
{Port, {data, <<_:8, "undefined">>}} ->
{ok, undefined};
Expand Down
45 changes: 43 additions & 2 deletions test/port_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
-export([errors/1]).
-export([timeout/1]).
-export([reset/1]).
-export([restart/1]).

%% Callbacks

Expand All @@ -24,7 +25,8 @@ all() ->
nested_return_type,
errors,
timeout,
reset
reset,
restart
].

init_per_suite(Config) ->
Expand Down Expand Up @@ -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]),
Expand Down

0 comments on commit dbcc004

Please sign in to comment.