package org.jerlang; import java.util.Map; import org.jerlang.erts.emulator.Instruction; import org.jerlang.erts.erlang.Error; import org.jerlang.stdlib.beam_lib.BeamData; import org.jerlang.stdlib.beam_lib.CodeChunk; import org.jerlang.type.Atom; import org.jerlang.type.List; import org.jerlang.type.Term; /** * Executes opcodes */ public class Interpreter { private static final Atom LINE = Atom.of("line"); public static Term dispatch(List params) { // A call to a function defined in a BEAM file // has always the signature as first parameter. Process process = (Process) ProcessRegistry.self(); FunctionSignature s = process.signature(); params_to_register(params, process.registers()); Module m = ModuleRegistry.get(s.module()); int start = find_start(s, m.beamData()); return continueExecution(s, start); } public static Term continueExecution(FunctionSignature signature, int start) { Process process = (Process) ProcessRegistry.self(); Module m = ModuleRegistry.get(signature.module()); if (m.isInternal()) { // We need to fetch params from registers List args = List.nil; for (int index = signature.fun_arity().toInt(); index > 0; index--) { args = new List(process.getX(index - 1), args); } Term res = m.apply(signature, args); process.setX(0, res); // Setting the EXITING state must be the last // operation before return. Otherwise another // thread waiting for this process might get // wrong result as X0 is not filled yet. process.setState(ProcessState.EXITING); return res; } CodeChunk code = m.beamData().codeChunk(); java.util.List<Instruction> instructions = code.instructions(); Map<Integer, Integer> labels = code.labels(); int maxInstructions = instructions.size(); if (start == -1) { start = find_start(process.signature(), m.beamData()); } Term result = List.nil; Term params = List.nil; // TODO: is this correct? for (int index = start; index < maxInstructions; index++) { Instruction i = instructions.get(index); // process.printStack(); // System.out.println(i); if (LINE.equals(i.element(1))) { continue; } try { // System.out.println("" + process + ": " + String.format("%2d", index) + ": " + i); if (i.opcode().methodHandle() != null) { Term r = (Term) i.opcode().methodHandle().invoke(process, m, i, params); if (r != null) { if (r instanceof org.jerlang.type.Integer) { // CP index = r.toInteger().toInt(); if (index == 0) { // Return } } else { // this must be a label jump (e.g. "{f,1}") int lbl = r.toRegisterIndex().toInt(); index = labels.get(lbl); } } } else { System.err.println("Unsupported opcode: " + i.opcode()); break; } } catch (Error error) { if (process.exceptionHandler() != null) { // System.err.println(process.exceptionHandler()); int new_index = process.exceptionHandler().index(); if (new_index >= 0) { index = new_index; } process.exceptionHandler().handle(process, error); process.setExceptionHandler(null); } else { System.err.println("Raise catched"); error.printStackTrace(); System.exit(1); break; } } catch (Throwable e) { System.err.println("Index: " + index); e.printStackTrace(); System.exit(1); break; } if (process.state() == ProcessState.WAITING) { // Set process was set to waiting mode. process.setCP(index); break; } Opcode opcode = i.opcode(); if (opcode == Opcode.apply_last || opcode == Opcode.call_ext_last || opcode == Opcode.call_ext_only || (opcode == Opcode._return && index <= 0)) { // The result of the call or return // is stored in the x0 register result = process.registers()[0]; process.setState(ProcessState.EXITING); break; } } return result; } /** * Store the params, if any, in the X registers of the process. */ private static void params_to_register(List params, Term[] registers) { int index = 0; while (params.length() > 0) { registers[index++] = params.head(); params = params.tail(); } } /** * Looks for the exported functions. * On match, returns the index of the label instruction * that is assigned to the function. */ private static int find_start(FunctionSignature s, BeamData beamData) { for (FunctionSignature fs : beamData.exportTableChunk().exports()) { if (fs.equals(s)) { int lbl = fs.label().toInt(); int idx = beamData.codeChunk().labels().get(lbl); return idx; } } throw new Error("No label found for " + s); } }