PageRenderTime 40ms CodeModel.GetById 10ms app.highlight 25ms RepoModel.GetById 1ms app.codeStats 0ms

/src/erlv8_vm.erl

http://github.com/beamjs/erlv8
Erlang | 392 lines | 237 code | 72 blank | 83 comment | 2 complexity | 56ca291c3d955761535f1ba806ba0a98 MD5 | raw file
  1-module(erlv8_vm).
  2
  3-behaviour(gen_server2).
  4-include_lib("erlv8/include/erlv8.hrl").
  5
  6%% API
  7-export([start_link/1,start/0,vm_resource/1,
  8	 run/2, run/3, run/4, 
  9	 run_timed/3, run_timed/4, run_timed/5,
 10	 global/1,stop/1,
 11	 to_string/2,to_detail_string/2,taint/2,untaint/1,equals/3, strict_equals/3, 
 12	 enqueue_tick/2, enqueue_tick/3, enqueue_tick/4, next_tick/2, next_tick/3, next_tick/4,
 13	 stor/3, retr/2, gc/1, kill/1]).
 14
 15%% gen_server2 callbacks
 16-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
 17		 terminate/2, code_change/3,prioritise_info/2]).
 18
 19-define(SERVER, ?MODULE). 
 20
 21-record(state, {
 22	  vm,
 23	  ticked,
 24	  storage = [],
 25	  context,
 26	  debug
 27	 }).
 28
 29-define(Error(Msg), lists:flatten(io_lib:format("~s: ~p",[Msg,Trace]))).
 30-define(ErrorVal(Msg), lists:flatten(io_lib:format("~s: ~p ~p",[Msg,Val,Trace]))).
 31
 32
 33%%%===================================================================
 34%%% API
 35%%%===================================================================
 36start() ->
 37	VM = erlv8_nif:new_vm(),
 38	supervisor2:start_child(erlv8_sup,[VM]).
 39
 40vm_resource(Server) ->
 41	gen_server2:call(Server, vm_resource).
 42
 43run(Server, Source) ->
 44	run(Server, erlv8_context:get(Server), Source).
 45
 46run_timed(Server, Source, Timeout) ->
 47    run_timed(Server, erlv8_context:get(Server), Source, Timeout).
 48
 49run_timed(Server, {_, _CtxRes} = Context, Source, Timeout) ->
 50    run_timed(Server, Context, Source, {"unknown",0,0}, Timeout).
 51
 52run_timed(Server, {C, CtxRes}, Source, {Name, LineOffset, ColumnOffset}, Timeout) ->
 53    Pid = spawn_link(fun() ->
 54			     receive {'$gen_call', From, run} ->
 55				     Res = run(Server, {C , CtxRes}, Source, {Name, LineOffset, ColumnOffset}),
 56				     gen_server:reply(From, Res)
 57			     end
 58		     end),
 59    try gen_server:call(Pid, run, Timeout)
 60    catch
 61	exit:{timeout ,_} ->
 62	    erlv8_vm:kill(Server),
 63	    {error, timeout}
 64    end.
 65
 66
 67run(Server, {_, _CtxRes} = Context, Source) ->
 68	run(Server, Context, Source, {"unknown",0,0}).
 69
 70run(Server, A, Source, B) when is_binary(Source) ->
 71    run(Server, A, binary_to_list(Source), B);
 72run(Server, {_, CtxRes}, Source, {Name, LineOffset, ColumnOffset}) when is_list(Source) ->
 73	enqueue_tick(Server, {script, CtxRes, Source, Name, LineOffset, ColumnOffset}).
 74
 75
 76global(Server) ->
 77	Ctx = erlv8_context:get(Server),
 78	erlv8_context:global(Ctx).
 79
 80stop(Server) ->
 81	gen_server2:call(Server,stop).
 82
 83to_string(Server, Val) ->
 84	enqueue_tick(Server, {to_string, Val}).
 85
 86to_detail_string(Server, Val) ->
 87	enqueue_tick(Server, {to_detail_string, Val}).
 88
 89enqueue_tick(Server, Tick) ->
 90	gen_server2:call(Server,{enqueue_tick, Tick}, infinity).
 91
 92enqueue_tick(Server, Tick, Ref) when is_reference(Ref) ->
 93	gen_server2:call(Server,{enqueue_tick, Tick, Ref}, infinity);
 94
 95enqueue_tick(Server, Tick, Timeout) ->
 96	gen_server2:call(Server,{enqueue_tick, Tick}, Timeout).
 97
 98enqueue_tick(Server, Tick, Timeout, Ref) when is_reference(Ref) ->
 99	gen_server2:call(Server,{enqueue_tick, Tick, Ref}, Timeout).
100
101next_tick(Server, Tick) ->
102	gen_server2:call(Server,{next_tick, Tick}, infinity).
103
104next_tick(Server, Tick, Ref) when is_reference(Ref) ->
105	gen_server2:call(Server,{next_tick, Tick, Ref}, infinity);
106
107next_tick(Server, Tick, Timeout) ->
108	gen_server2:call(Server,{next_tick, Tick}, Timeout).
109
110next_tick(Server, Tick, Timeout, Ref) when is_reference(Ref) ->
111	gen_server2:call(Server,{next_tick, Tick, Ref}, Timeout).
112
113taint(Server, Value) when ?is_v8(Value) ->
114    enqueue_tick(Server, {taint, Value});
115
116taint(Server, {Error, _} = Value) when Error == error;
117                                       Error == throw ->
118    enqueue_tick(Server, {taint, Value});
119
120taint(Server, Value) when is_list(Value);
121                          is_binary(Value);
122                          is_atom(Value);
123                          is_number(Value);
124                          is_reference(Value);
125                          is_function(Value);
126                          is_pid(Value) ->
127    enqueue_tick(Server, {taint, Value});
128
129taint(_Server, _) ->
130    undefined.
131
132
133equals(Server, V1, V2) ->
134    enqueue_tick(Server, {equals, V1, V2}).
135
136strict_equals(Server, V1, V2) ->
137    enqueue_tick(Server, {strict_equals, V1, V2}).
138
139
140stor(Server, Key, Value) ->
141	gen_server2:call(Server, {stor, Key, Value}).
142
143retr(Server, Key) ->
144	gen_server2:call(Server, {retr, Key}).
145
146
147untaint({erlv8_object, _,_}=O) ->
148	{erlv8_object,lists:map(fun ({Key, Val}) ->
149					{Key, untaint(Val)}
150				end,O:proplist()), undefined};
151untaint({erlv8_array, _,_}=O) ->
152	{erlv8_array,lists:map(fun untaint/1,O:list()), undefined};
153untaint({erlv8_fun, _,_}=F) -> %% broken
154	{erlv8_object,untaint(F:object()),undefined};
155untaint([H|T]) ->
156	[untaint(H)|untaint(T)];
157untaint([]) ->
158	[];
159untaint(Other) ->
160	Other.
161
162gc(Server) ->
163	(catch enqueue_tick(Server, {gc}, 0)),
164	ok.
165
166kill(Server) ->
167    gen_server2:call(Server, kill),
168    erlv8_vm:run(Server, "1"), % hide returning {throw, null}.
169    ok.
170
171%%--------------------------------------------------------------------
172%% @doc
173%% Starts the server
174%%
175%% @spec start_link(VM) -> {ok, Pid} | ignore | {error, Error}
176%% @end
177%%--------------------------------------------------------------------
178start_link(VM) ->
179	gen_server2:start_link(?MODULE, [VM], []).
180
181%%%===================================================================
182%%% gen_server callbacks
183%%%===================================================================
184
185%%--------------------------------------------------------------------
186%% @private
187%% @doc
188%% Initializes the server
189%%
190%% @spec init(Args) -> {ok, State} |
191%%                     {ok, State, Timeout} |
192%%                     ignore |
193%%                     {stop, Reason}
194%% @end
195%%--------------------------------------------------------------------
196init([VM]) ->
197	process_flag(trap_exit, true),
198	erlv8_nif:set_server(VM, self()),
199	Ctx = erlv8_nif:context(VM),
200	{ok, #state{vm = VM, context = Ctx, debug = ets:new(erlv8_vm_debug,[]), ticked = ets:new(erlv8_vm_ticked,[public]) }}.
201
202%%--------------------------------------------------------------------
203%% @private
204%% @doc
205%% Handling call messages
206%%
207%% @spec handle_call(Request, From, State) ->
208%%                                   {reply, Reply, State} |
209%%                                   {reply, Reply, State, Timeout} |
210%%                                   {noreply, State} |
211%%                                   {noreply, State, Timeout} |
212%%                                   {stop, Reason, Reply, State} |
213%%                                   {stop, Reason, State}
214%% @end
215%%--------------------------------------------------------------------
216handle_call(vm_resource, _From, #state{ vm = VM } = State) ->
217	{reply, VM, State};
218
219handle_call({stor, Key, Value}, _From, #state{ storage = Storage } = State) ->
220	{reply, ok, State#state{ storage = [{Key, Value}|Storage] }};
221
222handle_call({retr, Key}, _From, #state{ storage = Storage } = State) ->
223	{reply, proplists:get_value(Key, Storage), State};
224
225handle_call(context, _From, #state{} = State) ->
226	{reply, {self(), State#state.context}, State};
227
228handle_call(new_context, _From, #state{ vm = VM } = State) ->
229	{reply, {self(), erlv8_nif:new_context(VM)}, State};
230
231handle_call({global, Resource}, _From, #state{vm = VM} = State) ->
232	{reply, erlv8_nif:global(VM, Resource), State};
233
234handle_call({to_string, Val}, _From, #state { vm = VM } = State) ->
235	Reply = erlv8_nif:to_string(VM, Val),
236	{reply, Reply, State};
237
238handle_call({to_detail_string, Val}, _From, #state { vm = VM } = State) ->
239	Reply = erlv8_nif:to_detail_string(VM, Val),
240	{reply, Reply, State};
241
242handle_call(kill, _From, #state { vm = VM } = State) ->
243    Reply = erlv8_nif:kill(VM),
244    {reply, Reply, State};
245
246handle_call(stop, _From, State) ->
247	{stop, normal, ok, State};
248
249handle_call({enqueue_tick, Tick}, From, State) ->
250	Ref = make_ref(),
251	handle_call({enqueue_tick, Tick, Ref}, From, State);
252
253handle_call({enqueue_tick, Tick, Ref}, From, #state{ vm = VM, ticked = Ticked } = State) ->
254	tack = erlv8_nif:tick(VM, Ref, Tick),
255	update_ticked(Ref, From, Tick, Ticked),
256	{noreply, State};
257
258handle_call({next_tick, Tick}, From, State) ->
259	Ref = make_ref(),
260	handle_call({next_tick, Tick, Ref}, From, State);
261
262handle_call({next_tick, Tick, Ref}, From, #state{ vm = VM, ticked = Ticked } = State) ->
263	tack = erlv8_nif:tick(VM, Ref, Tick),
264	update_ticked(Ref, From, Tick, Ticked),
265	{noreply, State};
266
267handle_call(_Request, _From, State) ->
268	{noreply, State}.
269
270%%--------------------------------------------------------------------
271%% @private
272%% @doc
273%% Handling cast messages
274%%
275%% @spec handle_cast(Msg, State) -> {noreply, State} |
276%%                                  {noreply, State, Timeout} |
277%%                                  {stop, Reason, State}
278%% @end
279%%--------------------------------------------------------------------
280handle_cast(run, #state{ vm = VM } = State) ->
281	erlv8_nif:run(VM, self()),
282	{noreply, State};
283
284handle_cast(_Msg, State) ->
285	{noreply, State}.
286
287%%--------------------------------------------------------------------
288%% @private
289%% @doc
290%% Handling all non call/cast messages
291%%
292%% @spec handle_info(Info, State) -> {noreply, State} |
293%%                                   {noreply, State, Timeout} |
294%%                                   {stop, Reason, State}
295%% @end
296%%--------------------------------------------------------------------
297%% Invocation
298handle_info({F,#erlv8_fun_invocation{ is_construct_call = ICC, this = This, ref = Ref } = Invocation,Args}, #state{ ticked = Ticked } = State) when is_function(F), is_list(Args) ->
299	Self = self(),
300	spawn(fun () ->
301				  Result = (catch erlang:apply(F,[Invocation,Args])),
302				  Result1 = 
303				  case Result of 
304					  {'EXIT',{Val, Trace}} when is_atom(Val) ->
305						  {throw, {error, ?Error(Val)}};
306					  {'EXIT',{{Tag, Val}, Trace}} ->
307						  {throw, {error, ?ErrorVal(Tag)}};
308					  _ ->
309						  case ICC of 
310							  true ->
311								  This;
312							  false ->
313								  Result
314						  end
315				  end,
316				  case ets:lookup(Ticked, Ref) of
317					  [{Ref, {From, {call, _, _, _}}}] ->
318						  gen_server2:reply(From, Result1),
319						  ets:delete(Ticked, Ref);
320					  [{Ref, {From, {call, _, _}}}] ->
321						  gen_server2:reply(From, Result1),
322						  ets:delete(Ticked, Ref);
323					  [{Ref, {From, {inst, _, _}}}] ->
324						  gen_server2:reply(From, Result1),
325						  ets:delete(Ticked, Ref);
326					  _ ->
327						  enqueue_tick(Self, {result, Ref, Result1})
328				  end
329		  end),
330	{noreply, State};
331handle_info({result, Ref, Result}, #state{ ticked = Ticked } = State) ->
332    
333	case ets:lookup(Ticked, Ref) of
334		[] ->
335			{noreply, State};
336		[{Ref, {From, _Tick}}] ->
337			gen_server2:reply(From, Result),
338			ets:delete(Ticked, Ref),
339			{noreply, State}
340	end;
341
342handle_info({'DEBUG',Name,Payload}, #state{ debug = Debug } = State) ->
343	ets:insert(Debug, {Name, Payload}),
344	{noreply, State};
345
346handle_info(timeout, State) ->
347    kill(self()),
348    {noreply, State};
349handle_info(_Info, State) ->
350    {noreply, State}.
351
352prioritise_info({retick, _}, _State) ->
353	1;
354prioritise_info(tick_me,_State) ->
355	0;
356prioritise_info(_,_State) ->
357	0.
358
359
360%%--------------------------------------------------------------------
361%% @private
362%% @doc
363%% This function is called by a gen_server when it is about to
364%% terminate. It should be the opposite of Module:init/1 and do any
365%% necessary cleaning up. When it returns, the gen_server terminates
366%% with Reason. The return value is ignored.
367%%
368%% @spec terminate(Reason, State) -> void()
369%% @end
370%%--------------------------------------------------------------------
371terminate(_Reason, #state{ vm = VM } = _State) ->
372	ok = erlv8_nif:stop(VM,make_ref()).
373
374%%--------------------------------------------------------------------
375%% @private
376%% @doc
377%% Convert process state when code is changed
378%%
379%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
380%% @end
381%%--------------------------------------------------------------------
382code_change(_OldVsn, State, _Extra) ->
383	{ok, State}.
384
385%%%===================================================================
386%%% Internal functions
387%%%===================================================================
388update_ticked(_Ref, From, {result, _, _}, Ticked) -> %% do not insert results, nobody is going to reply on them
389	gen_server2:reply(From, ok),
390	Ticked;
391update_ticked(Ref, From, Tick, Ticked) ->
392	ets:insert(Ticked, {Ref, {From, Tick}}).