├── Makefile ├── README.md ├── doc └── overview.edoc ├── ebin └── .gitignore └── src ├── Makefile ├── Makefile.old ├── beam.app.src ├── beam_app.erl ├── beam_emu.erl ├── beam_load.erl ├── message.S.old └── message.erl /Makefile: -------------------------------------------------------------------------------- 1 | #@BEGIN-DIR-DEFAULT-RULES@ 2 | all: 3 | @if [ -d "src" -a -f "src/Makefile" ]; then (cd src && $(MAKE) all); fi 4 | @if [ -d "c_src" -a -f "c_src/Makefile" ]; then (cd c_src && $(MAKE) all); fi 5 | @if [ -d "test" -a -f "test/Makefile" ]; then (cd test && $(MAKE) all); fi 6 | 7 | clean: 8 | @if [ -d "src" -a -f "src/Makefile" ]; then (cd src && $(MAKE) clean); fi 9 | @if [ -d "c_src" -a -f "c_src/Makefile" ]; then (cd c_src && $(MAKE) clean); fi 10 | @if [ -d "test" -a -f "test/Makefile" ]; then (cd test && $(MAKE) clean); fi 11 | #@END-DIR-DEFAULT-RULES@ 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Beam opcode documentation project 2 | ================================= 3 | 4 | the 'beam' project is intended to document and give an 5 | executable semantics, in Erlang, for all high level opcodes 6 | present in the beam files. 7 | -------------------------------------------------------------------------------- /doc/overview.edoc: -------------------------------------------------------------------------------- 1 | @author Tony Rogvall 2 | @version 1.0 3 | @title BEAM emulator written in Erlang. 4 | @doc This is code is made for the fun of it. But may be 5 | used to get an understanding for how BEAM works. 6 | 7 | 8 | -------------------------------------------------------------------------------- /ebin/.gitignore: -------------------------------------------------------------------------------- 1 | *.beam 2 | *.app 3 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | #@BEGIN-APP-DEFAULT-RULES@ 2 | APP=$(shell basename `cd ..; pwd`) 3 | ERLC="$(shell which erlc)" 4 | ERLC_FLAGS=-MMD -MP -MF .$<.d -I ../.. +debug_info 5 | YRL_SRC=$(wildcard *.yrl) 6 | XRL_SRC=$(wildcard *.xrl) 7 | ERL_SOURCES=$(wildcard *.erl) $(YRL_SRC:%.yrl=%.erl) $(XRL_SRC:%.xrl=%.erl) 8 | ERL_OBJECTS=$(ERL_SOURCES:%.erl=../ebin/%.beam) 9 | ALL_OBJECTS=$(ERL_OBJECTS) 10 | ERL_MODULES=$(ERL_SOURCES:%.erl=%) 11 | comma=, 12 | empty= 13 | space = $(empty) $(empty) 14 | MODULES=$(subst $(space),$(comma),$(ERL_MODULES)) 15 | VERSION=$(shell git describe --always --tags) 16 | APP_SRC=$(APP).app.src 17 | APP_TARGET=../ebin/$(APP).app 18 | 19 | .PRECIOUS: $(YRL_SRC:%.yrl=%.erl) $(XRL_SRC:%.xrl=%.erl) 20 | 21 | all: $(APP_TARGET) $(ALL_OBJECTS) 22 | 23 | clean: 24 | rm -f $(ALL_OBJECTS) *.core .*.d 25 | 26 | ../ebin/%.beam: %.erl 27 | $(ERLC) $(ERLC_FLAGS) -o ../ebin $< 28 | 29 | ../ebin/%.beam: %.S 30 | $(ERLC) -o ../ebin $< 31 | 32 | %.erl: %.yrl 33 | $(ERLC) $< 34 | 35 | %.erl: %.xrl 36 | $(ERLC) $< 37 | 38 | $(APP_TARGET): $(APP_SRC) 39 | sed -e 's;{vsn,.*git};{vsn,"$(VERSION)"};' -e 's;"@@MODULES@@";$(MODULES);' $(APP_SRC) > $(APP_TARGET) 40 | 41 | .%.d: ; 42 | 43 | -include .*.d 44 | #@END-APP-DEFAULT-RULES@ 45 | -------------------------------------------------------------------------------- /src/Makefile.old: -------------------------------------------------------------------------------- 1 | 2 | MODULES = \ 3 | beam_load \ 4 | beam_emu 5 | 6 | EBIN = ../ebin 7 | ERLC = erlc 8 | 9 | include ../vsn.mk 10 | VSN=$(BEAM_VSN) 11 | 12 | APP_NAME= beam 13 | APP_FILE= $(APP_NAME).app 14 | APP_SRC= $(APP_FILE).src 15 | APP_TARGET= $(EBIN)/$(APP_FILE) 16 | 17 | ERL_FLAGS = \ 18 | -I ../include -W0 19 | 20 | override ERLC_FLAGS += $(ERL_FLAGS) 21 | 22 | debug: ERLC_FLAGS += +debug_info -Ddebug -W 23 | 24 | OBJS = $(MODULES:%=../ebin/%.beam) ../ebin/message.beam 25 | 26 | TARGET_FILES = $(OBJS) $(APP_TARGET) 27 | 28 | all: $(TARGET_FILES) 29 | 30 | debug: all 31 | 32 | depend: 33 | edep -MM -o ../ebin $(ERLC_FLAGS) $(MODULES:%=%.erl) > depend.mk 34 | 35 | dialyze: 36 | dialyzer --src -o dia.out $(ERLC_FLAGS) -c $(MODULES:%=%.erl) 37 | 38 | edoc: 39 | erl -noshell -pa doc -run edoc_run application 'beam' '[{sort_functions,false},{todo,true},{index_columns,3}]' 40 | 41 | clean: 42 | rm -f $(OBJS) 43 | 44 | $(APP_TARGET): $(APP_SRC) ../vsn.mk 45 | sed -e 's;%VSN%;$(VSN);' $< > $@ 46 | 47 | -include depend.mk 48 | 49 | # does not work for SMP - message.erl is implmented using prim_eval 50 | ../ebin/message.beam: message.S 51 | erlc -o ../ebin message.S 52 | 53 | $(EBIN)/%.beam: %.erl 54 | $(ERLC) $(ERLC_FLAGS) -o $(EBIN) $< 55 | 56 | -------------------------------------------------------------------------------- /src/beam.app.src: -------------------------------------------------------------------------------- 1 | {application, beam, 2 | [{description, "BEAM emulator written in Erlang"}, 3 | {vsn, git}, 4 | {modules, ["@@MODULES@@"]}, 5 | {mod,{beam_app,[]}}, 6 | {env, []}, 7 | {applications,[kernel,stdlib]} 8 | ]}. -------------------------------------------------------------------------------- /src/beam_app.erl: -------------------------------------------------------------------------------- 1 | %%% File : beam_app.erl 2 | %%% Description : 3 | %%% Created : 26 Oct 2009 by Tony Rogvall 4 | 5 | %%% @hidden 6 | %%% @author Tony Rogvall 7 | 8 | -module(beam_app). 9 | 10 | -behaviour(application). 11 | -export([start/2,stop/1]). 12 | 13 | %% start 14 | start(_Type, _StartArgs) -> 15 | ok. 16 | 17 | %% stop FIXME 18 | stop(_State) -> 19 | ok. 20 | -------------------------------------------------------------------------------- /src/beam_emu.erl: -------------------------------------------------------------------------------- 1 | %%% File : beam_emu.erl 2 | %%% Author : Tony Rogvall 3 | %%% Description : BEAM interpreter 4 | %%% Created : 10 Jan 2006 by Tony Rogvall 5 | 6 | -module(beam_emu). 7 | 8 | %% 9 | %% Simple API to call a emulated function 10 | %% 11 | -export([run/3]). 12 | 13 | %% Internal API to discover the meta level ;-) 14 | -export([level/0, level0/0]). 15 | 16 | %% 17 | %% Instruction set. The first argument is the Beam Instruction State 18 | %% then the instruction arguments (so -1 on arity to the orgininal) 19 | %% 20 | -export([label/2]). %% 1 21 | -export([func_info/4]). %% 2 22 | -export([int_code_end/1]). %% 3 23 | -export([call/3]). %% 4 24 | -export([call_last/4]). %% 5 25 | -export([call_only/3]). %% 6 26 | -export([call_ext/3]). %% 7 27 | -export([call_ext_last/4]). %% 8 28 | -export([bif0/4]). %% 9 29 | -export([bif1/5]). %% 10 30 | -export([bif2/6]). %% 11 31 | -export([allocate/3]). %% 12 32 | -export([allocate_heap/4]). %% 13 33 | -export([allocate_zero/3]). %% 14 34 | -export([allocate_heap_zero/4]). %% 15 35 | -export([test_heap/3]). %% 16 36 | -export([init/2]). %% 17 37 | -export([deallocate/2]). %% 18 38 | -export([return/1]). %% 19 39 | -export([send/1]). %% 20 40 | -export([remove_message/1]). %% 21 41 | -export([timeout/1]). %% 22 42 | -export([loop_rec/3]). %% 23 43 | -export([loop_rec_end/2]). %% 24 44 | -export([wait/2]). %% 25 45 | -export([wait_timeout/3]). %% 26 46 | -export([m_plus/5]). %% 27* 47 | -export([m_minus/5]). %% 28* 48 | -export([m_times/5]). %% 29* 49 | -export([m_div/5]). %% 30* 50 | -export([int_div/5]). %% 31* 51 | -export([int_rem/5]). %% 32* 52 | -export([int_band/5]). %% 33* 53 | -export([int_bor/5]). %% 34* 54 | -export([int_bxor/5]). %% 35* 55 | -export([int_bsl/5]). %% 36* 56 | -export([int_bsr/5]). %% 37* 57 | -export([int_bnot/4]). %% 38* 58 | -export([is_lt/4]). %% 39 59 | -export([is_ge/4]). %% 40 60 | -export([is_eq/4]). %% 41 61 | -export([is_ne/4]). %% 42 62 | -export([is_eq_exact/4]). %% 43 63 | -export([is_ne_exact/4]). %% 44 64 | -export([is_integer/3]). %% 45 65 | -export([is_float/3]). %% 46 66 | -export([is_number/3]). %% 47 67 | -export([is_atom/3]). %% 48 68 | -export([is_pid/3]). %% 49 69 | -export([is_reference/3]). %% 50 70 | -export([is_port/3]). %% 51 71 | -export([is_nil/3]). %% 52 72 | -export([is_binary/3]). %% 53 73 | -export([is_constant/3]). %% 54* 74 | -export([is_list/3]). %% 55 75 | -export([is_nonempty_list/3]). %% 56 76 | -export([is_tuple/3]). %% 57 77 | -export([test_arity/4]). %% 58 78 | -export([select_val/4]). %% 59 79 | -export([select_tuple_arity/4]). %% 60 80 | -export([jump/2]). %% 61 81 | -export(['catch'/3]). %% 62 82 | -export([catch_end/2]). %% 63 83 | -export([move/3]). %% 64 84 | -export([get_list/4]). %% 65 85 | -export([get_tuple_element/4]). %% 66 86 | -export([set_tuple_element/4]). %% 67 87 | -export([put_string/4]). %% 68* 88 | -export([put_list/4]). %% 69 89 | -export([put_tuple/3]). %% 70 90 | -export([put/2]). %% 71 91 | -export([badmatch/2]). %% 72 92 | -export([if_end/1]). %% 73 93 | -export([case_end/2]). %% 74 94 | -export([call_fun/2]). %% 75 95 | -export([make_fun/4]). %% 76* 96 | -export([is_function/3]). %% 77 97 | -export([call_ext_only/3]). %% 78 98 | -export([bs_start_match/3]). %% 79* 99 | -export([bs_get_integer/3]). %% 80* 100 | -export([bs_get_float/3]). %% 81* 101 | -export([bs_get_binary/3]). %% 82* 102 | -export([bs_skip_bits/3]). %% 83* 103 | -export([bs_test_tail/3]). %% 84* 104 | -export([bs_save/2]). %% 85* 105 | -export([bs_restore/2]). %% 86* 106 | -export([bs_init/3]). %% 87* 107 | -export([bs_final/3]). %% 88* 108 | -export([bs_put_integer/6]). %% 89 109 | -export([bs_put_binary/6]). %% 90 110 | -export([bs_put_float/6]). %% 91 111 | -export([bs_put_string/3]). %% 92 112 | -export([bs_need_buf/2]). %% 93* 113 | -export([fclearerror/1]). %% 94 114 | -export([fcheckerror/2]). %% 95 115 | -export([fmove/3]). %% 96 116 | -export([fconv/3]). %% 97 117 | -export([fadd/5]). %% 98 118 | -export([fsub/5]). %% 99 119 | -export([fmul/5]). %% 100 120 | -export([fdiv/5]). %% 101 121 | -export([fnegate/4]). %% 102 122 | -export([make_fun2/1]). %% 103* 123 | -export(['try'/3]). %% 104* 124 | -export([try_end/2]). %% 105* 125 | -export([try_case/2]). %% 106* 126 | -export([try_case_end/2]). %% 107* 127 | -export([raise/3]). %% 108* 128 | -export([bs_init2/7]). %% 109 129 | -export([bs_bits_to_bytes/4]). %% 110* 130 | -export([bs_add/6]). %% 111 131 | -export([apply/2]). %% 112 132 | -export([apply_last/3]). %% 113 133 | -export([is_boolean/3]). %% 114 134 | -export([is_function2/4]). %% 115 135 | -export([bs_start_match2/6]). %% 116* 136 | -export([bs_get_integer2/8]). %% 117* 137 | -export([bs_get_float2/8]). %% 118* 138 | -export([bs_get_binary2/8]). %% 119* 139 | -export([bs_skip_bits2/6]). %% 120* 140 | -export([bs_test_tail2/4]). %% 121* 141 | -export([bs_save2/3]). %% 122* 142 | -export([bs_restore2/3]). %% 123* 143 | -export([gc_bif1/6]). %% 124 144 | -export([gc_bif2/7]). %% 125 145 | -export([bs_final2/3]). %% 126* 146 | -export([bs_bits_to_bytes2/3]). %% 127* 147 | -export([put_literal/3]). %% 128* 148 | -export([is_bitstr/3]). %% 129 149 | -export([bs_context_to_binary/2]). %% 130 150 | -export([bs_test_unit/4]). %% 131 151 | -export([bs_match_string/5]). %% 132 152 | -export([bs_init_writable/1]). %% 133 153 | -export([bs_append/9]). %% 134 154 | -export([bs_private_append/7]). %% 135 155 | -export([trim/3]). %% 136 156 | -export([bs_init_bits/7]). %% 137 157 | -export([bs_get_utf8/6]). %% 138 158 | -export([bs_skip_utf8/5]). %% 139 159 | -export([bs_get_utf16/6]). %% 140 160 | -export([bs_skip_utf16/5]). %% 141 161 | -export([bs_get_utf32/6]). %% 142 162 | -export([bs_skip_utf32/5]). %% 143 163 | -export([bs_utf8_size/4]). %% 144 164 | -export([bs_put_utf8/4]). %% 145 165 | -export([bs_utf16_size/4]). %% 146 166 | -export([bs_put_utf16/4]). %% 147 167 | -export([bs_put_utf32/4]). %% 148 168 | -export([on_load/1]). %% 149 169 | -export([recv_mark/2]). %% 150 170 | -export([recv_set/2]). %% 151 171 | -export([gc_bif3/8]). %% 152 172 | -export([line/2]). %% 153 173 | -export([put_map_assoc/6]). %% 154* 174 | -export([put_map_exact/6]). %% 155* 175 | -export([is_map/3]). %% 156* 176 | -export([has_map_fields/4]). %% 157* 177 | -export([get_map_elements/4]). %% 158* 178 | -export([is_tagged_tuple/5]). %% 159; 179 | -export([build_stacktrace/1]). %% 160; 180 | -export([raw_raise/1]). %% 161; 181 | -export([get_hd/3]). %% 162; 182 | -export([get_tl/3]). %% 163; 183 | -export([put_tuple2/3]). %% 164; 184 | -export([bs_get_tail/4]). %% 165; 185 | -export([bs_start_match3/5]). %% 166; 186 | -export([bs_get_position/4]). %% 167; 187 | -export([bs_set_position/3]). %% 168; 188 | -export([swap/3]). %% 169; 189 | -export([bs_start_match4/5]). %% 170; 190 | -export([make_fun3/4]). %% 171; 191 | -export([init_yregs/2]). %% 172; 192 | -export([recv_marker_bind/3]). %% 173; 193 | -export([recv_marker_clear/2]). %% 174; 194 | -export([recv_marker_reserve/2]). %% 175; 195 | -export([recv_marker_use/2]). %% 176; 196 | 197 | 198 | %% generated from beam_load 199 | -export([bif/5]). 200 | -export([gc_bif/6]). 201 | 202 | -import(lists, [map/2, foldr/3, reverse/1]). 203 | 204 | -define(BLANK, []). 205 | -define(THE_NON_VALUE(S), S#s.non). 206 | -define(is_non_value(X,S), (?THE_NON_VALUE(S) =:= X)). 207 | 208 | -define(BSF_ALIGNED, 1). 209 | -define(BSF_LITTLE, 2). 210 | -define(BSF_SIGNED, 4). 211 | -define(BSF_EXACT, 8). 212 | -define(BSF_NATIVE, 16). 213 | 214 | %% 215 | %% @type beam_state() = term() 216 | %% @type arity() = non_negative_integer() 217 | %% @type label() = {'f',non_negative_integer()} 218 | %% @type void() = term() 219 | %% @type register() = term() 220 | %% 221 | -record(s, 222 | { x, 223 | y, 224 | f, 225 | cp, %% continuation pointer 226 | i, %% instruction pointer 227 | code, %% current code block 228 | cs=[], %% catch stack 229 | ferror, %% floating point error code... 230 | timer, %% timeout timer 231 | mark, %% message mark 232 | saved, %% message saved position 233 | tuple_dst, 234 | tuple_arity = 0, 235 | tuple_data = [], 236 | non, %% non value 237 | br, %% destination bit/binary reg 238 | bb = <<>> %% binary buffer 239 | }). 240 | 241 | 242 | %% @doc opcode=1 243 | %% End interpreted code 244 | %% 245 | label(S,L) -> 246 | io:format("meta op: label ~w\n",[L]), %% exit? 247 | next(S). 248 | 249 | %% 250 | %% @spec func_info(_S::beam_state(),Module::atom(),F::atom(), 251 | %% Arity::non_neg_integer()) -> void() 252 | %% 253 | %% @doc opcode=2 254 | %% Function information block 255 | %% 256 | func_info(S, M,F,Arity) -> 257 | fail(S#s { i=S#s.i+2},{f,0},error,{function_clause,M,F,Arity}). 258 | %% 259 | %% @spec int_code_end(_S::beam_state()) -> void() 260 | %% 261 | %% @doc opcode=3 262 | %% End interpreted code 263 | %% 264 | int_code_end(S) -> 265 | io:format("meta op: int_code_end\n"), %% exit? 266 | next(S). 267 | 268 | %% 269 | %% @spec call(_S::beam_state(), Arity::arity(),Label::label()) -> void() 270 | %% 271 | %% @doc opcode=4 272 | %% 273 | call(S, _Arity, {f,I}) -> 274 | Ys = [S#s.i+1 | S#s.y], 275 | dispatch(S#s { i=I, y=Ys}). 276 | 277 | %% 278 | %% @spec call_last(_S::beam_state,Arity::arity(),Label::label(),Dealloc::non_negative_integer()) -> void() 279 | %% 280 | %% @doc opcode=5 281 | %% 282 | call_last(S, _Arity, {f,I}, Dealloc) -> 283 | Ys = deallocate_(Dealloc, S#s.y), 284 | dispatch(S#s { i=I, y=Ys}). 285 | 286 | %% 287 | %% @doc opcode=6 288 | %% 289 | call_only(S, _Arity,{f,I1}) -> 290 | dispatch(S#s { i=I1}). 291 | 292 | %% 293 | %% @doc opcode=7 294 | %% @todo 295 | %% Check if the module is beam interpreted then 296 | %% and pass context, push code together with the return position. 297 | %% 298 | call_ext(S, Arity,{extfunc,Mod,Fun,Arity}) -> 299 | call_ext_(S, Mod,Fun,Arity). 300 | 301 | %% 302 | %% @doc opcode=8 303 | %% 304 | call_ext_last(S, Arity,{extfunc,Mod,Fun,Arity},Dealloc) -> 305 | call_ext_last_(S, Mod,Fun,Arity,Dealloc). 306 | 307 | %% 308 | %% @doc opcode=9 309 | %% 310 | bif0(S, Bif, nofail, Dst) -> 311 | Val = apply(erlang,Bif,[]), 312 | next(store(Dst,Val,S)). 313 | 314 | %% 315 | %% @doc opcode=10 316 | %% 317 | bif1(S, Bif, Fail, A1, Dst) -> 318 | case catch apply(erlang,Bif,[fetch(A1,S)]) of 319 | {'EXIT',Reason} -> 320 | fail(S, Fail,exit,Reason); 321 | Val -> 322 | next(store(Dst,Val,S)) 323 | end. 324 | 325 | %% 326 | %% @doc opcode=11 327 | %% 328 | bif2(S, Bif, Fail, A1, A2, Dst) -> 329 | case catch apply(erlang,Bif,[fetch(A1,S),fetch(A2,S)]) of 330 | {'EXIT',Reason} -> 331 | fail(S, Fail,exit,Reason); 332 | Val -> 333 | next(store(Dst,Val,S)) 334 | end. 335 | 336 | %% @hidden 337 | %% entry point from beam_load (maybe translate) 338 | bif(S,Bif,Fail,[],Dst) -> bif0(S,Bif,Fail,Dst); 339 | bif(S,Bif,Fail,[A1],Dst) -> bif1(S,Bif,Fail,A1,Dst); 340 | bif(S,Bif,Fail,[A1,A2],Dst) -> bif2(S,Bif,Fail,A1,A2,Dst). 341 | 342 | %% 343 | %% @doc opcode=12 344 | %% 345 | allocate(S, StackNeed,_Live) -> 346 | %% FIXME: maybe kill some regs 347 | Ys = allocate_(StackNeed,[],S#s.y), 348 | next(S#s { y = Ys }). 349 | 350 | %% 351 | %% @doc opcode=13 352 | %% 353 | allocate_heap(S, StackNeed,_HeapNeed,_Live) -> 354 | %% FIXME: maybe kill some regs 355 | Ys = allocate_(StackNeed,[],S#s.y), 356 | next(S#s { y = Ys }). 357 | 358 | %% 359 | %% @doc opcode=14 360 | %% 361 | allocate_zero(S,StackNeed,_Live) -> 362 | %% FIXME: maybe kill some regs 363 | Ys = allocate_(StackNeed,?BLANK,S#s.y), 364 | next(S#s { y = Ys }). 365 | 366 | %% @doc opcode=15 367 | allocate_heap_zero(S,StackNeed,_HeapNeed,_Live) -> 368 | %% FIXME: maybe kill some regs 369 | Ys = allocate_(StackNeed,?BLANK,S#s.y), 370 | next(S#s { y = Ys }). 371 | 372 | %% @doc opcode=16 373 | test_heap(S,{alloc,[{words,_N},{floats,_F}]},_Live) -> 374 | %% FIXME: emulate this better 375 | %% heap and float are dynamic 376 | next(S); 377 | %% 378 | %% @doc opcode=16 379 | %% 380 | test_heap(S,_HeapNeed,_Live) -> 381 | %% FIXME: emulate this better 382 | %% heap is dynamic 383 | next(S). 384 | %% 385 | %% @doc opcode=17 386 | %% 387 | init(S,Dst) -> 388 | next(make_blank(Dst,S)). 389 | %% 390 | %% @doc opcode=18 391 | %% 392 | deallocate(S,Deallocate) -> 393 | Ys = deallocate_(Deallocate, S#s.y), 394 | next(S#s { y = Ys }). 395 | 396 | %% 397 | %% @doc opcode=19 398 | %% return value 399 | %% @todo check if IRet is on form {Pos,Code} then install the code! 400 | %% 401 | return(S) -> 402 | case S#s.y of 403 | [IRet|Ys] -> 404 | dispatch(S#s {i=IRet, y=Ys}); 405 | [] -> 406 | fetch({x,0},S) 407 | end. 408 | 409 | %% 410 | %% @doc opcode=20 411 | %% 412 | send(S) -> 413 | Result = (fetch({x,0},S) ! fetch({x,1},S)), 414 | S1 = store({x,0}, Result, S), 415 | next(S1). 416 | 417 | %% 418 | %% @doc opcode=21 419 | %% 420 | remove_message(S) -> 421 | message:remove(), 422 | next(S). 423 | 424 | %% 425 | %% @doc opcode=22 426 | %% 427 | timeout(S) -> 428 | message:first(), %% restart scanning 429 | next(S). 430 | 431 | %% 432 | %% @doc opcode=23 433 | %% 434 | loop_rec(S,{f,IL},Dst) -> 435 | case message:current() of 436 | empty -> 437 | %% jump to wait or wait_timeout 438 | dispatch(S#s { i=IL}); 439 | {message,M} -> 440 | next(store(Dst,M,S)) 441 | end. 442 | 443 | %% 444 | %% @doc opcode=24 445 | %% 446 | loop_rec_end(S,{f,IL}) -> 447 | _Ignore = message:next(), 448 | dispatch(S#s {i=IL}). 449 | 450 | %% 451 | %% @doc opcode=25 452 | %% 453 | wait(S,{f,IL}) -> 454 | message:next(infinity), 455 | next(S#s {i=IL}). 456 | 457 | %% 458 | %% @doc opcode=26 459 | %% 460 | wait_timeout(S,{f,IL},Src) -> 461 | case S#s.timer of 462 | undefined -> 463 | Tmo = fetch(Src,S), 464 | if Tmo == infinity -> 465 | message:next(), 466 | dispatch(S#s {i=IL}); 467 | Tmo >= 0, Tmo =< 16#ffffffff -> 468 | Timer = erlang:start_timer(Tmo,undefined,tmo), 469 | case message:next(Tmo) of 470 | timeout -> 471 | next(S); 472 | {message,_} -> 473 | dispatch(S#s{i=IL,timer=Timer}) 474 | end; 475 | true -> 476 | fail(S,{f,0},error,timeout_value) 477 | end; 478 | Timer -> 479 | Timeout = case erlang:read_timer(Timer) of 480 | false -> 0; 481 | RVal -> RVal 482 | end, 483 | case message:next(Timeout) of 484 | timeout -> 485 | next(S#s { timer=undefined }); 486 | {message,_} -> 487 | dispatch(S#s {i=IL}) 488 | end 489 | end. 490 | 491 | %% 492 | %% 27...38 not generated by compiler anymore? (but may be expanded?) 493 | 494 | %% 495 | %% @doc opcode=27 496 | %% 497 | m_plus(S,Fail,A1,A2,Reg) -> 498 | binary_op(S,Fail,A1,A2,Reg,fun(A,B) -> A+B end). 499 | 500 | %% 501 | %% @doc opcode=28 502 | %% 503 | m_minus(S,Fail,A1,A2,Reg) -> 504 | binary_op(S,Fail,A1,A2,Reg,fun(A,B) -> A-B end). 505 | 506 | %% 507 | %% @doc opcode=29 508 | %% 509 | m_times(S,Fail,A1,A2,Reg) -> 510 | binary_op(S,Fail,A1,A2,Reg,fun(A,B) -> A*B end). 511 | 512 | %% 513 | %% @doc opcode=30 514 | %% 515 | m_div(S,Fail,A1,A2,Reg) -> 516 | binary_op(S,Fail,A1,A2,Reg,fun(A,B) -> A / B end). 517 | 518 | %% 519 | %% @doc opcode=31 520 | %% 521 | int_div(S,Fail,A1,A2,Reg) -> 522 | binary_op(S,Fail,A1,A2,Reg,fun(A,B) -> A div B end). 523 | 524 | %% 525 | %% @doc opcode=32 526 | %% 527 | int_rem(S,Fail,A1,A2,Reg) -> 528 | binary_op(S,Fail,A1,A2,Reg,fun(A,B) -> A rem B end). 529 | 530 | %% 531 | %% @doc opcode=33 532 | %% 533 | int_band(S,Fail,A1,A2,Reg) -> 534 | binary_op(S,Fail,A1,A2,Reg,fun(A,B) -> A band B end). 535 | 536 | %% 537 | %% @doc opcode=34 538 | %% 539 | int_bor(S,Fail,A1,A2,Reg) -> 540 | binary_op(S,Fail,A1,A2,Reg,fun(A,B) -> A bor B end). 541 | 542 | %% 543 | %% @doc opcode=35 544 | %% 545 | int_bxor(S,Fail,A1,A2,Reg) -> 546 | binary_op(S,Fail,A1,A2,Reg,fun(A,B) -> A bxor B end). 547 | 548 | %% 549 | %% @doc opcode=36 550 | %% 551 | int_bsl(S,Fail,A1,A2,Reg) -> 552 | binary_op(S,Fail,A1,A2,Reg,fun(A,B) -> A bsl B end). 553 | 554 | %% 555 | %% @doc opcode=37 556 | %% 557 | int_bsr(S,Fail,A1,A2,Reg) -> 558 | binary_op(S,Fail,A1,A2,Reg,fun(A,B) -> A bsr B end). 559 | 560 | %% 561 | %% @doc opcode=38 562 | %% 563 | int_bnot(S,Fail,A1,Reg) -> 564 | unary_op(S,Fail,A1,Reg,fun(A) -> bnot A end). 565 | 566 | %% 567 | %% @doc opcode=39 568 | %% 569 | is_lt(S,Fail,A1,A2) -> 570 | compare_op(S,Fail,A1,A2,fun(A,B) -> A < B end). 571 | 572 | 573 | %% 574 | %% @doc opcode=40 575 | %% 576 | is_ge(S,Fail,A1,A2) -> 577 | compare_op(S,Fail,A1,A2,fun(A,B) -> A >= B end). 578 | 579 | %% 580 | %% @doc opcode=41 581 | %% 582 | is_eq(S,Fail,A1,A2) -> 583 | compare_op(S,Fail,A1,A2,fun(A,B) -> A == B end). 584 | 585 | %% 586 | %% @doc opcode=42 587 | %% 588 | is_ne(S,Fail,A1,A2) -> 589 | compare_op(S,Fail,A1,A2,fun(A,B) -> A /= B end). 590 | 591 | %% 592 | %% @doc opcode=43 593 | %% 594 | is_eq_exact(S,Fail,A1,A2) -> 595 | compare_op(S,Fail,A1,A2,fun(A,B) -> A =:= B end). 596 | 597 | %% 598 | %% @doc opcode=44 599 | %% 600 | is_ne_exact(S,Fail,A1,A2) -> 601 | compare_op(S,Fail,A1,A2,fun(A,B) -> A =/= B end). 602 | 603 | %% 604 | %% @doc opcode=45 605 | %% 606 | is_integer(S,Fail,A1) -> 607 | test_op(S,Fail,A1,fun(A) -> is_integer(A) end). 608 | 609 | %% 610 | %% @doc opcode=46 611 | %% 612 | is_float(S,Fail,A1) -> 613 | test_op(S,Fail,A1,fun(A) -> is_float(A) end). 614 | 615 | %% @doc opcode=47 616 | is_number(S,Fail,A1) -> 617 | test_op(S,Fail,A1,fun(A) -> is_number(A) end). 618 | 619 | 620 | %% @doc opcode=48 621 | is_atom(S,Fail,A1) -> 622 | test_op(S,Fail,A1,fun(A) -> is_atom(A) end). 623 | 624 | %% @doc opcode=49 625 | is_pid(S,Fail,A1) -> 626 | test_op(S,Fail,A1,fun(A) -> is_pid(A) end). 627 | 628 | %% @doc opcode=50 629 | is_reference(S,Fail,A1) -> 630 | test_op(S,Fail,A1,fun(A) -> is_reference(A) end). 631 | 632 | %% @doc opcode=51 633 | is_port(S,Fail,A1) -> 634 | test_op(S,Fail,A1,fun(A) -> is_port(A) end). 635 | 636 | 637 | %% @doc opcode=52 638 | is_nil(S,Fail,A1) -> 639 | test_op(S,Fail,A1,fun(A) -> A =:= [] end). 640 | 641 | %% @doc opcode=53 642 | is_binary(S,Fail,A1) -> 643 | test_op(S,Fail,A1,fun(A) -> is_binary(A) end). 644 | 645 | %% @doc opcode=54 646 | is_constant(_S,_Fail,_A1) -> 647 | {not_implemented, 54}. 648 | 649 | %% @doc opcode=55 650 | is_list(S,Fail,A1) -> 651 | test_op(S,Fail,A1,fun(A) -> is_list(A) end). 652 | 653 | %% @doc opcode=56 654 | is_nonempty_list(S,Fail,A1) -> 655 | test_op(S,Fail,A1,fun([_|_]) -> true; 656 | (_) -> false end). 657 | %% 658 | %% @doc opcode=57 659 | %% 660 | is_tuple(S,Fail,A1) -> 661 | case is_tuple(fetch(A1,S)) of 662 | false -> fail(S,Fail); 663 | true -> next(S) 664 | end. 665 | 666 | %% @doc opcode=58 667 | %% @todo check size arg! 668 | test_arity(S,Fail,Src,Size) -> 669 | Val = fetch(Src,S), 670 | if is_tuple(Val), size(Val) == Size -> 671 | next(S); 672 | true -> fail(S,Fail) 673 | end. 674 | 675 | %% 676 | %% @doc opcode=59 677 | %% 678 | select_val(S,Val,Fail,{list,Pairs}) -> 679 | case select_val(fetch(Val,S), Pairs) of 680 | {f,I1} -> 681 | dispatch(S#s{i=I1}); 682 | false -> 683 | fail(S,Fail) 684 | end. 685 | 686 | %% 687 | %% @doc opcode=60 688 | %% 689 | select_tuple_arity(S,Val,Fail,{list,Pairs}) -> 690 | T = fetch(Val, S), 691 | if is_tuple(T) -> 692 | case select_val(size(T), Pairs) of 693 | {f,I1} -> 694 | dispatch(S#s {i=I1}); 695 | false -> 696 | fail(S,Fail) 697 | end; 698 | true -> 699 | fail(S,Fail) 700 | end. 701 | 702 | %% 703 | %% @doc opcode=61 704 | %% 705 | jump(S,{f,I1}) -> 706 | dispatch(S#s{i=I1}). 707 | 708 | %% 709 | %% @spec catch(_S::beam_state(),Dst::register(),Fail::label()) -> void() 710 | %% 711 | %% @doc opcode=62 712 | %% 713 | 714 | 'catch'(S,Dst,Fail) -> 715 | S1 = store(Dst,Fail,S), %% just for the record 716 | Cs = [{Fail,length(S1#s.y)} | S1#s.cs], 717 | next(S1#s { cs = Cs }). 718 | 719 | %% 720 | %% @doc opcode=63 721 | %% 722 | catch_end(S,Dst) -> 723 | S1 = make_blank(Dst,S), %% just for the record 724 | Cs = tl(S1#s.cs), 725 | X0 = fetch({x,0},S1), 726 | S2 = if ?is_non_value(X0,S) -> 727 | X1 = fetch({x,1},S1), 728 | X2 = fetch({x,2},S1), 729 | if X1 == thrown -> 730 | store({x,0},X2,S1); 731 | X1 == error -> 732 | store({x,0},{X2,[stack_trace]},S1); 733 | true -> 734 | store({x,0},{'EXIT',X2},S1) 735 | end; 736 | true -> 737 | S1 738 | end, 739 | next(S2#s { cs = Cs }). 740 | 741 | %% 742 | %% @doc opcode=64 743 | %% 744 | move(S,Src,Dst) -> 745 | S1 = store(Dst, fetch(Src,S), S), 746 | next(S1). 747 | 748 | %% 749 | %% @doc opcode=65 750 | %% 751 | get_list(S,Src,Head,Tail) -> 752 | [H|T] = fetch(Src,S), 753 | S1 = store(Head,H,S), 754 | S2 = store(Tail,T,S1), 755 | next(S2). 756 | 757 | %% 758 | %% @doc opcode=66 759 | %% 760 | get_tuple_element(S,Src,Ix,Dst) -> 761 | E = element(Ix+1, fetch(Src,S)), 762 | next(store(Dst,E,S)). 763 | 764 | %% 765 | %% @doc opcode=67 766 | %% 767 | set_tuple_element(S,Val,Dst,Ix) -> 768 | T = setelement(Ix,fetch(Dst,S),fetch(Val,S)), 769 | next(store(Dst,T,S)). 770 | 771 | %% 772 | %% @doc opcode=68 773 | %% 774 | put_string(S,_Len,{string,String},Dst) -> 775 | next(store(Dst,String,S)). 776 | 777 | %% 778 | %% @doc opcode=69 779 | %% 780 | put_list(S,Head,Tail,Dst) -> 781 | L = [fetch(Head,S) | fetch(Tail,S)], 782 | next(store(Dst,L,S)). 783 | 784 | %% 785 | %% @doc opcode=70 786 | %% 787 | put_tuple(S,Arity,Dst) -> 788 | S1 = 789 | if Arity == 0 -> 790 | store(Dst,{},S); 791 | true -> 792 | S#s { tuple_dst=Dst, tuple_arity=Arity, tuple_data=[]} 793 | end, 794 | next(S1). 795 | 796 | %% 797 | %% @doc opcode=71 798 | %% 799 | put(S,Src) -> 800 | Val = fetch(Src,S), 801 | Data = [Val | S#s.tuple_data], 802 | S1 = 803 | case S#s.tuple_arity-1 of 804 | 0 -> 805 | store(S#s.tuple_dst, list_to_tuple(reverse(Data)), 806 | S#s { tuple_dst=undefined, tuple_arity=0, tuple_data=[]}); 807 | A -> 808 | S#s { tuple_arity=A, tuple_data=Data } 809 | end, 810 | next(S1). 811 | 812 | %% 813 | %% @doc opcode=72 814 | %% @todo Check this 815 | %% 816 | badmatch(S,Fail) -> 817 | fail(S,Fail,error,badmatch). 818 | %% 819 | %% @doc opcode=73 820 | %% @todo I++ ? 821 | %% 822 | if_end(S) -> 823 | fail(S,{f,0},error,if_clause). 824 | 825 | %% 826 | %% @doc opcode=74 827 | %% 828 | case_end(S,CaseVal) -> 829 | fail(S,{f,0},error,{case_clause,fetch(CaseVal,S)}). 830 | 831 | %% 832 | %% @doc opcode=75 833 | %% 834 | call_fun(S,Arity) -> 835 | As = fetch_regs(Arity,S), 836 | Fun = fetch({x,Arity},S), 837 | case catch erlang:apply(Fun, As) of 838 | {'EXIT',Reason} -> 839 | fail(S,{f,0},exit,Reason); 840 | Ret -> 841 | next(store({x,0}, Ret, S)) 842 | end. 843 | 844 | %% 845 | %% @doc opcode=76 846 | %% 847 | make_fun(_S, _Arg1, _Arg2, _Arg3) -> 848 | {not_implemented, 76}. 849 | 850 | %% 851 | %% @doc opcode=77 852 | %% 853 | is_function(S,Fail,A1) -> 854 | case is_function(fetch(A1,S)) of 855 | false -> fail(S,Fail); 856 | true -> next(S) 857 | end. 858 | 859 | %% 860 | %% @doc opcode=78 861 | %% 862 | call_ext_only(S,Arity,{extfunc,Mod,Fun,Arity}) -> 863 | call_ext_last_(S,Mod,Fun,Arity,0). 864 | 865 | %% opcodes 79..88 not generated 866 | 867 | %% 868 | %% @doc opcode=79 869 | %% 870 | bs_start_match(_S,_Fail,_Reg) -> 871 | {not_implemented,79}. 872 | %% 873 | %% @doc opcode=80 874 | %% 875 | bs_get_integer(_S,_Fail,[_Arg,_N,_Flags,_Dst]) -> 876 | {not_implemented,80}. 877 | %% 878 | %% @doc opcode=81 879 | %% 880 | bs_get_float(_S,_Fail,[_Arg,_N,_Flags,_Dst]) -> 881 | {not_implemented,81}. 882 | %% 883 | %% @doc opcode=82 884 | %% 885 | bs_get_binary(_S,_Fail,[_Arg,_N,_Flags,_Dst]) -> 886 | {not_implemented,82}. 887 | %% 888 | %% @doc opcode=83 889 | %% 890 | bs_skip_bits(_S,_Fail,[_Arg,_N,_Flags]) -> 891 | {not_implemented,83}. 892 | %% 893 | %% @doc opcode=84 894 | %% 895 | bs_test_tail(_S,_Fail,[_N]) -> 896 | {not_implemented,84}. 897 | %% 898 | %% @doc opcode=85 899 | %% 900 | bs_save(_S,_N) -> 901 | {not_implemented,85}. 902 | %% 903 | %% @doc opcode=86 904 | %% 905 | bs_restore(_S,_N) -> 906 | {not_implemented,86}. 907 | %% 908 | %% @doc opcode=87 909 | %% 910 | bs_init(_S,_N,_Flags) -> 911 | {not_implemented,87}. 912 | %% 913 | %% @doc opcode=88 914 | %% 915 | bs_final(_S,_Fail,_X) -> 916 | {not_implemented,88}. 917 | %% 918 | %% @doc opcode=89 919 | %% 920 | bs_put_integer(S,Fail,ArgSz,_N,{field_flags,Flags},ArgInt) -> 921 | Int = fetch(ArgInt, S), 922 | Sz = fetch(ArgSz, S), 923 | case catch (if Flags band ?BSF_LITTLE =/= 0 -> 924 | <<(S#s.bb)/bits, Int:Sz/little>>; 925 | Flags band ?BSF_NATIVE =/= 0 -> 926 | <<(S#s.bb)/bits, Int:Sz/native>>; 927 | true -> 928 | <<(S#s.bb)/bits, Int:Sz>> 929 | end) of 930 | {'EXIT',Reason} -> 931 | fail(S,Fail,exit,Reason); 932 | BB -> 933 | S1 = store(S#s.br, BB, S), 934 | S2 = S1#s { bb = BB }, 935 | next(S2) 936 | end. 937 | 938 | %% 939 | %% @doc opcode=90 940 | %% 941 | bs_put_binary(S,Fail,ArgSz,_N,{field_flags,Flags},ArgBin) -> 942 | Bin = fetch(ArgBin, S), 943 | Sz = fetch(ArgSz, S), 944 | case catch (if Flags band ?BSF_LITTLE =/= 0 -> 945 | <<(S#s.bb)/bits, Bin:Sz/binary-little>>; 946 | Flags band ?BSF_NATIVE =/= 0 -> 947 | <<(S#s.bb)/bits, Bin:Sz/binary-native>>; 948 | true -> 949 | <<(S#s.bb)/bits, Bin:Sz/binary>> 950 | end) of 951 | {'EXIT',Reason} -> 952 | fail(S,Fail,exit,Reason); 953 | BB -> 954 | S1 = store(S#s.br, BB, S), 955 | S2 = S1#s { bb = BB }, 956 | next(S2) 957 | end. 958 | 959 | %% 960 | %% @doc opcode=91 961 | %% 962 | bs_put_float(S,Fail,ArgSz,_N,{field_flags,Flags},ArgFloat) -> 963 | Flt = fetch(ArgFloat, S), 964 | Sz = fetch(ArgSz, S), 965 | case catch (if Flags band ?BSF_LITTLE =/= 0 -> 966 | <<(S#s.bb)/bits, Flt:Sz/little-float>>; 967 | Flags band ?BSF_NATIVE =/= 0 -> 968 | <<(S#s.bb)/bits, Flt:Sz/native-float>>; 969 | true -> 970 | <<(S#s.bb)/bits, Flt:Sz/float>> 971 | end) of 972 | {'EXIT',Reason} -> 973 | fail(S,Fail,exit,Reason); 974 | BB -> 975 | S1 = store(S#s.br, BB, S), 976 | S2 = S1#s { bb = BB }, 977 | next(S2) 978 | end. 979 | 980 | %% 981 | %% @doc opcode=92 982 | %% 983 | bs_put_string(S,_Len,StrArg) -> 984 | String = fetch(StrArg, S), 985 | BB = <<(S#s.bb)/bits,(list_to_binary(String))/bits>>, 986 | S1 = store(S#s.br, BB, S), 987 | S2 = S1#s { bb = BB }, 988 | next(S2). 989 | 990 | %% 991 | %% @doc opcode=93 992 | %% 993 | bs_need_buf(_S,_N) -> 994 | {not_implemented,93}. 995 | 996 | %% 997 | %% @doc opcode=94 998 | %% 999 | fclearerror(S) -> 1000 | next(S#s { ferror=undefined}). 1001 | 1002 | %% @doc opcode=95 1003 | fcheckerror(S,Fail) -> 1004 | if S#s.ferror == undefined -> 1005 | next(S); 1006 | true -> 1007 | fail(S,Fail,error,S#s.ferror) 1008 | end. 1009 | 1010 | %% @doc opcode=96 1011 | fmove(S,Src,FDst) -> 1012 | S1 = store(FDst, fetch(Src,S), S), 1013 | next(S1). 1014 | 1015 | %% @doc opcode=97 1016 | %% @todo Check the bignum conversion to float 1017 | fconv(S,Src,FDst) -> 1018 | case fetch(Src, S) of 1019 | Int when is_integer(Int) -> 1020 | next(store(FDst,float(Int),S)); 1021 | Float when is_float(Float) -> 1022 | next(store(FDst,Float,S)); 1023 | _ -> 1024 | fail(S,{f,0},error,badarith) 1025 | end. 1026 | 1027 | %% @doc opcode=98 1028 | fadd(S,_Fail,FA1,FA2,FDst) -> 1029 | case catch (fetch(FA1,S) + fetch(FA2,S)) of 1030 | {'EXIT',Reason} -> 1031 | next(S#s { ferror=Reason}); 1032 | Val -> 1033 | next(store(FDst,Val,S)) 1034 | end. 1035 | 1036 | %% @doc opcode=99 1037 | fsub(S,_Fail,FA1,FA2,FDst) -> 1038 | case catch (fetch(FA1,S) - fetch(FA2,S)) of 1039 | {'EXIT',Reason} -> 1040 | next(S#s { ferror=Reason}); 1041 | Val -> 1042 | next(store(FDst,Val,S)) 1043 | end. 1044 | 1045 | %% @doc opcode=100 1046 | fmul(S,_Fail,FA1,FA2,FDst) -> 1047 | case catch (fetch(FA1,S) * fetch(FA2,S)) of 1048 | {'EXIT',Reason} -> 1049 | next(S#s { ferror=Reason }); 1050 | Val -> 1051 | next(store(FDst,Val,S)) 1052 | end. 1053 | 1054 | %% @doc opcode=101 1055 | fdiv(S,_Fail,FA1,FA2,FDst) -> 1056 | case catch (fetch(FA1,S) / fetch(FA2,S)) of 1057 | {'EXIT',Reason} -> 1058 | next(S#s { ferror=Reason}); 1059 | Val -> 1060 | next(store(FDst,Val,S)) 1061 | end. 1062 | 1063 | %% @doc opcode=102 1064 | fnegate(S,_Fail,FA1,FDst) -> 1065 | case catch -fetch(FA1,S) of 1066 | {'EXIT',Reason} -> 1067 | next(S#s { ferror=Reason}); 1068 | Val -> 1069 | next(store(FDst,Val,S)) 1070 | end. 1071 | 1072 | %% @doc opcode=103 1073 | make_fun2(_S) -> 1074 | {not_implemented,103}. 1075 | 1076 | %% @doc opcode=104 1077 | 'try'(_S,_Reg,_Fail) -> 1078 | {not_implemented,104}. 1079 | 1080 | %% @doc opcode=105 1081 | try_end(S,Reg) -> 1082 | S1 = make_blank(Reg,S), %% just for the record 1083 | Cs = tl(S1#s.cs), 1084 | X0 = fetch({x,0},S1), 1085 | if ?is_non_value(X0,S1) -> 1086 | S2 = store({x,0}, fetch({x,1},S1), S1), 1087 | S3 = store({x,1}, fetch({x,2},S1), S2), 1088 | S4 = store({x,2}, fetch({x,3},S1), S3), 1089 | next(S4#s { cs = Cs }); 1090 | true -> 1091 | next(S1#s { cs = Cs }) 1092 | end. 1093 | 1094 | %% @doc opcode=106 1095 | try_case(_S,_Reg) -> 1096 | {not_implemented,106}. 1097 | 1098 | %% @doc opcode=107 1099 | try_case_end(S,TryVal) -> 1100 | fail(S,{f,0},error,{try_clause,fetch(TryVal,S)}). 1101 | 1102 | %% @doc opcode=108 1103 | raise(S,Class,Reason) -> 1104 | %% Fixme: more work here 1105 | fail(S,{f,0},fetch(Class,S),fetch(Reason,S)). 1106 | 1107 | %% @doc opcode=109 1108 | bs_init2(S,_Fail,_Src,_W,_R,{field_flags,_Flags},Dst) -> 1109 | S1 = S#s { br = Dst }, 1110 | next(S1). 1111 | 1112 | %% @doc opcode=110 1113 | bs_bits_to_bytes(_S,_Fail,_Src,_Dst) -> 1114 | {not_implemented,110}. 1115 | 1116 | %% @doc opcode=111 1117 | bs_add(S,_Fail, Src1,Src2, Unit,Dest) -> 1118 | Val = fetch(Src2,S)*Unit + fetch(Src1,S), 1119 | next(store(Dest,Val,S)). 1120 | 1121 | %% @doc opcode=112 1122 | apply(S,Arity) -> 1123 | As = fetch_regs(Arity,S), 1124 | Mod = fetch({x,Arity},S), 1125 | Fun = fetch({x,Arity+1},S), 1126 | case catch apply(Mod,Fun,As) of 1127 | {'EXIT',Reason} -> 1128 | fail(S,{f,0},exit,Reason); 1129 | Ret -> 1130 | next(store({x,0},Ret,S)) 1131 | end. 1132 | 1133 | %% @doc opcode=113 1134 | apply_last(S,Arity,U) -> 1135 | As = fetch_regs(Arity,S), 1136 | Mod = fetch({x,Arity},S), 1137 | Fun = fetch({x,Arity+1},S), 1138 | case catch apply(Mod,Fun,As) of 1139 | {'EXIT',Reason} -> 1140 | fail(S,{f,0},exit,Reason); 1141 | Ret -> 1142 | case deallocate_(U, S#s.y) of 1143 | [IRet|Ys] -> 1144 | S1 = S#s { y=Ys }, 1145 | S2 = store({x,0},Ret,S1), 1146 | dispatch(S2#s {i=IRet }); %% was S? 1147 | [] -> 1148 | Ret %% ? 1149 | end 1150 | end. 1151 | 1152 | %% 1153 | %% @doc opcode=114 1154 | %% 1155 | is_boolean(S,Fail,A1) -> 1156 | case is_boolean(fetch(A1,S)) of 1157 | false -> fail(S,Fail); 1158 | true -> next(S) 1159 | end. 1160 | %% 1161 | %% @doc opcode=115 1162 | %% 1163 | is_function2(S,Fail,A1,A2) -> 1164 | case is_function(fetch(A1,S),fetch(A2,S)) of 1165 | false -> fail(S,Fail); 1166 | true -> next(S) 1167 | end. 1168 | %% 1169 | %% @doc opcode=116 1170 | %% 1171 | bs_start_match2(_S,_Fail,_Ctx,_Live,_Save,_Dst) -> 1172 | {not_implemented,116}. 1173 | %% 1174 | %% @doc opcode=117 1175 | %% 1176 | bs_get_integer2(_S,_Fail,_Ctx,_Live,_Size,_N,{field_flags,_Flags},_Dst) -> 1177 | {not_implemented,117}. 1178 | %% 1179 | %% @doc opcode=118 1180 | %% 1181 | bs_get_float2(_S,_Fail,_Ctx,_Live,_Size,_N,{field_flags,_Flags},_Dst) -> 1182 | {not_implemented,118}. 1183 | %% 1184 | %% @doc opcode=119 1185 | %% 1186 | bs_get_binary2(_S, _Fail,_Ctx,_Live,_Size,_N,{field_flags,_Flags},_Dst) -> 1187 | {not_implemented,119}. 1188 | %% 1189 | %% @doc opcode=120 1190 | %% 1191 | bs_skip_bits2(_S,_Fail,_CtxReg,_SizeReg,_Unit,{field_flags,0}) -> 1192 | {not_implemented,120}. 1193 | %% 1194 | %% @doc opcode=121 1195 | %% 1196 | bs_test_tail2(_S, _Fail, _Ctx, _N) -> 1197 | {not_implemented,121}. 1198 | %% 1199 | %% @doc opcode=122 1200 | %% 1201 | bs_save2(_S, _Ctx, _N) -> 1202 | {not_implemented,122}. 1203 | %% 1204 | %% @doc opcode=123 1205 | %% 1206 | bs_restore2(_S, _Ctx, _N) -> 1207 | {not_implemented, 123}. 1208 | %% 1209 | %% @doc opcode=124 1210 | %% 1211 | gc_bif1(S,Bif,Fail,_Live,A1,Dst) -> 1212 | case catch apply(erlang,Bif,[fetch(A1,S)]) of 1213 | {'EXIT',Reason} -> 1214 | fail(S,Fail,exit,Reason); 1215 | Val -> 1216 | next(store(Dst,Val,S)) 1217 | end. 1218 | 1219 | %% 1220 | %% @doc opcode=125 1221 | %% 1222 | gc_bif2(S,Bif,Fail,_Live,A1,A2,Dst) -> 1223 | case catch apply(erlang,Bif,[fetch(A1,S),fetch(A2,S)]) of 1224 | {'EXIT',Reason} -> 1225 | fail(S,Fail,exit,Reason); 1226 | Val -> 1227 | next(store(Dst,Val,S)) 1228 | end. 1229 | 1230 | %% 1231 | %% @doc opcode=126 1232 | %% 1233 | bs_final2(_S, _X, _Y) -> 1234 | {not_implemented, 126}. 1235 | %% 1236 | %% @doc opcode=127 1237 | %% 1238 | bs_bits_to_bytes2(_S, _A2,_A3) -> 1239 | {not_implemented, 127}. 1240 | %% 1241 | %% @doc opcode=128 1242 | %% 1243 | put_literal(_S, _Index, _Dst) -> 1244 | {not_implemented, 128}. 1245 | 1246 | %% 1247 | %% @doc opcode=129 1248 | %% 1249 | is_bitstr(S,Fail,A1) -> 1250 | case is_bitstring(fetch(A1,S)) of 1251 | false -> fail(S,Fail); 1252 | true -> next(S) 1253 | end. 1254 | %% 1255 | %% @doc opcode=130 1256 | %% 1257 | bs_context_to_binary(_S,_Dst) -> 1258 | {not_implemented, 130}. 1259 | %% 1260 | %% @doc opcode=131 1261 | %% 1262 | bs_test_unit(_S,_Fail,_Ctx,_N) -> 1263 | {not_implemented, 131}. 1264 | %% 1265 | %% @doc opcode=132 1266 | %% 1267 | bs_match_string(_S,_Fail,_Ctx,_Bits,_String) -> 1268 | {not_implemented, 130}. 1269 | %% 1270 | %% @doc opcode=133 1271 | %% 1272 | bs_init_writable(_S) -> 1273 | {not_implemented, 133}. 1274 | %% 1275 | %% @doc opcode=134 1276 | %% 1277 | bs_append(_S, _Fail, _Arg2, _W, _R, _U, _Arg6,{field_flags,_Flags},_Arg8) -> 1278 | {not_implemented,134}. 1279 | %% 1280 | %% @doc opcode=135 1281 | %% 1282 | bs_private_append(_S, _Fail, _Arg2, _U, _Arg4, {field_flags,_Flags}, _Arg6) -> 1283 | {not_implemented,135}. 1284 | %% 1285 | %% @doc opcode=136 1286 | %% 1287 | trim(_S,_N,_Remaining) -> 1288 | {not_implemented,136}. 1289 | %% 1290 | %% @doc opcode=137 1291 | %% 1292 | bs_init_bits(_S,_Fail,_Arg2,_W,_R,{field_flags,_Flags},_Arg6) -> 1293 | {not_implemented,137}. 1294 | %% 1295 | %% @doc opcode=138 1296 | %% 1297 | bs_get_utf8(_S,_Fail,_Arg2,_Arg3,{field_flags,_Flags},_Arg4) -> 1298 | {not_implemented,138}. 1299 | %% 1300 | %% @doc opcode=139 1301 | %% 1302 | bs_skip_utf8(_S,_Fail,_Arg2,_Arg3,{field_flags,_Flags}) -> 1303 | {not_implemented,139}. 1304 | %% 1305 | %% @doc opcode=140 1306 | %% 1307 | bs_get_utf16(_S,_Fail,_Arg2,_Arg3,{field_flags,_Flags},_Arg4) -> 1308 | {not_implemented,140}. 1309 | %% 1310 | %% @doc opcode=141 1311 | %% 1312 | bs_skip_utf16(_S,_Fail,_Arg2,_Arg3, {field_flags,_Flags}) -> 1313 | {not_implemented,141}. 1314 | %% 1315 | %% @doc opcode=142 1316 | %% 1317 | bs_get_utf32(_S,_Fail,_Arg2,_Arg3,{field_flags,_Flags},_Arg4) -> 1318 | {not_implemented,142}. 1319 | %% 1320 | %% @doc opcode=143 1321 | %% 1322 | bs_skip_utf32(_S,_Fail,_Arg2,_Arg3,{field_flags,_Flags}) -> 1323 | {not_implemented,143}. 1324 | %% 1325 | %% @doc opcode=144 1326 | %% 1327 | bs_utf8_size(_S,_Fail,_Arg2,_Arg3) -> 1328 | {not_implemented,144}. 1329 | %% 1330 | %% @doc opcode=145 1331 | %% 1332 | bs_put_utf8(_S,_Fail,{field_flags,_Flags},_Arg3) -> 1333 | {not_implemented,145}. 1334 | %% 1335 | %% @doc opcode=146 1336 | %% 1337 | bs_utf16_size(_S,_Fail,_Arg2,_Arg3) -> 1338 | {not_implemented,146}. 1339 | %% 1340 | %% @doc opcode=147 1341 | %% 1342 | bs_put_utf16(_S,_Fail,{field_flags,_Flags},_Arg3) -> 1343 | {not_implemented,147}. 1344 | 1345 | %% @doc opcode=148 1346 | bs_put_utf32(_S,_Fail,{field_flags,_Flags},_Arg3) -> 1347 | {not_implemented,148}. 1348 | 1349 | %% @doc opcode=149 1350 | on_load(S) -> 1351 | io:format("meta op: on_load\n"), %% exit? 1352 | next(S). 1353 | 1354 | %% @doc opcode=150 1355 | recv_mark(S,{f,I}) -> 1356 | next(S#s { mark = I, saved=message:save_message_pos() }). 1357 | 1358 | %% @doc opcode=151 1359 | recv_set(S,{f,I}) -> 1360 | if S#s.mark == I+1 -> 1361 | message:restore_message_pos(S#s.saved); 1362 | true -> 1363 | ok 1364 | end, 1365 | next(S). 1366 | 1367 | %% @doc opcode=152 1368 | gc_bif3(S,Bif,Fail,_Live,A1,A2,A3,Dst) -> 1369 | case catch apply(erlang,Bif,[fetch(A1,S),fetch(A2,S),fetch(A3,S)]) of 1370 | {'EXIT',Reason} -> 1371 | fail(S,Fail,exit,Reason); 1372 | Val -> 1373 | next(store(Dst,Val,S)) 1374 | end. 1375 | 1376 | %% @hidden dispatch from beam_load 1377 | gc_bif(S,Bif,Fail,_Need,[A1],Dst) -> 1378 | gc_bif1(S,Bif,Fail,_Need,A1,Dst); 1379 | gc_bif(S,Bif,Fail,_Need,[A1,A2],Dst) -> 1380 | gc_bif2(S,Bif,Fail,_Need,A1,A2,Dst); 1381 | gc_bif(S,Bif,Fail,_Need,[A1,A2,A3],Dst) -> 1382 | gc_bif3(S,Bif,Fail,_Need,A1,A2,A3,Dst). 1383 | 1384 | %% @doc opcode=153 1385 | %% meta opcode to keep track on line number in stack traces 1386 | %% @enc 1387 | line(S, I) -> 1388 | io:format("meta op: line ~w\n", I), 1389 | next(S). 1390 | 1391 | %% @doc opcode=154 1392 | put_map_assoc(_S,_A1,_A2,_A3,_A4,_A5) -> 1393 | {not_implemented,154}. 1394 | 1395 | %% @doc opcode=155 1396 | put_map_exact(_S,_A1,_A2,_A3,_A4,_A5) -> 1397 | {not_implemented,155}. 1398 | 1399 | %% @doc opcode=156 1400 | is_map(_S,_A1,_A2) -> 1401 | {not_implemented,156}. 1402 | 1403 | %% @doc opcode=157 1404 | has_map_fields(_S,_A1,_A2,_A3) -> 1405 | {not_implemented, 157}. 1406 | 1407 | %% @doc opcode=158 1408 | get_map_elements(_S,_A1,_A2,_A3) -> 1409 | {not_implemented, 158}. 1410 | 1411 | %% @doc opcode=159 1412 | is_tagged_tuple(_S,_A1,_A2,_A3,_A4) -> 1413 | {not_implemented, 159}. 1414 | 1415 | %% @doc opcode=160 1416 | build_stacktrace(_S) -> 1417 | {not_implemented, 160}. 1418 | 1419 | %% @doc opcode=161 1420 | raw_raise(_S) -> 1421 | {not_implemented, 161}. 1422 | 1423 | %% @doc opcode=162 1424 | get_hd(_S,_A1,_A2) -> 1425 | {not_implemented, 162}. 1426 | 1427 | %% @doc opcode=163 1428 | get_tl(_S,_A1,_A2) -> 1429 | {not_implemented, 163}. 1430 | 1431 | %% @doc opcode=164 1432 | put_tuple2(_S,_A1,_A2) -> 1433 | {not_implemented, 164}. 1434 | 1435 | %% @doc opcode=165 1436 | bs_get_tail(_S,_A1,_A2,_A3) -> 1437 | {not_implemented, 165}. 1438 | 1439 | %% @doc opcode=166 1440 | bs_start_match3(_S,_A1,_A2,_A3,_A4) -> 1441 | {not_implemented, 166}. 1442 | 1443 | %% @doc opcode=167 1444 | bs_get_position(_S,_A1,_A2,_A3) -> 1445 | {not_implemented, 167}. 1446 | 1447 | %% @doc opcode=168 1448 | bs_set_position(_S,_A1,_A2) -> 1449 | {not_implemented, 168}. 1450 | 1451 | %% @doc opcode=169 1452 | swap(S,A1,A2) -> 1453 | V1 = fetch(A1,S), 1454 | V2 = fetch(A2,S), 1455 | S1 = store(A2,V1,S), 1456 | S2 = store(A1,V2,S1), 1457 | next(S2). 1458 | 1459 | %% @doc opcode=170 1460 | bs_start_match4(_S,_A1,_A2,_A3,_A4) -> 1461 | {not_implemented, 170}. 1462 | 1463 | %% @doc opcode=171 1464 | make_fun3(_S,_A1,_A2,_A3) -> 1465 | {not_implemented, 171}. 1466 | 1467 | %% @doc opcode=172 1468 | init_yregs(_S,_A1) -> 1469 | {not_implemented, 172}. 1470 | 1471 | %% @doc opcode=173 1472 | recv_marker_bind(_S,_A1,_A2) -> 1473 | {not_implemented, 173}. 1474 | 1475 | %% @doc opcode=174 1476 | recv_marker_clear(_S,_A1) -> 1477 | {not_implemented, 174}. 1478 | 1479 | %% @doc opcode=175 1480 | recv_marker_reserve(_S,_A1) -> 1481 | {not_implemented, 175}. 1482 | 1483 | %% @doc opcode=176 1484 | recv_marker_use(_S,_A1) -> 1485 | {not_implemented, 176}. 1486 | 1487 | %% 1488 | %% @spec run(Module::atom(), Function::atom(), Args::[term()]) -> term() 1489 | %% 1490 | %% @doc Execute an interpreted BEAM function 1491 | %% 1492 | run(Mod, F, Args) when is_atom(Mod), is_atom(F), is_list(Args) -> 1493 | case beam_load:file(Mod) of 1494 | {ok,{Mod,Exp,LCode}} -> 1495 | A = length(Args), 1496 | case lists:keysearch({F,A}, 1, Exp) of 1497 | false -> erlang:error({undef,[{Mod,F,Args}]}); 1498 | {value,{_,I}} -> call_(I,LCode,Args) 1499 | end; 1500 | {ok,{Mod1,_Exp,_LCode}} -> 1501 | erlang:error({load_error,Mod1}); 1502 | _Error -> 1503 | erlang:error({load_error,Mod}) 1504 | end; 1505 | run(File, F, Args) when is_list(File), is_atom(F), is_list(Args) -> 1506 | case beam_load:file(File) of 1507 | {ok,{Mod,Exp,LCode}} -> 1508 | A = length(Args), 1509 | case lists:keysearch({F,A}, 1, Exp) of 1510 | false -> erlang:error({undef,[{Mod,F,Args}]}); 1511 | {value,{_,I}} -> call_(I,LCode,Args) 1512 | end; 1513 | _Error -> 1514 | erlang:error({load_error,File}) 1515 | end. 1516 | 1517 | %% @private 1518 | new_(Args) -> 1519 | #s { x = list_to_tuple(Args), 1520 | f = {0.0, 0.0}, 1521 | y = [], 1522 | non = make_ref() %% must be different from other terms! 1523 | }. 1524 | 1525 | %% @private 1526 | call_(I, C, Args) -> 1527 | init_(I, C, new_(Args)). 1528 | 1529 | %% @private 1530 | init_(I, C, S) -> 1531 | dispatch(S#s { i=I, code=C }). 1532 | 1533 | %% @private 1534 | next(S) -> 1535 | dispatch(S#s { i=S#s.i + 1 }). 1536 | 1537 | %% @private 1538 | %% dispatch the instruction 1539 | dispatch(S) -> 1540 | {Op,Args} = element(S#s.i, S#s.code), 1541 | erlang:display({level(), S#s.i, {Op,Args}}), 1542 | apply(?MODULE, Op, [S|Args]). 1543 | 1544 | %% 1545 | %% @spec level() -> non_neg_integer() 1546 | %% @doc Return the meta BEAM meta level 1547 | %% 1548 | level() -> 1549 | beam_emu:level0(). 1550 | 1551 | level0() -> 1552 | 0. 1553 | 1554 | %% @private 1555 | unary_op(S,Fail,A1,Reg,Op) -> 1556 | case catch Op(fetch(A1,S)) of 1557 | {'EXIT',Reason} -> 1558 | fail(S,Fail,exit,Reason); 1559 | Val -> 1560 | next(store(Reg,Val,S)) 1561 | end. 1562 | 1563 | %% @private 1564 | binary_op(S,Fail,A1,A2,Reg,Op) -> 1565 | case catch Op(fetch(A1,S),fetch(A2,S)) of 1566 | {'EXIT',Reason} -> 1567 | fail(S,Fail,exit,Reason); 1568 | Val -> 1569 | next(store(Reg,Val,S)) 1570 | end. 1571 | 1572 | %% @private 1573 | compare_op(S,Fail,A1,A2,Op) -> 1574 | case Op(fetch(A1,S),fetch(A2,S)) of 1575 | false -> fail(S,Fail); 1576 | true -> next(S) 1577 | end. 1578 | 1579 | %% @private 1580 | test_op(S,Fail,A1,Op) -> 1581 | case Op(fetch(A1,S)) of 1582 | false -> fail(S,Fail); 1583 | true -> next(S) 1584 | end. 1585 | 1586 | %% @private 1587 | call_ext_(S,erlang,'throw',1) -> 1588 | fail(S,{f,0},thrown,fetch({x,0},S)); 1589 | call_ext_(S,erlang,'exit',1) -> 1590 | fail(S,{f,0},exit,fetch({x,0},S)); 1591 | call_ext_(S,beam_emu,F,0) when F==level; F==level0 -> 1592 | Ret = level()+1, 1593 | next(store({x,0},Ret,S)); 1594 | call_ext_(S,Mod,Fun,Arity) -> 1595 | As = fetch_regs(Arity,S), 1596 | case catch erlang:apply(Mod,Fun,As) of 1597 | {'EXIT',Reason} -> 1598 | fail(S,{f,0},exit,Reason); 1599 | Ret -> 1600 | next(store({x,0},Ret,S)) 1601 | end. 1602 | 1603 | call_ext_last_(S,erlang,'throw',1,_Dealloc) -> 1604 | fail(S,{f,0},thrown,fetch({x,0},S)); 1605 | call_ext_last_(S,erlang,'exit',1,_Dealloc) -> 1606 | fail(S,{f,0},exit,fetch({x,0},S)); 1607 | call_ext_last_(S,beam_emu,F,0,Dealloc) when F==level; F==level0 -> 1608 | Ret = level()+1, 1609 | case deallocate_(Dealloc, S#s.y) of 1610 | [IRet|Ys] -> 1611 | S1 = S#s { y=Ys }, 1612 | S2 = store({x,0},Ret,S1), 1613 | dispatch(S2#s { i=IRet}); 1614 | [] -> 1615 | Ret 1616 | end; 1617 | call_ext_last_(S,Mod,Fun,Arity,Dealloc) -> 1618 | As = fetch_regs(Arity,S), 1619 | case catch apply(Mod,Fun,As) of 1620 | {'EXIT',Reason} -> 1621 | fail(S,{f,0},exit,Reason); 1622 | Ret -> 1623 | case deallocate_(Dealloc, S#s.y) of 1624 | [IRet|Ys] -> 1625 | S1 = S#s { y=Ys }, 1626 | S2 = store({x,0},Ret,S1), 1627 | dispatch(S2#s { i=IRet }); 1628 | [] -> 1629 | Ret 1630 | end 1631 | end. 1632 | 1633 | %% @private 1634 | fetch(Reg, State) -> 1635 | Res = fetch_(Reg, State), 1636 | io:format("fetch: ~w = ~p\n", [Reg, Res]), 1637 | Res. 1638 | 1639 | fetch_({x,I}, #s { x=X }) -> element(I+1, X); 1640 | fetch_({y,I}, #s { y=Y }) -> lists:nth(I+1,Y); 1641 | fetch_({fr,I}, #s { f=F }) -> element(I+1, F); 1642 | fetch_({atom,C},_S) -> C; 1643 | fetch_({integer,C},_S) -> C; 1644 | fetch_({float,C},_S) -> C; 1645 | fetch_(nil,_S) -> []; 1646 | fetch_({literal,Lit},_S) -> Lit; 1647 | fetch_({string,String},_S) -> String; 1648 | fetch_(Src, S) -> 1649 | erlang:display({fetch,Src}), 1650 | exit({function_clause,S}). 1651 | 1652 | %% fetch_args([A|As], S) -> 1653 | %% [fetch(A,S) | fetch_args(As,S)]; 1654 | %% fetch_args([], _S) -> 1655 | %% []. 1656 | 1657 | %% fetch sequence of N registers {x,0} .. {x,N-1} 1658 | fetch_regs(0, _S) -> []; 1659 | fetch_regs(N, S) -> fetch_regs(N-1, S, []). 1660 | 1661 | fetch_regs(0, S, Regs) -> 1662 | [fetch({x,0},S)|Regs]; 1663 | fetch_regs(I, S, Regs) -> 1664 | fetch_regs(I-1,S,[fetch({x,I},S) | Regs]). 1665 | 1666 | make_blank(Dst, S) -> 1667 | store(Dst, ?BLANK, S). 1668 | 1669 | store({x,I},Val,S=#s{x=X}) -> 1670 | NX = size(X), 1671 | if I >= NX -> 1672 | X1 = list_to_tuple(tuple_to_list(X)++fill_((I+1)-NX,[],Val)), 1673 | S#s { x = X1 }; 1674 | true -> 1675 | S#s { x = setelement(I+1, X, Val) } 1676 | end; 1677 | store({fr,I},Val,S=#s{f=F}) -> 1678 | NF = size(F), 1679 | if NF =< I -> 1680 | F1 = list_to_tuple(tuple_to_list(F)++fill_((I+1)-NF,0.0,Val)), 1681 | S#s { f = F1 }; 1682 | true -> 1683 | S#s { f = setelement(I+1, F, Val) } 1684 | end; 1685 | store({y,I}, Val, S=#s{y=Y}) -> 1686 | S#s { y = set_nth_element(I, Y, Val) }. 1687 | 1688 | %% @private 1689 | %% set nth element counting from 0! 1690 | set_nth_element(0, [_H|T], V) -> [V|T]; 1691 | set_nth_element(I, [H|T], V) -> [H | set_nth_element(I-1,T,V)]. 1692 | 1693 | %% @private 1694 | %% generate a lists of N nils (lists:duplicate!) 1695 | 1696 | %%fill(N) -> 1697 | %% fill_(N,[],[]). 1698 | 1699 | fill_(0,_D,_V) -> []; 1700 | fill_(1,_D,V) -> [V]; 1701 | fill_(I,D,V) -> [D | fill_(I-1,D,V)]. 1702 | 1703 | %% @private 1704 | allocate_(0,_D,Ys) -> Ys; 1705 | allocate_(I,D,Ys) -> [D | allocate_(I-1,D,Ys)]. 1706 | 1707 | %% @private 1708 | deallocate_(0, Ys) -> Ys; 1709 | deallocate_(I, [_|Ys]) -> deallocate_(I-1, Ys). 1710 | 1711 | %% @private 1712 | fail(S, {f,0}, Class, Reason) -> 1713 | erlang:display({fail,Class,Reason}), 1714 | case S#s.cs of 1715 | [{{f,If},U0}|_] -> 1716 | U1 = length(S#s.y), 1717 | Ys = deallocate_(U1-U0, S#s.y), 1718 | S0 = store({x,0},?BLANK,S), 1719 | S1 = store({x,1},Class,S0), 1720 | S2 = store({x,2},Reason,S1), 1721 | dispatch(S2#s { i=If, y=Ys }); %% was S? 1722 | [] -> 1723 | if Class == thrown -> 1724 | exit(no_catch); 1725 | Class == exit -> 1726 | exit(Reason); 1727 | Class == error -> 1728 | exit({Reason,[stack_trace]}) 1729 | end 1730 | end; 1731 | fail(S, {f,I1},_Class,_Reason) -> 1732 | dispatch(S#s { i=I1 }). 1733 | 1734 | fail(S, {f,I1}) -> 1735 | dispatch(S#s { i=I1 }). 1736 | 1737 | 1738 | select_val(Val, [Val,Jump|_]) -> 1739 | Jump; 1740 | select_val(Val, [{_,Val},Jump|_]) -> 1741 | Jump; 1742 | select_val(Val, [_,_|Select]) -> 1743 | select_val(Val, Select); 1744 | select_val(_Val, []) -> 1745 | false. 1746 | -------------------------------------------------------------------------------- /src/beam_load.erl: -------------------------------------------------------------------------------- 1 | %%% File : beam_load.erl 2 | %%% Author : Tony Rogvall 3 | %%% Description : Beam loader 4 | %%% Created : 9 Jan 2006 by Tony Rogvall 5 | 6 | -module(beam_load). 7 | 8 | -export([wrap/2, file/1]). 9 | -compile(export_all). 10 | 11 | -import(lists, [reverse/1, foldl/3, foldr/3, map/2]). 12 | 13 | -include_lib("compiler/src/beam_disasm.hrl"). 14 | 15 | %% 16 | %% wrap all calls from a module M by creating a stubb module 17 | %% with that name, and temorarily renaming the module M to '__M' 18 | %% 19 | wrap(File, Mod) -> 20 | case beam_disasm:file(File) of 21 | B = #beam_file{} -> 22 | Module = B#beam_file.module, 23 | Exports = B#beam_file.labeled_exports, 24 | %% generate stub calls to Mod 25 | {L,FCode} = 26 | foldl(fun ({module_info,0,_},{L,Code}) -> 27 | {L,Code}; 28 | ({module_info,1,_},{L,Code}) -> 29 | {L,Code}; 30 | ({Func,Arity,_Label},{L,Code}) -> 31 | {L+2, 32 | [{function,Func,Arity,L+1, 33 | [{label,L}, 34 | {func_info,{atom,Module},{atom,Func},Arity}, 35 | {label,L+1}, 36 | {call_ext_only,Arity, 37 | {extfunc,Mod,Func,Arity}}]} | Code]} 38 | end, {1,[]}, Exports), 39 | Asm = 40 | FCode ++ 41 | [{function, module_info, 0, L+1, 42 | [ 43 | {label,L}, 44 | {func_info,{atom,Module},{atom,module_info},0}, 45 | {label,L+1}, 46 | {move,{atom,Mod},{x,0}}, 47 | {call_ext_only,1,{extfunc,erlang,get_module_info,1}}]}, 48 | 49 | {function, module_info, 1, L+3, 50 | [ 51 | {label,L+2}, 52 | {func_info,{atom,Module},{atom,module_info},1}, 53 | {label,L+3}, 54 | {move,{x,0},{x,1}}, 55 | {move,{atom,Mod},{x,0}}, 56 | {call_ext_only,2,{extfunc,erlang,get_module_info,2}}]} 57 | ], 58 | Exp = map(fun({function,Name,Arity,_Entry,_Code}) -> 59 | {Name,Arity} 60 | end, Asm), 61 | Code = {Module,Exp,[],Asm,L+4}, 62 | beam_asm:module(Code, [], File, []); 63 | Err -> 64 | Err 65 | end. 66 | 67 | file(Mod) when is_atom(Mod) -> 68 | case code:which(Mod) of 69 | File when is_list(File) -> 70 | file(File); 71 | Reason when is_atom(Reason) -> 72 | {error,Reason} 73 | end; 74 | file(File) when is_list(File) -> 75 | case beam_disasm:file(File) of 76 | B = #beam_file{} -> 77 | {ok,{LCode,Store}} = load_code(B#beam_file.code,1,[],dict:new()), 78 | Exp = map(fun({F,A,L}) -> 79 | case dict:find(L, Store) of 80 | error -> {{F,A},undefined}; 81 | {ok,Pos} -> {{F,A},Pos} 82 | end 83 | end, B#beam_file.labeled_exports), 84 | {ok,{B#beam_file.module,Exp,LCode}}; 85 | Err -> 86 | Err 87 | end. 88 | 89 | 90 | load_code([{function,Name,Arity,Entry,FCode}|Code], Pos, LCode, Store) -> 91 | load_fcode(FCode, {{Name,Arity},Entry}, Code, Pos, LCode, Store); 92 | load_code([], _Pos, LCode, Store) -> 93 | resolve_code(LCode, [], Store). 94 | 95 | load_fcode([{label,L}|FCode],FAE,Code,Pos,LCode,Store) -> 96 | Store1 = dict:store(L, Pos, Store), 97 | load_fcode(FCode,FAE,Code,Pos,LCode,Store1); 98 | load_fcode([{line,L}|FCode],FAE,Code,Pos,LCode,Store) -> 99 | Store1 = dict:store({line,L}, Pos, Store), 100 | load_fcode(FCode,FAE,Code,Pos,LCode,Store1); 101 | load_fcode([Instr|FCode],FAE,Code,Pos,LCode,Store) -> 102 | load_fcode(FCode,FAE,Code,Pos+1,[Instr|LCode],Store); 103 | load_fcode([],{FA,E},Code,Pos,LCode,Store) -> 104 | {ok,PosE} = dict:find(E, Store), %% resolve entry point 105 | Store1 = dict:store(FA, PosE,Store), 106 | load_code(Code,Pos,LCode,Store1). 107 | 108 | resolve_code([{call,A,{_M,F,A}}|Code], Acc, Store) -> 109 | {ok,Pos} = dict:find({F,A}, Store), 110 | resolve_code(Code, [{call,[A,{f,Pos}]}|Acc], Store); 111 | resolve_code([{call_last,A,{_M,F,A},U}|Code], Acc, Store) -> 112 | {ok,Pos} = dict:find({F,A}, Store), 113 | resolve_code(Code, [{call_last,[A,{f,Pos},U]}|Acc], Store); 114 | resolve_code([{call_only,A,{_M,F,A}}|Code], Acc, Store) -> 115 | {ok,Pos} = dict:find({F,A}, Store), 116 | resolve_code(Code, [{call_only,[A,{f,Pos}]}|Acc], Store); 117 | resolve_code([I|Code], Acc, Store) -> 118 | I1 = 119 | case I of 120 | %% undo some of the beam_disasm work 121 | {arithfbif,Op,F,[A1,A2],Reg} -> 122 | {Op,[resolve_arg(F,Store),A1,A2,Reg]}; 123 | {arithfbif,Op,F,[A1],Reg} -> 124 | {Op,[resolve_arg(F,Store),A1,Reg]}; 125 | {arithbif,Op,F,[A1,A2],Reg} -> 126 | case Op of 127 | '+' -> {m_plus,[resolve_arg(F,Store),A1,A2,Reg]}; 128 | '-' -> {m_minus,[resolve_arg(F,Store),A1,A2,Reg]}; 129 | '*' -> {m_times,[resolve_arg(F,Store),A1,A2,Reg]}; 130 | '/' -> {m_div,[resolve_arg(F,Store),A1,A2,Reg]}; 131 | 'div' -> {int_div,[resolve_arg(F,Store),A1,A2,Reg]}; 132 | 'rem' -> {int_rem,[resolve_arg(F,Store),A1,A2,Reg]}; 133 | 'band' -> {int_band,[resolve_arg(F,Store),A1,A2,Reg]}; 134 | 'bor' -> {int_bor,[resolve_arg(F,Store),A1,A2,Reg]}; 135 | 'bxor' -> {int_bxor,[resolve_arg(F,Store),A1,A2,Reg]}; 136 | 'bsl' -> {int_bsl,[resolve_arg(F,Store),A1,A2,Reg]}; 137 | 'bsr' -> {int_bsr,[resolve_arg(F,Store),A1,A2,Reg]} 138 | end; 139 | {arithbif,Op,F,[A1],Reg} -> 140 | case Op of 141 | 'bnot' -> {int_bnot,[resolve_arg(F,Store),A1,Reg]} 142 | end; 143 | 144 | {test,Test,F,Args} -> 145 | {Test, [resolve_arg(F,Store)|Args]}; 146 | 147 | _ when is_atom(I) -> 148 | {I,[]}; 149 | _ when is_tuple(I) -> 150 | [Op|Args] = tuple_to_list(I), 151 | {Op,resolve_args(Args,Store)} 152 | end, 153 | resolve_code(Code, [I1|Acc], Store); 154 | resolve_code([], Acc, Store) -> 155 | {ok, {list_to_tuple(Acc), Store}}. 156 | 157 | resolve_arg(Arg, Store) -> 158 | case Arg of 159 | {f,0} -> Arg; 160 | {f,L} -> 161 | case dict:find(L, Store) of 162 | {ok,Pos} -> {f,Pos}; 163 | error -> 164 | io:format("unable to lookup label ~p\n", [L]), 165 | {f,undefined} 166 | end; 167 | {list,L} -> 168 | L1 = resolve_args(L,Store), 169 | {list,L1}; 170 | _ -> 171 | Arg 172 | end. 173 | 174 | resolve_args([A|As],Store) -> 175 | [resolve_arg(A,Store) | resolve_args(As,Store)]; 176 | resolve_args([],_Store) -> 177 | []. 178 | 179 | 180 | get_section(Name, Sections, Default) -> 181 | case lists:keysearch(Name,1,Sections) of 182 | false -> Default; 183 | {value, {_, Value}} -> Value 184 | end. 185 | 186 | get_section(Name, Sections) -> 187 | case lists:keysearch(Name,1,Sections) of 188 | {value, {_, Value}} -> Value 189 | end. 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | -------------------------------------------------------------------------------- /src/message.S.old: -------------------------------------------------------------------------------- 1 | {module, message}. %% version = 0 2 | 3 | {exports, [{first,0}, 4 | {next,0}, {next,1}, 5 | {remove,0}, 6 | {current,0}, 7 | {module_info,0}, 8 | {module_info,1}]}. 9 | 10 | {attributes, []}. 11 | 12 | {labels, 24}. 13 | 14 | %% 15 | %% first() -> empty | {message, M} 16 | %% 17 | %% Description: 18 | %% Restart message queue scanning and get the first message 19 | %% return 20 | %% 21 | {function, first, 0, 2}. 22 | {label,1}. 23 | {func_info,{atom,message},{atom,first},0}. 24 | {label,2}. 25 | timeout. 26 | {loop_rec,{f,3},{x,0}}. 27 | {test_heap,3,1}. 28 | {put_tuple,2,{x,1}}. 29 | {put,{atom,message}}. 30 | {put,{x,0}}. 31 | {move,{x,1},{x,0}}. 32 | return. 33 | {label,3}. 34 | {move,{atom,empty},{x,0}}. 35 | return. 36 | 37 | 38 | %% 39 | %% next() -> empty | {message, M} 40 | %% 41 | %% Description: 42 | %% Get the next message in the message queue 43 | %% 44 | {function, next, 0, 5}. 45 | {label,4}. 46 | {func_info,{atom,message},{atom,next},0}. 47 | {label,5}. 48 | {loop_rec,{f,7},{x,0}}. 49 | %% {jump,{f,8}}. does not work, do a dummy 50 | {move,{atom,false},{x,0}}. 51 | {test,is_eq,{f,8},[{x,0},{atom,true}]}. 52 | {label,6}. 53 | {loop_rec,{f,7},{x,0}}. 54 | {test_heap,3,1}. 55 | {put_tuple,2,{x,1}}. 56 | {put,{atom,message}}. 57 | {put,{x,0}}. 58 | {move,{x,1},{x,0}}. 59 | return. 60 | {label,8}. 61 | {loop_rec_end,{f,6}}. 62 | {label,7}. 63 | {move,{atom,empty},{x,0}}. 64 | return. 65 | 66 | %% 67 | %% current() -> empty | {message,M} 68 | %% 69 | %% Description: 70 | %% Peek the current message 71 | %% 72 | {function, current, 0, 10}. 73 | {label,9}. 74 | {func_info,{atom,message},{atom,current},0}. 75 | {label,10}. 76 | {loop_rec,{f,11},{x,0}}. 77 | {test_heap,3,1}. 78 | {put_tuple,2,{x,1}}. 79 | {put,{atom,message}}. 80 | {put,{x,0}}. 81 | {move,{x,1},{x,0}}. 82 | return. 83 | {label,11}. 84 | {move,{atom,empty},{x,0}}. 85 | return. 86 | 87 | %% 88 | %% next(Tmo) -> timeout | {message,M} 89 | %% 90 | %% Description: 91 | %% 92 | %% Get the next message in the message queue 93 | %% or wait for next message maximum Tmo millisecs 94 | %% 95 | {function, next, 1, 13}. 96 | {label,12}. 97 | {func_info,{atom,message},{atom,next},1}. 98 | {label,13}. 99 | {allocate,1,1}. 100 | {move,{x,0},{y,0}}. 101 | %% {'%live', 0}. 102 | %% Check if empty 103 | {loop_rec,{f,17},{x,0}}. 104 | %% Dummy forward jump 105 | {move,{atom,false},{x,0}}. 106 | {test,is_eq,{f,16},[{x,0},{atom,true}]}. 107 | {label,15}. 108 | {loop_rec,{f,17},{x,0}}. 109 | {test_heap,3,1}. 110 | {put_tuple,2,{x,1}}. 111 | {put,{atom,message}}. 112 | {put,{x,0}}. 113 | {move,{x,1},{x,0}}. 114 | {deallocate,1}. 115 | return. 116 | {label,16}. 117 | {loop_rec_end,{f,15}}. 118 | {label,17}. 119 | {wait_timeout,{f,15},{y,0}}. 120 | {move,{atom,timeout},{x,0}}. 121 | {deallocate,1}. 122 | return. 123 | 124 | %% 125 | %% remove() -> true | false 126 | %% 127 | %% Description: 128 | %% Remove current message. Returns true if a message was 129 | %% removed, false otherwise 130 | %% 131 | %% 132 | %% return true | false 133 | {function, remove, 0, 19}. 134 | {label,18}. 135 | {func_info,{atom,message},{atom,remove},0}. 136 | {label,19}. 137 | {loop_rec,{f,20},{x,0}}. 138 | remove_message. 139 | {move,{atom,true},{x,0}}. 140 | return. 141 | {label,20}. 142 | {move,{atom,false},{x,0}}. 143 | return. 144 | 145 | %% 146 | %% Standard module info 147 | %% 148 | 149 | {function, module_info, 0, 21}. 150 | {label,21}. 151 | {func_info,{atom,message},{atom,module_info},0}. 152 | {label,22}. 153 | {move,{atom,message},{x,0}}. 154 | {call_ext_only,1,{extfunc,erlang,get_module_info,1}}. 155 | 156 | 157 | {function, module_info, 1, 24}. 158 | {label,23}. 159 | {func_info,{atom,message},{atom,module_info},1}. 160 | {label,24}. 161 | {move,{x,0},{x,1}}. 162 | {move,{atom,message},{x,0}}. 163 | {call_ext_only,2,{extfunc,erlang,get_module_info,2}}. 164 | -------------------------------------------------------------------------------- /src/message.erl: -------------------------------------------------------------------------------- 1 | %%% @author Tony Rogvall 2 | %%% @copyright (C) 2014, Tony Rogvall 3 | %%% @doc 4 | %%% peek / remove message in message queue 5 | %%% @end 6 | %%% Created : 15 Feb 2014 by Tony Rogvall 7 | 8 | -module(message). 9 | 10 | -export([first/0, 11 | current/0, 12 | next/0, 13 | next/1, 14 | remove/0 15 | ]). 16 | -export([save_message_pos/0, 17 | restore_message_pos/1]). 18 | %% @doc 19 | %% Restart message queue processing and return the first message 20 | %% or 'empty' if not message exist. 21 | %% @end 22 | -spec first() -> empty | {message,Message::term()}. 23 | 24 | first() -> 25 | put('$msg', empty), 26 | put('$msg_pos', -1), 27 | prim_eval:'receive'(fun(Message) -> 28 | case get('$msg') of 29 | empty -> 30 | put('$msg_pos', 0), 31 | put('$msg',{message,Message}), 32 | nomatch; 33 | _ -> 34 | nomatch 35 | end 36 | end, 0), 37 | get('$msg'). 38 | 39 | -spec current() -> empty | {message,Message::term()}. 40 | current() -> 41 | case get('$msg') of 42 | undefined -> first(); 43 | timeout -> first(); 44 | empty -> empty; 45 | M -> M 46 | end. 47 | 48 | -spec next() -> empty | {message,Message::term()}. 49 | 50 | next() -> 51 | next_(empty,0). 52 | 53 | -spec next(T::timeout()) -> timeout | {message,Message::term()}. 54 | 55 | next(Timeout) -> 56 | next_(timeout, Timeout). 57 | 58 | next_(Default, Timeout) -> 59 | put('$msg', Default), 60 | set_message_pos_(2), 61 | prim_eval:'receive'( 62 | fun(Message) -> 63 | case get('$msg_n')-1 of 64 | -1 -> %% skip rest of messages 65 | nomatch; 66 | 0 -> %% return next message 67 | put('$msg_n', 0), 68 | put('$msg_pos', get('$msg_pos')+1), %% advance pos 69 | put('$msg',{message,Message}), 70 | nomatch; 71 | N -> 72 | put('$msg_n', N), 73 | nomatch 74 | end 75 | end, Timeout), 76 | get('$msg'). 77 | 78 | %% 79 | %% @doc 80 | %% Remove current message. Returns true if a message was 81 | %% removed, false otherwise 82 | %% @end 83 | %% 84 | -spec remove() -> boolean(). 85 | 86 | remove() -> 87 | set_message_pos_(1), 88 | M = prim_eval:'receive'( 89 | fun(_Message) -> 90 | case get('$msg_n')-1 of 91 | -1 -> %% skip rest of messages 92 | nomatch; 93 | 0 -> %% return next message 94 | put('$msg_n', 0), 95 | true; 96 | N -> 97 | put('$msg_n', N), 98 | nomatch 99 | end 100 | end, 0), 101 | erase('$msg'), 102 | if M =:= timeout -> false; 103 | true -> M 104 | end. 105 | 106 | save_message_pos() -> 107 | get('$msg_pos'). 108 | 109 | restore_message_pos(Pos) -> 110 | put('$msg_pos', Pos). 111 | 112 | set_message_pos_(Offs) -> 113 | Pos = get_message_pos_(), 114 | put('$msg_pos', Pos), 115 | put('$msg_n', Pos+Offs). 116 | 117 | get_message_pos_() -> 118 | case get('$msg_pos') of 119 | undefined -> 0; 120 | -1 -> 0; 121 | N -> N 122 | end. 123 | --------------------------------------------------------------------------------