<commit_before>package org.perl6.nqp.jast2bc;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;

import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

public class JASTToJVMBytecode {
    public static void main(String[] argv)
    {
        if (argv.length != 2)
            usage();
        
        try
        {
            BufferedReader in = new BufferedReader(new InputStreamReader(
                    new FileInputStream(argv[0]), "UTF-8"));
            JavaClass c = buildClassFrom(in);
            in.close();
            FileOutputStream fos = new FileOutputStream(argv[1]);
            fos.write(c.bytes);
            fos.close();
        }
        catch (Exception e)
        {
            System.err.println("Error: " + e.getMessage());
        }
        
    }
    
    public static JavaClass buildClassFromString(String in) {
        try {
            BufferedReader br = new BufferedReader(new StringReader(in));
            JavaClass c = buildClassFrom(br);
            return c;
        }
        catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    
    public static void writeClassFromString(String in, String filename) {
        try {
            BufferedReader br = new BufferedReader(new StringReader(in));
            JavaClass c = buildClassFrom(br);
            FileOutputStream fos = new FileOutputStream(filename);
            fos.write(c.bytes);
            fos.close();
        }
        catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    
    private static JavaClass buildClassFrom(BufferedReader in) throws Exception
    {
        // Read in class name, superclass and any fields.
        String curLine, className = null, superName = null, fileName = null;
        List<String> fieldLines = new ArrayList<String>();
        while ((curLine = in.readLine()) != null) {
            if (curLine.startsWith("+ class ")) {
                className = curLine.substring("+ class ".length());
            }
            else if (curLine.startsWith("+ super ")) {
                superName = curLine.substring("+ super ".length());
            }
            else if (curLine.startsWith("+ filename ")) {
            	fileName = curLine.substring("+ filename ".length());
            }
            else if (curLine.startsWith("+ field ")) {
                fieldLines.add(curLine.substring("+ field ".length()));
            }
            else if (curLine.equals("+ method")) {
                break;
            }
            else {
                throw new Exception("Cannot understand '" + curLine + "'");
            }
        }
        if (className == null)
            throw new Exception("Missing class name");
        if (superName == null)
            throw new Exception("Missing superclass name");

        className = className.replace('.', '/');
        superName = superName.replace('.', '/');
        
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
        cw.visit(Opcodes.V1_7, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, className, null, 
                superName, null);
        cw.visitSource(fileName, null);
        
        // Add the fields.
        for (String field : fieldLines) {
            String[] bits = field.split("\\s");
            
            cw.visitField(
                    bits[2].equals("static")
                        ? Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC
                        : Opcodes.ACC_PUBLIC, 
                    bits[0], processType(bits[1]).getDescriptor(), null, null);
        }
        
        // Process all of the methods.
        if (!curLine.equals("+ method"))
            throw new Exception("Expected method after class configuration");
        while (processMethod(in, cw, className))
            ;
        
        // Add empty constructor.
        MethodVisitor constructor = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
        constructor.visitCode();
        constructor.visitVarInsn(Opcodes.ALOAD, 0);
        constructor.visitMethodInsn(Opcodes.INVOKESPECIAL, 
                superName, "<init>", "()V");
        constructor.visitInsn(Opcodes.RETURN);
        constructor.visitMaxs(1, 1);
        constructor.visitEnd();

        cw.visitEnd();
        
        JavaClass c = new JavaClass(className, cw.toByteArray());
        return c;
    }
    
    private static boolean processMethod(BufferedReader in, ClassWriter c, String className) throws Exception {
        String curLine, methodName = null, returnType = null;
        String crName = null, crCuid = null, crOuter = null;
        boolean isStatic = false;
        List<String> argNames = new ArrayList<String>();
        List<Type> argTypes = new ArrayList<Type>();
        Map<String, Integer> argIndexes = new HashMap<String, Integer>();
        Map<String, VariableDef> localVariables = new HashMap<String, VariableDef>();
        Map<String, Label> labelMap = new HashMap<String, Label>();
        Stack<Label> tryStartStack = new Stack<Label>();
        Stack<Label> tryEndStack = new Stack<Label>();
        List<String> croLex = new ArrayList<String>();
        List<String> criLex = new ArrayList<String>();
        List<String> crnLex = new ArrayList<String>();
        List<String> crsLex = new ArrayList<String>();
        Label beginAll = new Label();
        Label endAll = new Label();
        long[] crHandlers = null;
        int curArgIndex = 1;
        
        MethodVisitor m = null;
        boolean contAfter = false;
        
        boolean inMethodHeader = true;
        while ((curLine = in.readLine()) != null) {
            // See if we need to move to the next method.
            if (curLine.equals("+ method")) {
                if (inMethodHeader)
                    throw new Exception("Unexpected + method in method header");
                contAfter = true;
                break;
            }
            
            // Is it a header line?
            if (curLine.startsWith("++ ")) {
                if (!inMethodHeader)
                    throw new Exception("Unexpected method header directive: " + curLine);
                if (curLine.startsWith("++ name "))
                    methodName = curLine.substring("++ name ".length());
                else if (curLine.startsWith("++ returns "))
                    returnType = curLine.substring("++ returns ".length());
                else if (curLine.equals("++ static")) {
                    isStatic = true;
                    curArgIndex = 0; /* No invocant. */
                }
                else if (curLine.startsWith("++ arg ")) {
                    String[] bits = curLine.split("\\s", 4);
                    Type t = processType(bits[3]);
                    argNames.add(bits[2]);
                    argTypes.add(t);
                    argIndexes.put(bits[2], curArgIndex);
                    curArgIndex += (t == Type.LONG_TYPE || t == Type.DOUBLE_TYPE ? 2 : 1);
                }
                else if (curLine.startsWith("++ local ")) {
                    String[] bits = curLine.split("\\s", 4);
                    if (localVariables.containsKey(bits[2]))
                        throw new Exception("Duplicate local name: " + bits[2]);
                    Type t = processType(bits[3]);
                    localVariables.put(bits[2], new VariableDef(curArgIndex, t.getDescriptor(), beginAll, endAll));
                    curArgIndex += (t == Type.LONG_TYPE || t == Type.DOUBLE_TYPE ? 2 : 1);
                }
                else if (curLine.startsWith("++ crname "))
                    crName = curLine.substring("++ crname ".length());
                else if (curLine.startsWith("++ crcuid "))
                    crCuid = curLine.substring("++ crcuid ".length());
                else if (curLine.startsWith("++ crouter "))
                    crOuter = curLine.substring("++ crouter ".length());
                else if (curLine.startsWith("++ olex "))
                    croLex.add(curLine.substring("++ olex ".length()));
                else if (curLine.startsWith("++ ilex "))
                    criLex.add(curLine.substring("++ ilex ".length()));
                else if (curLine.startsWith("++ nlex "))
                    crnLex.add(curLine.substring("++ nlex ".length()));
                else if (curLine.startsWith("++ slex "))
                    crsLex.add(curLine.substring("++ slex ".length()));
                else if (curLine.startsWith("++ handlers ")) {
                    String suffix = curLine.substring("++ handlers ".length());
                    if (!suffix.equals("")) {
                        String[] longStrs = suffix.split("\\s");
                        crHandlers = new long[longStrs.length];
                        for (int i = 0; i < longStrs.length; i++)
                            crHandlers[i] = Long.parseLong(longStrs[i]);
                    }
                    else {
                        crHandlers = new long[0];
                    }
                }
                else
                    throw new Exception("Cannot understand '" + curLine + "'");
                continue;
            }
            
            // Otherwise, we have an instruction. If we've been in the method
            // header, this will be the first instruction also.
            if (inMethodHeader) {
                // Transition to instructions mode.
                inMethodHeader = false;
                
                // Create method object.
                String desc = Type.getMethodDescriptor(processType(returnType), argTypes.toArray(new Type[0]));
                m = c.visitMethod(
                        (isStatic
                            ? Opcodes.ACC_STATIC | Opcodes.ACC_PUBLIC
                            : Opcodes.ACC_PUBLIC), 
                            methodName, desc, null, null);
                                  
                 // Add code ref info annotation.
                 if (crCuid != null && !crCuid.equals("")) {
                    Type crAnnType = Type.getType("Lorg/perl6/nqp/runtime/CodeRefAnnotation;");
                    AnnotationVisitor av = m.visitAnnotation(crAnnType.getDescriptor(), true);
                    av.visit("name", crName);
                    av.visit("cuid", crCuid);
                    av.visit("outerCuid", crOuter);
                    
                    AnnotationVisitor avLex;
                    avLex = av.visitArray("oLexicalNames");
                    for (int i = 0; i < croLex.size(); i++)
                        avLex.visit(null, croLex.get(i));
                    avLex.visitEnd();
                    avLex = av.visitArray("iLexicalNames");
                    for (int i = 0; i < criLex.size(); i++)
                        avLex.visit(null, criLex.get(i));
                    avLex.visitEnd();
                    avLex = av.visitArray("nLexicalNames");
                    for (int i = 0; i < crnLex.size(); i++)
                        avLex.visit(null, crnLex.get(i));
                    avLex.visitEnd();
                    avLex = av.visitArray("sLexicalNames");
                    for (int i = 0; i < crsLex.size(); i++)
                        avLex.visit(null, crsLex.get(i));
                    avLex.visitEnd();
                    
                    av.visit("handlers", crHandlers);
                    av.visitEnd();
                 }
                 
                 m.visitCode();
                 m.visitLabel(beginAll);
            }
            
            // Check if it's a label.
            if (curLine.startsWith(":")) {
                String labelName = curLine.substring(1);
                if (!labelMap.containsKey(labelName))
                    labelMap.put(labelName, new Label());
                m.visitLabel(labelMap.get(labelName));
                continue;
            }
            
            // Check if it's some other kind of directive.
            if (curLine.startsWith(".")) {
                if (curLine.startsWith(".push_ic ")) {
                    Long value = Long.parseLong(curLine.substring(".push_ic ".length()));
                    m.visitLdcInsn(value);
                }
                else if (curLine.startsWith(".push_nc ")) {
                    Double value = Double.parseDouble(curLine.substring(".push_nc ".length()));
                    m.visitLdcInsn(value);
                }
                else if (curLine.startsWith(".push_sc ")) {
                    String value = curLine.substring(".push_sc ".length());
                    m.visitLdcInsn(decodeString(value));
                }
                else if (curLine.startsWith(".push_cc ")) {
                    String cName = curLine.substring(".push_cc ".length());
                    Type t = Type.getType(cName);
                    m.visitLdcInsn(t);
                }
                else if (curLine.startsWith(".push_idx ")) {
                    Integer value = Integer.parseInt(curLine.substring(".push_idx ".length()));
                    if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
                        m.visitIntInsn(Opcodes.BIPUSH, value);
                    }
                    else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
                        m.visitIntInsn(Opcodes.SIPUSH, value);
                    }
                    else {
                        m.visitLdcInsn(value);
                    }
                }
                else if (curLine.equals(".try")) {
                    Label start = new Label();
                    m.visitLabel(start);
                    tryStartStack.push(start);
                }
                else if (curLine.startsWith(".catch ")) {
                    Label afterCatch = new Label();
                    m.visitJumpInsn(Opcodes.GOTO, afterCatch);
                    tryEndStack.push(afterCatch);
                    
                    String typeName = curLine.substring(".catch ".length());
                    if (typeName.equals(""))
                        typeName = null;
                    else
                        typeName = typeName.substring(1, typeName.length() - 1); 
                    Label start = tryStartStack.peek();
                    Label end = new Label();
                    Label handler = new Label();
                    m.visitLabel(end);
                    m.visitLabel(handler);
                    m.visitTryCatchBlock(start, end, handler, typeName);
                }
                else if (curLine.equals(".endtry")) {
                    tryStartStack.pop();
                    m.visitLabel(tryEndStack.pop());
                }
                else if (curLine.startsWith(".line ")) {
                	int lineNumber = Integer.parseInt(curLine.substring(".line ".length()), 10);
                	Label l = new Label();
                	m.visitLabel(l);
                	m.visitLineNumber(lineNumber, l);
                }
                else {
                    throw new Exception("Don't understand directive: " + curLine);
                }
                continue;
            }
            
            // Process line as an instruction.
            emitInstruction(in, m, labelMap, argIndexes, localVariables, curLine);
        }
        if (inMethodHeader)
            throw new Exception("Unexpected end of file in method header");

        // Add locals.
        m.visitLabel(endAll);
        for (Map.Entry<String, VariableDef> e : localVariables.entrySet()) {
            VariableDef def = e.getValue();
            m.visitLocalVariable(e.getKey(), def.type, null, def.start, def.end, def.index);
        }
        if (!isStatic)
            m.visitLocalVariable("this", "L"+className+";", null, beginAll, endAll, 0);
        for (int i = 0; i < argTypes.size(); i++) {
            m.visitLocalVariable(argNames.get(i), argTypes.get(i).getDescriptor(), null,
                    beginAll, endAll, argIndexes.get(argNames.get(i)));
        }

        m.visitMaxs(0, 0);
        m.visitEnd();
        return contAfter;
    }
    
    private static String decodeString(String value) {
        StringBuilder sb = new StringBuilder(value.length());
        for (int i = 0; i < value.length(); i++) {
            char ch = value.charAt(i);
            if (ch == '\\') {
                i++;
                switch (value.charAt(i)) {
                case '\\': sb.append('\\'); break;
                case 'n': sb.append('\n'); break;
                case 'r': sb.append('\r'); break;
                case 't': sb.append('\t'); break;
                default:
                    new RuntimeException("Invalid string literal");
                }
            }
            else {
                sb.append(ch);
            }
        }
        return sb.toString();
    }

    private static void emitInstruction(BufferedReader in, MethodVisitor m,
            Map<String, Label> labelMap,
            Map<String, Integer> argIndexes,
            Map<String, VariableDef> localVariables,
            String curLine) throws Exception {
        // Find instruction code and get rest of the string.
        int endIns = curLine.indexOf(" ");
        String rest = "";
        if (endIns < 0)
            endIns = curLine.length();
        else
            rest = curLine.substring(endIns + 1);
        int instruction = Integer.parseInt(curLine.substring(0, endIns));

        // Go by instruction.
        switch (instruction) {
        case 0x00: // nop
        case 0x01: //aconst_null
        case 0x02: // iconst_m1
        case 0x03: // iconst_0
        case 0x04: // iconst_1
        case 0x05: // iconst_2
        case 0x06: // iconst_3
        case 0x07: // iconst_4
        case 0x08: // iconst_5
        case 0x09: // lconst_0
        case 0x0a: // lconst_1
        case 0x0b: // fconst_0
        case 0x0c: // fconst_1
        case 0x0d: // fconst_2
        case 0x0e: // dconst_0
        case 0x0f: // dconst_1
            m.visitInsn(instruction);
            break;
        case 0x15: // iload
        case 0x16: // lload
        case 0x17: // fload
        case 0x18: // dload
        case 0x19: // aload
            if (localVariables.containsKey(rest))
                m.visitVarInsn(instruction, localVariables.get(rest).index);
            else if (argIndexes.containsKey(rest))
                m.visitVarInsn(instruction, argIndexes.get(rest));
            else
                throw new Exception("Undeclared local variable: " + rest);
            break;
        case 0x1a: // iload_0
            m.visitVarInsn(Opcodes.ILOAD, 0);
            break;
        case 0x1b: // iload_1
            m.visitVarInsn(Opcodes.ILOAD, 1);
            break;
        case 0x1c: // iload_2
            m.visitVarInsn(Opcodes.ILOAD, 2);
            break;
        case 0x1d: // iload_3
            m.visitVarInsn(Opcodes.ILOAD, 3);
            break;
        case 0x1e: // lload_0
            m.visitVarInsn(Opcodes.LLOAD, 0);
            break;
        case 0x1f: // lload_1
            m.visitVarInsn(Opcodes.LLOAD, 1);
            break;
        case 0x20: // lload_2
            m.visitVarInsn(Opcodes.LLOAD, 2);
            break;
        case 0x21: // lload_3
            m.visitVarInsn(Opcodes.LLOAD, 3);
            break;
        case 0x22: // fload_0
            m.visitVarInsn(Opcodes.FLOAD, 0);
            break;
        case 0x23: // fload_1
            m.visitVarInsn(Opcodes.FLOAD, 1);
            break;
        case 0x24: // fload_2
            m.visitVarInsn(Opcodes.FLOAD, 2);
            break;
        case 0x25: // fload_3
            m.visitVarInsn(Opcodes.FLOAD, 3);
            break;
        case 0x26: // dload_0
            m.visitVarInsn(Opcodes.DLOAD, 0);
            break;
        case 0x27: // dload_1
            m.visitVarInsn(Opcodes.DLOAD, 1);
            break;
        case 0x28: // dload_2
            m.visitVarInsn(Opcodes.DLOAD, 2);
            break;
        case 0x29: // dload_3
            m.visitVarInsn(Opcodes.DLOAD, 3);
            break;
        case 0x2a: // aload_0
            m.visitVarInsn(Opcodes.ALOAD, 0);
            break;
        case 0x2b: // aload_1
            m.visitVarInsn(Opcodes.ALOAD, 1);
            break;
        case 0x2c: // aload_2
            m.visitVarInsn(Opcodes.ALOAD, 2);
            break;
        case 0x2d: // aload_3
            m.visitVarInsn(Opcodes.ALOAD, 3);
            break;
        case 0x2e: // iaload
        case 0x2f: // laload
        case 0x30: // faload
        case 0x31: // daload
        case 0x32: // aaload
        case 0x33: // baload
        case 0x34: // caload
        case 0x35: // saload
            m.visitInsn(instruction);
            break;
        case 0x36: // istore
        case 0x37: // lstore
        case 0x38: // fstore
        case 0x39: // dstore
        case 0x3a: // astore
            if (localVariables.containsKey(rest))
                m.visitVarInsn(instruction, localVariables.get(rest).index);
            else if (argIndexes.containsKey(rest))
                m.visitVarInsn(instruction, argIndexes.get(rest));
            else
                throw new Exception("Undeclared local variable: " + rest);
            break;
        case 0x3b: // istore_0
            m.visitVarInsn(Opcodes.ISTORE, 0);
            break;
        case 0x3c: // istore_1
            m.visitVarInsn(Opcodes.ISTORE, 1);
            break;
        case 0x3d: // istore_2
            m.visitVarInsn(Opcodes.ISTORE, 2);
            break;
        case 0x3e: // istore_3
            m.visitVarInsn(Opcodes.ISTORE, 3);
            break;
        case 0x3f: // lstore_0
            m.visitVarInsn(Opcodes.LSTORE, 0);
            break;
        case 0x40: // lstore_1
            m.visitVarInsn(Opcodes.LSTORE, 1);
            break;
        case 0x41: // lstore_2
            m.visitVarInsn(Opcodes.LSTORE, 2);
            break;<commit_msg>Refactor arg and variable handling for local variable annotations<commit_after>package org.perl6.nqp.jast2bc;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;

import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

public class JASTToJVMBytecode {
    public static void main(String[] argv)
    {
        if (argv.length != 2)
            usage();
        
        try
        {
            BufferedReader in = new BufferedReader(new InputStreamReader(
                    new FileInputStream(argv[0]), "UTF-8"));
            JavaClass c = buildClassFrom(in);
            in.close();
            FileOutputStream fos = new FileOutputStream(argv[1]);
            fos.write(c.bytes);
            fos.close();
        }
        catch (Exception e)
        {
            System.err.println("Error: " + e.getMessage());
        }
        
    }
    
    public static JavaClass buildClassFromString(String in) {
        try {
            BufferedReader br = new BufferedReader(new StringReader(in));
            JavaClass c = buildClassFrom(br);
            return c;
        }
        catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    
    public static void writeClassFromString(String in, String filename) {
        try {
            BufferedReader br = new BufferedReader(new StringReader(in));
            JavaClass c = buildClassFrom(br);
            FileOutputStream fos = new FileOutputStream(filename);
            fos.write(c.bytes);
            fos.close();
        }
        catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    
    private static JavaClass buildClassFrom(BufferedReader in) throws Exception
    {
        // Read in class name, superclass and any fields.
        String curLine, className = null, superName = null, fileName = null;
        List<String> fieldLines = new ArrayList<String>();
        while ((curLine = in.readLine()) != null) {
            if (curLine.startsWith("+ class ")) {
                className = curLine.substring("+ class ".length());
            }
            else if (curLine.startsWith("+ super ")) {
                superName = curLine.substring("+ super ".length());
            }
            else if (curLine.startsWith("+ filename ")) {
            	fileName = curLine.substring("+ filename ".length());
            }
            else if (curLine.startsWith("+ field ")) {
                fieldLines.add(curLine.substring("+ field ".length()));
            }
            else if (curLine.equals("+ method")) {
                break;
            }
            else {
                throw new Exception("Cannot understand '" + curLine + "'");
            }
        }
        if (className == null)
            throw new Exception("Missing class name");
        if (superName == null)
            throw new Exception("Missing superclass name");

        className = className.replace('.', '/');
        superName = superName.replace('.', '/');
        
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
        cw.visit(Opcodes.V1_7, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, className, null, 
                superName, null);
        cw.visitSource(fileName, null);
        
        // Add the fields.
        for (String field : fieldLines) {
            String[] bits = field.split("\\s");
            
            cw.visitField(
                    bits[2].equals("static")
                        ? Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC
                        : Opcodes.ACC_PUBLIC, 
                    bits[0], processType(bits[1]).getDescriptor(), null, null);
        }
        
        // Process all of the methods.
        if (!curLine.equals("+ method"))
            throw new Exception("Expected method after class configuration");
        while (processMethod(in, cw, className))
            ;
        
        // Add empty constructor.
        MethodVisitor constructor = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
        constructor.visitCode();
        constructor.visitVarInsn(Opcodes.ALOAD, 0);
        constructor.visitMethodInsn(Opcodes.INVOKESPECIAL, 
                superName, "<init>", "()V");
        constructor.visitInsn(Opcodes.RETURN);
        constructor.visitMaxs(1, 1);
        constructor.visitEnd();

        cw.visitEnd();
        
        JavaClass c = new JavaClass(className, cw.toByteArray());
        return c;
    }
    
    private static boolean processMethod(BufferedReader in, ClassWriter c, String className) throws Exception {
        String curLine, methodName = null, returnType = null;
        String crName = null, crCuid = null, crOuter = null;
        boolean isStatic = false;
        List<Type> argTypes = new ArrayList<Type>();
        Map<String, VariableDef> localVariables = new HashMap<String, VariableDef>();
        Map<String, Label> labelMap = new HashMap<String, Label>();
        Stack<Label> tryStartStack = new Stack<Label>();
        Stack<Label> tryEndStack = new Stack<Label>();
        List<String> croLex = new ArrayList<String>();
        List<String> criLex = new ArrayList<String>();
        List<String> crnLex = new ArrayList<String>();
        List<String> crsLex = new ArrayList<String>();
        Label beginAll = new Label();
        Label endAll = new Label();
        long[] crHandlers = null;
        int curArgIndex = 1;
        
        MethodVisitor m = null;
        boolean contAfter = false;
        boolean inMethodHeader = true;
        while ((curLine = in.readLine()) != null) {
            // See if we need to move to the next method.
            if (curLine.equals("+ method")) {
                if (inMethodHeader)
                    throw new Exception("Unexpected + method in method header");
                contAfter = true;
                break;
            }
            
            // Is it a header line?
            if (curLine.startsWith("++ ")) {
                if (!inMethodHeader)
                    throw new Exception("Unexpected method header directive: " + curLine);
                if (curLine.startsWith("++ name "))
                    methodName = curLine.substring("++ name ".length());
                else if (curLine.startsWith("++ returns "))
                    returnType = curLine.substring("++ returns ".length());
                else if (curLine.equals("++ static")) {
                    isStatic = true;
                    curArgIndex = 0; /* No invocant. */
                }
                else if (curLine.startsWith("++ local ") || curLine.startsWith("++ arg")) {
                    String[] bits = curLine.split("\\s", 4);
                    if (localVariables.containsKey(bits[2]))
                        throw new Exception("Duplicate local name: " + bits[2]);
                    Type t = processType(bits[3]);
                    localVariables.put(bits[2], new VariableDef(curArgIndex, t.getDescriptor(), beginAll, endAll));
                    curArgIndex += (t == Type.LONG_TYPE || t == Type.DOUBLE_TYPE ? 2 : 1);
                    if (curLine.startsWith("++ arg")) {
                    	argTypes.add(t);
                    }
                }
                else if (curLine.startsWith("++ crname "))
                    crName = curLine.substring("++ crname ".length());
                else if (curLine.startsWith("++ crcuid "))
                    crCuid = curLine.substring("++ crcuid ".length());
                else if (curLine.startsWith("++ crouter "))
                    crOuter = curLine.substring("++ crouter ".length());
                else if (curLine.startsWith("++ olex "))
                    croLex.add(curLine.substring("++ olex ".length()));
                else if (curLine.startsWith("++ ilex "))
                    criLex.add(curLine.substring("++ ilex ".length()));
                else if (curLine.startsWith("++ nlex "))
                    crnLex.add(curLine.substring("++ nlex ".length()));
                else if (curLine.startsWith("++ slex "))
                    crsLex.add(curLine.substring("++ slex ".length()));
                else if (curLine.startsWith("++ handlers ")) {
                    String suffix = curLine.substring("++ handlers ".length());
                    if (!suffix.equals("")) {
                        String[] longStrs = suffix.split("\\s");
                        crHandlers = new long[longStrs.length];
                        for (int i = 0; i < longStrs.length; i++)
                            crHandlers[i] = Long.parseLong(longStrs[i]);
                    }
                    else {
                        crHandlers = new long[0];
                    }
                }
                else
                    throw new Exception("Cannot understand '" + curLine + "'");
                continue;
            }
            
            // Otherwise, we have an instruction. If we've been in the method
            // header, this will be the first instruction also.
            if (inMethodHeader) {
                // Transition to instructions mode.
                inMethodHeader = false;
                
                // Create method object.
                String desc = Type.getMethodDescriptor(processType(returnType), argTypes.toArray(new Type[0]));
                m = c.visitMethod(
                        (isStatic
                            ? Opcodes.ACC_STATIC | Opcodes.ACC_PUBLIC
                            : Opcodes.ACC_PUBLIC), 
                            methodName, desc, null, null);
                                  
                 // Add code ref info annotation.
                 if (crCuid != null && !crCuid.equals("")) {
                    Type crAnnType = Type.getType("Lorg/perl6/nqp/runtime/CodeRefAnnotation;");
                    AnnotationVisitor av = m.visitAnnotation(crAnnType.getDescriptor(), true);
                    av.visit("name", crName);
                    av.visit("cuid", crCuid);
                    av.visit("outerCuid", crOuter);
                    
                    AnnotationVisitor avLex;
                    avLex = av.visitArray("oLexicalNames");
                    for (int i = 0; i < croLex.size(); i++)
                        avLex.visit(null, croLex.get(i));
                    avLex.visitEnd();
                    avLex = av.visitArray("iLexicalNames");
                    for (int i = 0; i < criLex.size(); i++)
                        avLex.visit(null, criLex.get(i));
                    avLex.visitEnd();
                    avLex = av.visitArray("nLexicalNames");
                    for (int i = 0; i < crnLex.size(); i++)
                        avLex.visit(null, crnLex.get(i));
                    avLex.visitEnd();
                    avLex = av.visitArray("sLexicalNames");
                    for (int i = 0; i < crsLex.size(); i++)
                        avLex.visit(null, crsLex.get(i));
                    avLex.visitEnd();
                    
                    av.visit("handlers", crHandlers);
                    av.visitEnd();
                 }

                 if (!isStatic) {
                	 localVariables.put("this", new VariableDef(0, "L"+className+";", beginAll, endAll));
                 }                 

                 m.visitCode();
                 m.visitLabel(beginAll);
            }
            
            // Check if it's a label.
            if (curLine.startsWith(":")) {
                String labelName = curLine.substring(1);
                if (!labelMap.containsKey(labelName))
                    labelMap.put(labelName, new Label());
                m.visitLabel(labelMap.get(labelName));
                continue;
            }
            
            // Check if it's some other kind of directive.
            if (curLine.startsWith(".")) {
                if (curLine.startsWith(".push_ic ")) {
                    Long value = Long.parseLong(curLine.substring(".push_ic ".length()));
                    m.visitLdcInsn(value);
                }
                else if (curLine.startsWith(".push_nc ")) {
                    Double value = Double.parseDouble(curLine.substring(".push_nc ".length()));
                    m.visitLdcInsn(value);
                }
                else if (curLine.startsWith(".push_sc ")) {
                    String value = curLine.substring(".push_sc ".length());
                    m.visitLdcInsn(decodeString(value));
                }
                else if (curLine.startsWith(".push_cc ")) {
                    String cName = curLine.substring(".push_cc ".length());
                    Type t = Type.getType(cName);
                    m.visitLdcInsn(t);
                }
                else if (curLine.startsWith(".push_idx ")) {
                    Integer value = Integer.parseInt(curLine.substring(".push_idx ".length()));
                    if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
                        m.visitIntInsn(Opcodes.BIPUSH, value);
                    }
                    else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
                        m.visitIntInsn(Opcodes.SIPUSH, value);
                    }
                    else {
                        m.visitLdcInsn(value);
                    }
                }
                else if (curLine.equals(".try")) {
                    Label start = new Label();
                    m.visitLabel(start);
                    tryStartStack.push(start);
                }
                else if (curLine.startsWith(".catch ")) {
                    Label afterCatch = new Label();
                    m.visitJumpInsn(Opcodes.GOTO, afterCatch);
                    tryEndStack.push(afterCatch);
                    
                    String typeName = curLine.substring(".catch ".length());
                    if (typeName.equals(""))
                        typeName = null;
                    else
                        typeName = typeName.substring(1, typeName.length() - 1); 
                    Label start = tryStartStack.peek();
                    Label end = new Label();
                    Label handler = new Label();
                    m.visitLabel(end);
                    m.visitLabel(handler);
                    m.visitTryCatchBlock(start, end, handler, typeName);
                }
                else if (curLine.equals(".endtry")) {
                    tryStartStack.pop();
                    m.visitLabel(tryEndStack.pop());
                }
                else if (curLine.startsWith(".line ")) {
                	int lineNumber = Integer.parseInt(curLine.substring(".line ".length()), 10);
                	Label l = new Label();
                	m.visitLabel(l);
                	m.visitLineNumber(lineNumber, l);
                }
                else {
                    throw new Exception("Don't understand directive: " + curLine);
                }
                continue;
            }
            
            // Process line as an instruction.
            emitInstruction(in, m, labelMap, localVariables, curLine);
        }
        if (inMethodHeader)
            throw new Exception("Unexpected end of file in method header");

        // Add locals.
        m.visitLabel(endAll);
        for (Map.Entry<String, VariableDef> e : localVariables.entrySet()) {
            VariableDef def = e.getValue();
            m.visitLocalVariable(e.getKey(), def.type, null, def.start, def.end, def.index);
        }

        m.visitMaxs(0, 0);
        m.visitEnd();
        return contAfter;
    }
    
    private static String decodeString(String value) {
        StringBuilder sb = new StringBuilder(value.length());
        for (int i = 0; i < value.length(); i++) {
            char ch = value.charAt(i);
            if (ch == '\\') {
                i++;
                switch (value.charAt(i)) {
                case '\\': sb.append('\\'); break;
                case 'n': sb.append('\n'); break;
                case 'r': sb.append('\r'); break;
                case 't': sb.append('\t'); break;
                default:
                    new RuntimeException("Invalid string literal");
                }
            }
            else {
                sb.append(ch);
            }
        }
        return sb.toString();
    }

    private static void emitInstruction(BufferedReader in, MethodVisitor m,
            Map<String, Label> labelMap,
            Map<String, VariableDef> localVariables,
            String curLine) throws Exception {
        // Find instruction code and get rest of the string.
        int endIns = curLine.indexOf(" ");
        String rest = "";
        if (endIns < 0)
            endIns = curLine.length();
        else
            rest = curLine.substring(endIns + 1);
        int instruction = Integer.parseInt(curLine.substring(0, endIns));

        // Go by instruction.
        switch (instruction) {
        case 0x00: // nop
        case 0x01: //aconst_null
        case 0x02: // iconst_m1
        case 0x03: // iconst_0
        case 0x04: // iconst_1
        case 0x05: // iconst_2
        case 0x06: // iconst_3
        case 0x07: // iconst_4
        case 0x08: // iconst_5
        case 0x09: // lconst_0
        case 0x0a: // lconst_1
        case 0x0b: // fconst_0
        case 0x0c: // fconst_1
        case 0x0d: // fconst_2
        case 0x0e: // dconst_0
        case 0x0f: // dconst_1
            m.visitInsn(instruction);
            break;
        case 0x15: // iload
        case 0x16: // lload
        case 0x17: // fload
        case 0x18: // dload
        case 0x19: // aload
            if (localVariables.containsKey(rest))
                m.visitVarInsn(instruction, localVariables.get(rest).index);
            else
                throw new Exception("Undeclared local variable: " + rest);
            break;
        case 0x1a: // iload_0
            m.visitVarInsn(Opcodes.ILOAD, 0);
            break;
        case 0x1b: // iload_1
            m.visitVarInsn(Opcodes.ILOAD, 1);
            break;
        case 0x1c: // iload_2
            m.visitVarInsn(Opcodes.ILOAD, 2);
            break;
        case 0x1d: // iload_3
            m.visitVarInsn(Opcodes.ILOAD, 3);
            break;
        case 0x1e: // lload_0
            m.visitVarInsn(Opcodes.LLOAD, 0);
            break;
        case 0x1f: // lload_1
            m.visitVarInsn(Opcodes.LLOAD, 1);
            break;
        case 0x20: // lload_2
            m.visitVarInsn(Opcodes.LLOAD, 2);
            break;
        case 0x21: // lload_3
            m.visitVarInsn(Opcodes.LLOAD, 3);
            break;
        case 0x22: // fload_0
            m.visitVarInsn(Opcodes.FLOAD, 0);
            break;
        case 0x23: // fload_1
            m.visitVarInsn(Opcodes.FLOAD, 1);
            break;
        case 0x24: // fload_2
            m.visitVarInsn(Opcodes.FLOAD, 2);
            break;
        case 0x25: // fload_3
            m.visitVarInsn(Opcodes.FLOAD, 3);
            break;
        case 0x26: // dload_0
            m.visitVarInsn(Opcodes.DLOAD, 0);
            break;
        case 0x27: // dload_1
            m.visitVarInsn(Opcodes.DLOAD, 1);
            break;
        case 0x28: // dload_2
            m.visitVarInsn(Opcodes.DLOAD, 2);
            break;
        case 0x29: // dload_3
            m.visitVarInsn(Opcodes.DLOAD, 3);
            break;
        case 0x2a: // aload_0
            m.visitVarInsn(Opcodes.ALOAD, 0);
            break;
        case 0x2b: // aload_1
            m.visitVarInsn(Opcodes.ALOAD, 1);
            break;
        case 0x2c: // aload_2
            m.visitVarInsn(Opcodes.ALOAD, 2);
            break;
        case 0x2d: // aload_3
            m.visitVarInsn(Opcodes.ALOAD, 3);
            break;
        case 0x2e: // iaload
        case 0x2f: // laload
        case 0x30: // faload
        case 0x31: // daload
        case 0x32: // aaload
        case 0x33: // baload
        case 0x34: // caload
        case 0x35: // saload
            m.visitInsn(instruction);
            break;
        case 0x36: // istore
        case 0x37: // lstore
        case 0x38: // fstore
        case 0x39: // dstore
        case 0x3a: // astore
            if (localVariables.containsKey(rest))
                m.visitVarInsn(instruction, localVariables.get(rest).index);
            else
                throw new Exception("Undeclared local variable: " + rest);
            break;
        case 0x3b: // istore_0
            m.visitVarInsn(Opcodes.ISTORE, 0);
            break;
        case 0x3c: // istore_1
            m.visitVarInsn(Opcodes.ISTORE, 1);
            break;
        case 0x3d: // istore_2
            m.visitVarInsn(Opcodes.ISTORE, 2);
            break;
        case 0x3e: // istore_3
            m.visitVarInsn(Opcodes.ISTORE, 3);
            break;
        case 0x3f: // lstore_0
            m.visitVarInsn(Opcodes.LSTORE, 0);
            break;
        case 0x40: // lstore_1
            m.visitVarInsn(Opcodes.LSTORE, 1);
            break;
        case 0x41: // lstore_2
            m.visitVarInsn(Opcodes.LSTORE, 2);
            break;
{'fraction_non_alphanumeric': 0.11538025614870226, 'fraction_numerical': 0.012399410631304545, 'mean_word_length': 1.4173150684931506, 'pattern_counts': {'":': 2, '<': 77, '<?xml version=': 0, '>': 62, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '677714', 'n_tokens_mistral': 12691, 'n_tokens_neox': 11974, 'n_words': 2684}