ling_lib.erl 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. %% Copyright (c) 2013-2014 Cloudozer LLP. All rights reserved.
  2. %%
  3. %% Redistribution and use in source and binary forms, with or without
  4. %% modification, are permitted provided that the following conditions are met:
  5. %%
  6. %% * Redistributions of source code must retain the above copyright notice, this
  7. %% list of conditions and the following disclaimer.
  8. %%
  9. %% * Redistributions in binary form must reproduce the above copyright notice,
  10. %% this list of conditions and the following disclaimer in the documentation
  11. %% and/or other materials provided with the distribution.
  12. %%
  13. %% * Redistributions in any form must be accompanied by information on how to
  14. %% obtain complete source code for the LING software and any accompanying
  15. %% software that uses the LING software. The source code must either be included
  16. %% in the distribution or be available for no more than the cost of distribution
  17. %% plus a nominal fee, and must be freely redistributable under reasonable
  18. %% conditions. For an executable file, complete source code means the source
  19. %% code for all modules it contains. It does not include source code for modules
  20. %% or files that typically accompany the major components of the operating
  21. %% system on which the executable file runs.
  22. %%
  23. %% THIS SOFTWARE IS PROVIDED BY CLOUDOZER LLP ``AS IS'' AND ANY EXPRESS OR
  24. %% IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  25. %% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE
  26. %% DISCLAIMED. IN NO EVENT SHALL CLOUDOZER LLP BE LIABLE FOR ANY DIRECT,
  27. %% INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  28. %% (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  29. %% LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  30. %% ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  31. %% (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  32. %% SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  33. -module(ling_lib).
  34. -export([abstract_code/1]).
  35. -export([specs_to_binary/1]).
  36. -include("ling_code.hrl").
  37. abstract_code(Ling) when is_list(Ling) -> %% {ok,Forms} | {error,Error}
  38. case file:read_file(Ling) of
  39. {ok,Bin} ->
  40. abstract_code(Bin);
  41. Err ->
  42. Err
  43. end;
  44. abstract_code(<<"FOR1",_Size:32,"LING",Chunks/binary>>) ->
  45. scan_chunks(Chunks);
  46. abstract_code(_) ->
  47. {error,badarg}.
  48. scan_chunks(<<"Abst",Sz:32,Data:(Sz)/binary,_/binary>>) ->
  49. {ok,binary_to_term(Data)};
  50. scan_chunks(<<_Tag:4/binary,Sz:32,_Data:(Sz)/binary,PadChunks/binary>>) ->
  51. PadSize = case Sz rem 4 of
  52. 0 -> 0;
  53. N -> 4 -N
  54. end,
  55. <<_Pad:(PadSize)/binary,Chunks/binary>> = PadChunks,
  56. scan_chunks(Chunks);
  57. scan_chunks(<<>>) ->
  58. no_abstract_code.
  59. %% (unsigned)
  60. %% opcode
  61. %% atom
  62. %% bif
  63. %% catch
  64. %% export
  65. %% reg_as_term
  66. %% slot_as_term
  67. %% tag_int
  68. %% literal
  69. %% label
  70. %% nolabel
  71. %% nil
  72. %% fu
  73. %% str
  74. %% 0xxxxxxx {opcode,7}
  75. %% 1000xxxx {reg_as_term,4}
  76. %% 1001xxxx {slot_as_term,4}
  77. %% 1010xxxx {unsigned,12} [1]
  78. %% 1011xxxx {unsigned,20} [2]
  79. %% 1100xxxx {atom,12} [1]
  80. %% 1101xxxx {export,12} [1]
  81. %% 111000xx {literal,10} [1]
  82. %% 111001xx {bif,10} [1]
  83. %% 111010xx {opcode,10} [1]
  84. %% 111011xx {tag_int,10} [1]
  85. %% 111100xx {fu,10} [1]
  86. %% 11110100 {catch,8} [1]
  87. %% 11110101 {str,8} [1]
  88. %% 11110110 (unused)
  89. %% 11110111 (unused)
  90. %% 11111000 {tag_int,32} [4]
  91. %% 11111001 {unsigned,32} [4]
  92. %% 11111010 {reg_as_term,8} [1]
  93. %% 11111011 {slot_as_term,8} [1]
  94. %% 11111100 nil
  95. %% 11111101 {f,none}
  96. %% 11111110 {f,16} [2]
  97. %% 11111111 (extended)
  98. %% extended tag (follows 255):
  99. %%
  100. %% 00000000 {f,32} [4]
  101. %% 00000001 {atom,32} [4]
  102. %% 00000010 {export,32} [4]
  103. %% 00000011 {literal,32} [4]
  104. %% 00000100 {bif,32} [4]
  105. %% 00000101 {fu,32} [4]
  106. %% 00000110 {catch,32} [4]
  107. %% 00000111 {str,32} [4]
  108. %% 00001000 {slot_as_term,32} [4]
  109. %% 00001001 {opcode,32} [4]
  110. %% ... (unused)
  111. %%
  112. specs_to_binary(Specs) -> %% {ok,Bin}
  113. L = [wrap("Atom", atoms_chunk(Specs)),
  114. wrap("ExpT", exports_chunk(Specs)),
  115. wrap("ImpT", imports_chunk(Specs)),
  116. wrap("Code", code_chunk(Specs)),
  117. wrap("CatT", catches_chunk(Specs)),
  118. wrap("FunT", lambdas_chunk(Specs)),
  119. wrap("StrT", strings_chunk(Specs)),
  120. wrap("LitT", literals_chunk(Specs)),
  121. wrap("Attr", attrs_chunk(Specs)),
  122. wrap("CInf", cinfo_chunk(Specs)),
  123. wrap("Line", line_chunk(Specs))]
  124. %%
  125. %% This increases the size of the image; make an option?
  126. %%
  127. ++ if Specs#m.abst_code =/= no_abstract_code ->
  128. [wrap("Abst", term_to_binary(Specs#m.abst_code))];
  129. true -> [] end,
  130. Chunks = list_to_binary(L),
  131. FormSize = byte_size(Chunks) + 4,
  132. <<"FOR1",FormSize:32,"LING",Chunks/binary>>.
  133. wrap(_, undefined) ->
  134. <<>>;
  135. wrap([A,B,C,D], Body) ->
  136. ChunkSize = byte_size(Body),
  137. AlignedSize = (ChunkSize + 3) band (bnot 3),
  138. PadSize = AlignedSize - ChunkSize,
  139. <<A,B,C,D,ChunkSize:32,Body/binary,0:PadSize/unit:8>>.
  140. atoms_chunk(#m{atoms=Atoms}) ->
  141. %mad:info("Atoms=~p~n", [Atoms]),
  142. L = [<<(length(Atoms)):32>>] ++
  143. [[length(atom_to_list(A))] ++ atom_to_list(A) || A <- Atoms],
  144. list_to_binary(L).
  145. exports_chunk(#m{atoms=Atoms,exports=Exports}) ->
  146. L = [<<(length(Exports)):32>>] ++
  147. [<<(ai(F, Atoms)):32,N:32,Off:32>> || {F,N,Off} <- Exports],
  148. list_to_binary(L).
  149. imports_chunk(#m{atoms=Atoms,imports=Imports}) ->
  150. L = [<<(length(Imports)):32>>] ++
  151. [<<(ai(M, Atoms)):32,(ai(F, Atoms)):32,N:32>> || {M,F,N} <- Imports],
  152. list_to_binary(L).
  153. code_chunk(#m{code=Code}) ->
  154. CodeSize = length(lists:concat(Code)),
  155. <<CodeSize:32,(encode(Code))/binary>>.
  156. catches_chunk(#m{catches=Catches}) ->
  157. L = [<<(length(Catches)):32>>] ++
  158. [<<Off:32>> || Off <- Catches],
  159. list_to_binary(L).
  160. lambdas_chunk(#m{atoms=Atoms,lambdas=Lambdas}) ->
  161. L = [<<(length(Lambdas)):32>>] ++
  162. [<<(ai(F, Atoms)):32,A:32,Off:32,Idx:32,NFree:32,Ou:32>>
  163. || {F,A,Off,Idx,NFree,Ou} <- Lambdas],
  164. list_to_binary(L).
  165. strings_chunk(#m{strings=StrTabBin}) ->
  166. StrTabBin.
  167. literals_chunk(#m{literals=Literals}) ->
  168. %mad:info("Literals=~p~n", [Literals]),
  169. EncLits = [term_to_binary(Lit) || Lit <- Literals],
  170. L = [<<(length(Literals)):32>>] ++
  171. [<<(byte_size(EncLit)):32,EncLit/binary>> || EncLit <- EncLits],
  172. list_to_binary(L).
  173. attrs_chunk(#m{attrs=undefined}) ->
  174. undefined;
  175. attrs_chunk(#m{attrs=Attrs}) ->
  176. term_to_binary(Attrs).
  177. cinfo_chunk(#m{compile_info=undefined}) ->
  178. undefined;
  179. cinfo_chunk(#m{compile_info=CInfo}) ->
  180. term_to_binary(CInfo).
  181. line_chunk(#m{line_info=undefined}) ->
  182. undefined;
  183. line_chunk(#m{atoms=Atoms,line_info={LineRefs,FileNames}}) ->
  184. PackedLineRefs = lists:map(fun({Offset,undefined}) ->
  185. {Offset,0};
  186. ({Offset,{nofile,Line}}) ->
  187. {Offset,Line}; %% does absense of file ref mean default file?
  188. ({Offset,{File,Line}}) ->
  189. {Offset,(File bsl 24) bor Line}
  190. end, LineRefs),
  191. L = [<<(length(LineRefs)):32,(length(FileNames)):32>>] ++
  192. [<<Offset:32,Location:32>> || {Offset,Location} <- PackedLineRefs] ++
  193. [<<(ai(Name, Atoms)):32>> || Name <- FileNames],
  194. list_to_binary(L).
  195. ai(A, As) ->
  196. ai(A, As, 0).
  197. ai(A, [A|_], I) -> I;
  198. ai(A, [_|As], I) ->
  199. ai(A, As, I+1).
  200. encode(Specs) ->
  201. list_to_binary([enc(S) || S <- lists:concat(Specs)]).
  202. enc({opcode,N}) when N < 128 ->
  203. <<0:1,N:7>>;
  204. enc({opcode,N}) when N < 1024 ->
  205. <<58:6,N:10>>;
  206. enc({opcode,N}) ->
  207. <<255,9,N:32>>;
  208. enc({reg_as_term,X}) when X < 16 ->
  209. <<8:4,X:4>>;
  210. enc({reg_as_term,X}) when X < 256 ->
  211. <<250,X>>;
  212. enc({slot_as_term,Y}) when Y < 16 ->
  213. <<9:4,Y:4>>;
  214. enc({slot_as_term,Y}) when Y < 256 ->
  215. <<251,Y>>;
  216. enc({slot_as_term,Y}) ->
  217. <<255,8,Y:32>>;
  218. enc(N) when is_integer(N), N >= 0, N < 4096 ->
  219. <<10:4,N:12>>;
  220. enc(N) when is_integer(N), N >= 0, N < 1048576 ->
  221. <<11:4,N:20>>;
  222. enc(N) when is_integer(N), N >= 0 ->
  223. <<249,N:32>>;
  224. enc({atom,N}) when N < 4096 ->
  225. <<12:4,N:12>>;
  226. enc({atom,N}) ->
  227. <<255,1,N:32>>;
  228. enc({export,none}) ->
  229. <<246>>; %% very rare - yet a single byte encoding
  230. enc({export,N}) when N < 4096 ->
  231. <<13:4,N:12>>;
  232. enc({export,N}) ->
  233. <<255,2,N:32>>;
  234. enc({literal,N}) when N < 1024 ->
  235. <<56:6,N:10>>;
  236. enc({literal,N}) ->
  237. <<255,3,N:32>>;
  238. enc({bif,N}) when N < 1024 ->
  239. <<57:6,N:10>>;
  240. enc({bif,N}) ->
  241. <<255,4,N:32>>;
  242. enc({tag_int,I}) when I >= -512, I < 512 ->
  243. <<59:6,I:10/signed>>;
  244. enc({tag_int,I}) ->
  245. <<248,I:32/signed>>;
  246. enc({fu,N}) when N < 1024 ->
  247. <<60:6,N:10>>;
  248. enc({fu,N}) ->
  249. <<255,5,N:32>>;
  250. enc({'catch',N}) when N < 256 ->
  251. <<244,N>>;
  252. enc({'catch',N}) ->
  253. <<255,6,N:32>>;
  254. enc({str,N}) when N < 256 ->
  255. <<245,N>>;
  256. enc({str,N}) ->
  257. <<255,7,N:32>>;
  258. enc(nil) ->
  259. <<252>>;
  260. enc({f,none}) ->
  261. <<253>>;
  262. enc({f,O}) when O < 65536 ->
  263. <<254,O:16>>;
  264. enc({f,O}) ->
  265. <<255,0,O:32>>.
  266. %%EOF