package org.jerlang;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.nio.file.Files;
import java.util.Comparator;
import java.util.TreeMap;
import org.jerlang.erts.Erlang;
import org.jerlang.exception.ThrowException;
import org.jerlang.stdlib.BeamLib;
import org.jerlang.stdlib.beam_lib.AbstractSyntaxTreeChunk;
import org.jerlang.stdlib.beam_lib.AbstractSyntaxTreeChunkReader;
import org.jerlang.stdlib.beam_lib.AtomChunk;
import org.jerlang.stdlib.beam_lib.AtomChunkReader;
import org.jerlang.stdlib.beam_lib.AttributeChunk;
import org.jerlang.stdlib.beam_lib.AttributeChunkReader;
import org.jerlang.stdlib.beam_lib.BeamData;
import org.jerlang.stdlib.beam_lib.Chunk;
import org.jerlang.stdlib.beam_lib.ChunkId;
import org.jerlang.stdlib.beam_lib.CodeChunk;
import org.jerlang.stdlib.beam_lib.CodeChunkReader;
import org.jerlang.stdlib.beam_lib.CompileInfoChunk;
import org.jerlang.stdlib.beam_lib.CompileInfoChunkReader;
import org.jerlang.stdlib.beam_lib.ExportTableChunk;
import org.jerlang.stdlib.beam_lib.ExportTableChunkReader;
import org.jerlang.stdlib.beam_lib.FunctionTableChunk;
import org.jerlang.stdlib.beam_lib.FunctionTableChunkReader;
import org.jerlang.stdlib.beam_lib.ImportTableChunk;
import org.jerlang.stdlib.beam_lib.ImportTableChunkReader;
import org.jerlang.stdlib.beam_lib.LineTableChunk;
import org.jerlang.stdlib.beam_lib.LineTableChunkReader;
import org.jerlang.stdlib.beam_lib.LiteralTableChunk;
import org.jerlang.stdlib.beam_lib.LiteralTableChunkReader;
import org.jerlang.stdlib.beam_lib.LocalFunctionTableChunk;
import org.jerlang.stdlib.beam_lib.LocalFunctionTableChunkReader;
import org.jerlang.stdlib.beam_lib.StringTableChunk;
import org.jerlang.stdlib.beam_lib.StringTableChunkReader;
import org.jerlang.type.Atom;
import org.jerlang.type.List;
import org.jerlang.type.Str;
import org.jerlang.type.Term;
import org.jerlang.type.Tuple;
import org.jerlang.util.ByteUtil;
/**
* The ModuleLoader loads a BEAM file into a BeamData object and
* registers the loaded module at ModuleRegistry.
*/
public class ModuleLoader {
/**
* Try to load the module from file system
*/
public static void load(Atom module) {
String filename = module.toString() + ".beam";
Term info = BeamLib.info(Str.of(filename));
if (!(info instanceof List)) {
System.err.println("Can not load module: " + info);
return;
}
Module m = new Module(loadBeamData(filename, info.toList()), module);
ModuleRegistry.register(m);
m.export();
}
private static BeamData loadBeamData(String filename, List info) {
try {
AbstractSyntaxTreeChunk abstractSyntaxTreeChunk = null;
AtomChunk atomChunk = null;
AttributeChunk attributeChunk = null;
CodeChunk codeChunk = null;
CompileInfoChunk compileInfoChunk = null;
ExportTableChunk exportTableChunk = null;
FunctionTableChunk functionTableChunk = null;
ImportTableChunk importTableChunk = null;
LineTableChunk lineTableChunk = null;
LiteralTableChunk literalTableChunk = null;
LocalFunctionTableChunk localFunctionTableChunk = null;
StringTableChunk stringTableChunk = null;
byte[] bytes = ByteUtil.maybe_decompress(Files.readAllBytes(new File(filename).toPath()));
Tuple chunksTuple = get_chunks_tuple(info);
List chunkList = sort_chunk_list(chunksTuple.element(2).toList());
while (chunkList.length() > 0) {
Tuple chunkTuple = chunkList.head().toTuple();
int offset = chunkTuple.element(2).toInteger().toInt();
int length = chunkTuple.element(3).toInteger().toInt();
ChunkId chunkId = ChunkId.of(chunkTuple.element(1).toStr().string());
Chunk chunk = new Chunk(chunkId, offset, length);
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes));
dis.skipBytes(offset);
switch (chunkId) {
case ABST:
abstractSyntaxTreeChunk = new AbstractSyntaxTreeChunkReader(chunk, dis).read();
break;
case ATOM:
atomChunk = new AtomChunkReader(chunk, dis).read();
break;
case ATTR:
attributeChunk = new AttributeChunkReader(chunk, dis).read();
break;
case CINF:
compileInfoChunk = new CompileInfoChunkReader(chunk, dis).read();
break;
case CODE:
codeChunk = new CodeChunkReader(chunk, dis, atomChunk, literalTableChunk).read();
break;
case EXPT:
exportTableChunk = new ExportTableChunkReader(chunk, dis, atomChunk).read();
break;
case FUNT:
functionTableChunk = new FunctionTableChunkReader(chunk, dis, atomChunk).read();
break;
case IMPT:
importTableChunk = new ImportTableChunkReader(chunk, dis, atomChunk).read();
break;
case LINE:
lineTableChunk = new LineTableChunkReader(chunk, dis, atomChunk).read();
break;
case LITT:
literalTableChunk = new LiteralTableChunkReader(chunk, dis).read();
break;
case LOCT:
localFunctionTableChunk = new LocalFunctionTableChunkReader(chunk, dis, atomChunk).read();
break;
case STRT:
stringTableChunk = new StringTableChunkReader(chunk, dis).read();
break;
default:
break;
}
chunkList = chunkList.tail();
}
BeamData beamData = new BeamData(
abstractSyntaxTreeChunk,
atomChunk,
attributeChunk,
codeChunk,
compileInfoChunk,
exportTableChunk,
functionTableChunk,
importTableChunk,
lineTableChunk,
literalTableChunk,
localFunctionTableChunk,
stringTableChunk);
// System.out.println(beamData);
return beamData;
} catch (Throwable e) {
e.printStackTrace();
return null;
}
}
/**
* We need to sort the list of chunks so that chunks
* without dependencies (e.g. the atom chunk) are loaded first.
* Otherwise, loading of chunks depending on such chunks,
* like the code chunk, is not possible.
*/
private static List sort_chunk_list(List chunkList) {
TreeMap<ChunkId, Tuple> chunks = new TreeMap<>(new Comparator<ChunkId>() {
@Override
public int compare(ChunkId a, ChunkId b) {
return -Integer.compare(a.sortOrder(), b.sortOrder());
}
});
while (chunkList.length() > 0) {
Tuple chunkTuple = chunkList.head().toTuple();
ChunkId chunkId = ChunkId.of(chunkTuple.element(1).toStr().string());
chunks.put(chunkId, chunkTuple);
chunkList = chunkList.tail();
}
List result = List.of(chunks.values());
return result;
}
private static Tuple get_chunks_tuple(List info) {
Atom chunks = Atom.of("chunks");
while (info.length() > 0) {
Tuple tuple = info.head().toTuple();
if (chunks.equals(tuple.element(1))) {
return tuple;
}
info = info.tail();
}
return null;
}
public static void main(String[] args) throws ThrowException {
Erlang.apply(Atom.of("fun_test"), Atom.of("t1"), List.nil);
}
}