package org.jerlang.stdlib.beam_lib; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.jerlang.Opcode; import org.jerlang.erts.emulator.Instruction; /** * = The Code Chunk * * The Code Chunk is a mandatory chunk that stores the code for the module. * The Code Chunk is composed of a header followed by one or more function * definitions. * * == Header * * The Code Chunk Header is composed of 28 or more bytes. * This is the structure of the Code Chunk Header: * * [cols="1,1,6", options="header"] * |=== * |Length * |Value * |Description * * |4 bytes * |`Code` * |Magic number indicating the Code Chunk. * * |4 bytes * |size * |Code Chunk length in bytes * * |4 bytes * |info-size * |Length of the information fields before code. * This is for future expansion. * * |4 bytes * |version * |Instruction set version. * This is 0 for OTP R5-R15. * This will be incremented if the instructions are changed in incompatible * ways (instructions renumbered, argument types changed, etc.) * * |4 bytes * |opcode-max * |The highest opcode used in the code section. * This allows addition of new opcodes without incrementing the version * of the BEAM file. * * |4 bytes * |labels * |The number of labels. * This is a hint for the loader to help allocate the label table. * * |4 bytes * |function-count * |The number of functions contained in the Code Chunk. * |=== * * NOTE: The value in the labels field holding the number of labels must be * 1 greater than the number of labels in the BEAM file * (remembering that labels are indexed from zero). * This is because the 0 label is reserved for the undefined location. * The compiler must set the labels value equal to the highest label it uses +1. * * == Code Definition Format * * The code is simply defined as a block of code. * Special instructions identify where functions insert into the code. * Each operation is coded according to the opcodes defined in the * (not yet existing) Opcode section. * * == Requirements * * A BEAM file loader should not load a BEAM file if: * * * It does not understand the instruction set version of the BEAM file * * The opcode-max is higher than the greatest opcode that the loader comprehends * * Based on: * https://synrc.com/publications/cat/Functional%20Languages/Erlang/BEAM.pdf */ public class CodeChunk extends Chunk { private int numberOfLabels; private int functions; private ArrayList<Instruction> instructions; private Map<Integer, Integer> labels; public CodeChunk(Chunk chunk) { super(ChunkId.CODE, chunk); instructions = new ArrayList<>(); labels = new HashMap<>(); } public boolean add(Instruction instruction) { if (instruction.opcode().equals(Opcode.label)) { labels.put(instruction.arg(0).toInteger().toInt(), instructions.size()); } instructions.add(instruction); return instruction.opcode() != Opcode.int_code_end; } public List<Instruction> instructions() { return Collections.unmodifiableList(instructions); } public int functions() { return functions; } public Map<Integer, Integer> labels() { return Collections.unmodifiableMap(labels); } public int numberOfLabels() { return numberOfLabels; } public void setInfo(int labels, int functions) { this.numberOfLabels = labels; this.functions = functions; } @Override public String toString() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("{code_chunk,[\n "); for (Instruction i : instructions) { stringBuilder.append(i).append(",\n "); } if (instructions.size() > 0) { stringBuilder.setLength(stringBuilder.length() - 6); } stringBuilder.append("]}"); return stringBuilder.toString(); } }