Package seph.lang.compiler

Source Code of seph.lang.compiler.AbstractionCompiler$Arity

/*
* See LICENSE file in distribution for copyright and licensing information.
*/
package seph.lang.compiler;

import java.lang.reflect.Field;

import java.util.Arrays;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicInteger;

import seph.lang.*;
import seph.lang.ast.*;
import seph.lang.persistent.*;
import seph.lang.parser.StaticScope;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.Type;
import org.objectweb.asm.Handle;
import static org.objectweb.asm.Opcodes.*;
import static seph.lang.compiler.CompilationHelpers.*;
import static seph.lang.ActivationHelpers.*;
import static seph.lang.Types.*;
import static seph.lang.compiler.SephCallSite.*;
import static seph.lang.compiler.MethodAdapter.EMPTY;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

/**
* @author <a href="mailto:ola.bini@gmail.com">Ola Bini</a>
*/
public class AbstractionCompiler {
    public static boolean PRINT_COMPILE = false;

    private final static Handle BOOTSTRAP_METHOD = new Handle(H_INVOKESTATIC, "seph/lang/compiler/SephCallSite", "bootstrap", BOOTSTRAP_SIGNATURE_DESC);

    private final static AtomicInteger compiledCount = new AtomicInteger(0);

    private final Message code;
    private final LexicalScope capture;

    private List<LiteralEntry>  literals = new LinkedList<>();
    private List<ArgumentEntry> arguments = new LinkedList<>();

    private Class<?> abstractionClass;

    private final ClassWriter cw;
    public final String className;
    private final List<String> argNames;

    private int messageIndex = 0;

    private boolean printThisClass = false;

    private final SemiStaticScope scope;

    private final String abstractionName;

    private final seph.lang.Runtime runtime;

    private static class ScopeEntry {
        public final int depth;
        public final int index;
        public ScopeEntry(int depth, int index) {
            this.depth = depth;
            this.index = index;
        }
    }

    public static class SemiStaticScope {
        public final List<String> names;
        public final SemiStaticScope parent;
        public SemiStaticScope(List<String> names, SemiStaticScope parent) {
            this.names = names;
            this.parent = parent;
        }

        public boolean hasName(String name) {
            if(names.contains(name)) {
                return true;
            }
            if(parent != null) {
                return parent.hasName(name);
            }
            return false;
        }

        public ScopeEntry find(String name) {
            return find(name, 0);
        }
       
        private ScopeEntry find(String name, int depth) {
            int ix = names.indexOf(name);
            if(ix != -1) {
                return new ScopeEntry(depth, ix);
            } else if(parent != null) {
                return parent.find(name, depth + 1);
            } else {
                return null;
            }
        }
    }

    private AbstractionCompiler(seph.lang.Runtime runtime, Message code, List<String> argNames, LexicalScope capture, StaticScope scope, SemiStaticScope parentScope, String name) {
        this.code = code;
        this.argNames = argNames;
        this.capture = capture;
        this.abstractionName = name;
        this.runtime = runtime;
        this.className = "seph$gen$abstraction$" + compiledCount.getAndIncrement() + "$" + encode(abstractionName);
        this.cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
        final java.io.File sourceFile = new java.io.File(code.filename());
        this.cw.visitSource(code.filename(), sourceFile.getAbsolutePath());

        List<String> newNames = new LinkedList<>();
        for(String s : scope.getNames()) {
            if(!parentScope.hasName(s)) {
                newNames.add(s);
            }
        }
        for(String s : scope.getShadowing()) {
            if(!newNames.contains(s)) {
                newNames.add(s);
            }
        }
        this.scope = new SemiStaticScope(newNames, parentScope);
    }

    private void generateAbstractionClass() {
        cw.visit(V1_7, ACC_PUBLIC, p(className), null, p(SimpleSephObject.class), new String[0]);

        activateWithMethod();
        activationForMethod();
        abstractionFields();
        constructor(SimpleSephObject.class);

        cw.visitEnd();

        final byte[] classBytes = cw.toByteArray();

        if(printThisClass || PRINT_COMPILE || runtime.configuration().doPrintBytecode()) {
            new ClassReader(classBytes).accept(new org.objectweb.asm.util.TraceClassVisitor(new java.io.PrintWriter(System.err)), 0);
            // try {
            //     java.io.FileOutputStream ff = new java.io.FileOutputStream(className + ".class");
            //     ff.write(classBytes);
            //     ff.close();
            // } catch(Exception e) {
            // }
        }

        try {
            abstractionClass = seph.lang.Runtime.LOADER.defineClass(className, classBytes);
        } catch(Throwable e) {
            new ClassReader(classBytes).accept(new org.objectweb.asm.util.TraceClassVisitor(new java.io.PrintWriter(System.err)), 0);
            throw new RuntimeException(e);
        }
    }

    private SephObject instantiateAbstraction() {
        try {

            return (SephObject)abstractionClass.getConstructor(LexicalScope.class).newInstance(capture);
        } catch(Exception e) {
            System.err.println(e);
            e.printStackTrace();
            throw new CompilationAborted("An error was encountered during compilation");
        }
    }

    private final static MethodType ARGUMENT_METHOD_TYPE = MethodType.methodType(SephObject.class, LexicalScope.class, SThread.class, LexicalScope.class, boolean.class, boolean.class);
    private void setStaticValues() {
        try {
            Field f = abstractionClass.getDeclaredField("fullMsg");
            f.setAccessible(true);
            f.set(null, code);

            for(LiteralEntry le : literals) {
                f = abstractionClass.getDeclaredField(le.name);
                f.setAccessible(true);
                f.set(null, le.code.literal());
            }

            for(ArgumentEntry ae : arguments) {
                f = abstractionClass.getDeclaredField(ae.codeName);
                f.setAccessible(true);
                f.set(null, ae.argumentCode);

                MethodHandle h = findStatic(abstractionClass, ae.methodName, ARGUMENT_METHOD_TYPE);
                f = abstractionClass.getDeclaredField(ae.handleName);
                f.setAccessible(true);
                f.set(null, h);
            }
        } catch(Exception e) {
            System.err.println(e);
            e.printStackTrace();
            throw new CompilationAborted("An error was encountered during compilation");
        }
    }

    private void abstractionFields() {
        cw.visitField(ACC_PRIVATE + ACC_STATIC, "fullMsg", c(Message.class), null, null);
        cw.visitField(ACC_PRIVATE + ACC_FINAL, "capture", c(LexicalScope.class), null, null);
    }

    private void constructor(Class<?> superClass) {
        MethodAdapter ma = new MethodAdapter(cw.visitMethod(ACC_PUBLIC, "<init>", sig(void.class, LexicalScope.class), null, null));
        ma.loadThis();
        ma.dup();
        ma.dup();

        ma.getStatic(PersistentArrayMap.class, "EMPTY", PersistentArrayMap.class);
        ma.getStatic(SimpleSephObject.class, "activatable", Symbol.class);
        ma.getStatic(seph.lang.Runtime.class, "TRUE", SephObject.class);

        ma.virtualCall(PersistentArrayMap.class, "associate", IPersistentMap.class, Object.class, Object.class);
        ma.init(superClass, void.class, IPersistentMap.class);

        ma.loadLocal(1);

        ma.putField(className, "capture", LexicalScope.class);

        ma.ret();
        ma.end();
    }

    public static SephObject compile(seph.lang.Runtime runtime, Message code, List<String> argNames, LexicalScope capture, StaticScope scope, SemiStaticScope parent, String name) {
        AbstractionCompiler c = new AbstractionCompiler(runtime, code, argNames, capture, scope, parent, name);
        c.generateAbstractionClass();
        c.setStaticValues();
        return c.instantiateAbstraction();
    }

    public static String compile(seph.lang.Runtime runtime, Message code, List<String> argNames, StaticScope scope, SemiStaticScope parent, String name) {
        AbstractionCompiler c = new AbstractionCompiler(runtime, code, argNames, null, scope, parent, name);
        c.generateAbstractionClass();
        c.setStaticValues();
        return c.className;
    }

    public static SephObject compile(seph.lang.Runtime runtime, ISeq argumentsAndCode, LexicalScope capture, StaticScope scope, SemiStaticScope parent, String name) {
        final List<String> argNames = new ArrayList<>();
        if(argumentsAndCode != null) {
            for(;RT.next(argumentsAndCode) != null; argumentsAndCode = RT.next(argumentsAndCode)) {
                argNames.add(((Message)RT.first(argumentsAndCode)).name());
            }
        }
        return compile(runtime, (Message)RT.first(argumentsAndCode), argNames, capture, scope, parent, name);
    }

    public static String compile(seph.lang.Runtime runtime, ISeq argumentsAndCode, StaticScope scope, SemiStaticScope parent, String name) {
        final List<String> argNames = new ArrayList<>();
        if(argumentsAndCode != null) {
            for(;RT.next(argumentsAndCode) != null; argumentsAndCode = RT.next(argumentsAndCode)) {
                argNames.add(((Message)RT.first(argumentsAndCode)).name());
            }
        }
        return compile(runtime, (Message)RT.first(argumentsAndCode), argNames, scope, parent, name);
    }

    private static class LiteralEntry {
        public final String name;
        public final Message code;
        public final int position;
        public LiteralEntry(String name, Message code, int position) {
            this.name = name;
            this.code = code;
            this.position = position;
        }
    }

    private static class ArgumentEntry {
        public final String codeName;
        public final String handleName;
        public final String methodName;
        public final Message argumentCode;
        public final String keyword;

        public ArgumentEntry(String codeName, String handleName, String methodName, Message argumentCode, String keyword) {
            this.codeName = codeName;
            this.handleName = handleName;
            this.methodName = methodName;
            this.argumentCode = argumentCode;
            this.keyword = keyword;
        }
    }



















    private void compileLiteral(MethodAdapter ma, Message current) {
        int position = literals.size();
        LiteralEntry le = new LiteralEntry("literal" + position, current, position);
        literals.add(le);
       
        cw.visitField(ACC_PRIVATE + ACC_STATIC, le.name, c(SephObject.class), null, null);

        ma.pop();
        ma.getStatic(className, le.name, SephObject.class);
    }

    private void compileTerminator(MethodAdapter ma, Message current, int[] layout) {
        if(current.next() != null && !(current.next() instanceof Terminator)) {
            ma.pop();
            ma.loadLocal(layout[RECEIVER_IDX]);
        }
    }

    private static final Message SENTINEL = new LiteralMessage(null, null, null, -1, -1);

    private String currentAssignment = null;
   
    private void compileAssignment(MethodAdapter ma, Assignment current, int[] layout) {
        Message left = (Message)current.arguments().seq().first();
        Message right = (Message)current.arguments().seq().next().first();
        String name = left.name();
        ScopeEntry se = scope.find(name);

        String oldAssignment = currentAssignment;
        currentAssignment = name;

        //        printThisClass = true;
        switch(current.getAssignment()) {
        case EQ:
            compileCode(ma, right, SENTINEL, layout);   // [val]
            ma.dup(); // [val, val]
            ma.loadLocal(layout[METHOD_SCOPE_IDX])// [val, val, scope]
            ma.swap(); // [val, scope, val]
            ma.load(se.depth); // [val, scope, val, depth]
            ma.swap(); // [val, scope, depth, val]
            ma.load(se.index); // [val, scope, depth, val, index]
            ma.swap(); // [val, scope, depth, index, val]
            ma.virtualCall(LexicalScope.class, "assign", void.class, int.class, int.class, SephObject.class);
            break;
        case PLUS_EQ:
            compileCode(ma, left, SENTINEL, layout);
            compileCode(ma, NamedMessage.create("+", new PersistentList(right), null, left.filename(), left.line(), left.position(), null), SENTINEL, layout);
            ma.dup();
            ma.loadLocal(layout[METHOD_SCOPE_IDX]);
            ma.swap();
            ma.load(se.depth); // [val, scope, val, depth]
            ma.swap(); // [val, scope, depth, val]
            ma.load(se.index); // [val, scope, depth, val, index]
            ma.swap(); // [val, scope, depth, index, val]
            ma.virtualCall(LexicalScope.class, "assign", void.class, int.class, int.class, SephObject.class);
            break;
        default:
            assert false : "Should never reach here - the compiler has to cover all cases of assignment, but is missing: " + current.getAssignment();
        }

        currentAssignment = oldAssignment;
    }

    private void compileArgument(Message argument, int currentMessageIndex, int argIndex, List<ArgumentEntry> currentArguments, Message last, List<Handle> mhsAndAsts) {
        //               printThisClass = true;
        String keyword = null;
        Message argumentToCompile = argument;
        String name = argument.name();
        if(name.endsWith(":")) {
            keyword = name.substring(0, name.length() - 1);
            argumentToCompile = argument.next();
        }

        final String codeName   = "code_arg_" + currentMessageIndex + "_" + argIndex;
        final String handleName = "handle_arg_" + currentMessageIndex + "_" + argIndex;
        final String methodName = "argument_" + currentMessageIndex + "_" + argIndex;
        ArgumentEntry ae = new ArgumentEntry(codeName, handleName, methodName, argument, keyword);
        mhsAndAsts.add(new Handle(H_INVOKESTATIC,  className, methodName, sig(SephObject.class, LexicalScope.class, SThread.class, LexicalScope.class, boolean.class, boolean.class)));
        mhsAndAsts.add(new Handle(H_GETSTATIC,     className, codeName, c(SephObject.class)));

        arguments.add(ae);
        currentArguments.add(ae);

        cw.visitField(ACC_PRIVATE + ACC_STATIC, codeName,   c(SephObject.class), null, null);
        cw.visitField(ACC_PRIVATE + ACC_STATIC, handleName, c(MethodHandle.class), null, null);

        MethodAdapter ma = new MethodAdapter(cw.visitMethod(ACC_PUBLIC + ACC_STATIC, methodName, sig(SephObject.class, LexicalScope.class, SThread.class, LexicalScope.class, boolean.class, boolean.class), null, null));
       
        int[] layout = VARIABLE_LAYOUT_ARGUMENT_METHOD;

        ma.loadLocalInt(layout[SHOULD_EVALUATE_IDX]);
        ma.zero();

        Label els = new Label();
        ma.ifNotEqual(els);

        ma.getStatic(className, codeName, SephObject.class);
        ma.retValue();

        ma.label(els);

        Message current = argumentToCompile;

        ma.loadLocal(layout[METHOD_SCOPE_IDX]);
        ma.getField(LexicalScope.class, "ground", SephObject.class);
        ma.dup();
        ma.storeLocal(layout[RECEIVER_IDX]);

        boolean first = true;

        while(current != null) {
            ma.line(current.line());
            if(current.isLiteral()) {
                compileLiteral(ma, current);
                first = false;
            } else if(current instanceof Terminator) {
                compileTerminator(ma, current, layout);
                first = true;
            } else if(current instanceof Abstraction) {
                ma.pop();
                String aname = currentAssignment;
                if(aname == null) {
                    aname = "__inline__";
                }
                String newName = AbstractionCompiler.compile(this.runtime, RT.seq(current.arguments()), ((Abstraction)current).scope, scope, aname);
                ma.create(newName);
                ma.dup();
                ma.loadLocal(0);
                ma.init(newName, Void.TYPE, LexicalScope.class);
                first = false;
            } else if(current instanceof Assignment) {
                compileAssignment(ma, (Assignment)current, layout);
                first = false;
            } else {
                compileMessageSend(ma, current, layout, first, last);
                first = false;
            }
            current = current.next();
        }

        ma.retValue();
        ma.end();
    }

    private static class Arity {
        public final int positional;
        public final int keyword;
        public Arity(int positional, int keyword) {
            this.positional = positional;
            this.keyword = keyword;
        }
    }

    private Arity countArguments(IPersistentList arguments) {
        final int arity = RT.count(arguments);
        int keywordArgs = 0;
        for(ISeq seq = arguments.seq(); seq != null; seq = seq.next()) {
            Message m = (Message)seq.first();
            if(m.name().endsWith(":")) {
                keywordArgs++;
            }
        }
        return new Arity(arity - keywordArgs, keywordArgs);
    }

    private Handle[] compileArguments(MethodAdapter ma, IPersistentList arguments, int[] layout, Message last) {
        int num = 0;
        final int currentMessageIndex = messageIndex++;

        final int arity = RT.count(arguments);
        final List<ArgumentEntry> currentArguments = new ArrayList<>();
        final List<Message> keywordArguments = new LinkedList<>();
        final List<String> keywordArgumentNames = new LinkedList<>();
        final List<Handle> mhsAndAsts = new LinkedList<>();


        for(ISeq seq = arguments.seq(); seq != null; seq = seq.next()) {
            Message m = (Message)seq.first();
            if(m.name().endsWith(":")) {
                keywordArgumentNames.add(m.name().substring(0, m.name().length() - 1));
                keywordArguments.add(m);
            } else {
                compileArgument((Message)seq.first(), currentMessageIndex, num++, currentArguments, last, mhsAndAsts);
            }
        }

        final LinkedList<ArgumentEntry> keywordCurrentArguments = new LinkedList<>();
        for(Message m : keywordArguments) {
            compileArgument(m, currentMessageIndex, num++, keywordCurrentArguments, last, mhsAndAsts);
        }

        if((arity - keywordArguments.size()) > 5) {
            ma.load(arity - keywordArguments.size());
            ma.newArray(MethodHandle.class);
            int i = 0;
            for(ArgumentEntry ae : keywordCurrentArguments) {
                ma.dup();
                ma.load(i++);
               
                ma.getStatic(className, ae.handleName, MethodHandle.class);
                ma.loadLocal(layout[METHOD_SCOPE_IDX]);
                ma.virtualCall(MethodHandle.class, "bindTo", MethodHandle.class, Object.class);

                ma.storeArray();
            }
        } else {
            for(ArgumentEntry ae : currentArguments) {
                // printThisClass = true;
                ma.getStatic(className, ae.handleName, MethodHandle.class);

                ma.loadLocal(layout[METHOD_SCOPE_IDX]);

                ma.virtualCall(MethodHandle.class, "bindTo", MethodHandle.class, Object.class);
            }
        }

        if(keywordArguments.size() > 0) {
            ma.load(keywordArgumentNames.size());
            ma.newArray(String.class);
            int i = 0;
            for(String s : keywordArgumentNames) {
                ma.dup();
                ma.load(i++);
                ma.load(s);
                ma.storeArray();
            }

            ma.load(keywordArgumentNames.size());
            ma.newArray(MethodHandle.class);
            i = 0;
            for(ArgumentEntry ae : keywordCurrentArguments) {
                ma.dup();
                ma.load(i++);
                ma.getStatic(className, ae.handleName, MethodHandle.class);

                ma.loadLocal(layout[METHOD_SCOPE_IDX]);

                ma.virtualCall(MethodHandle.class, "bindTo", MethodHandle.class, Object.class);
                ma.storeArray();
            }
        }

        return mhsAndAsts.toArray(new Handle[0]);
    }


    private final static int[] VARIABLE_LAYOUT_ACTIVATE_WITH   = new int[7];
    private final static int[] VARIABLE_LAYOUT_ARGUMENT_METHOD = new int[7];

    private final static int RECEIVER_IDX               = 0;
    private final static int THREAD_IDX                 = 1;
    private final static int SCOPE_IDX                  = 2;
    private final static int ARGUMENTS_IDX              = 3;
    private final static int METHOD_SCOPE_IDX           = 4;
    private final static int SHOULD_EVALUATE_IDX        = 5;
    private final static int SHOULD_EVALUATE_FULLY_IDX  = 6;

    static {
        VARIABLE_LAYOUT_ACTIVATE_WITH[RECEIVER_IDX]     = 1;
        VARIABLE_LAYOUT_ACTIVATE_WITH[THREAD_IDX]       = 2;
        VARIABLE_LAYOUT_ACTIVATE_WITH[SCOPE_IDX]        = 3;
        VARIABLE_LAYOUT_ACTIVATE_WITH[ARGUMENTS_IDX]    = 4;
        VARIABLE_LAYOUT_ACTIVATE_WITH[METHOD_SCOPE_IDX] = 5;

        VARIABLE_LAYOUT_ARGUMENT_METHOD[ARGUMENTS_IDX]             = -1// argument evaluation doesn't have any arguments
        VARIABLE_LAYOUT_ARGUMENT_METHOD[METHOD_SCOPE_IDX]          = 0;
        VARIABLE_LAYOUT_ARGUMENT_METHOD[THREAD_IDX]                = 1;
        VARIABLE_LAYOUT_ARGUMENT_METHOD[SCOPE_IDX]                 = 2;
        VARIABLE_LAYOUT_ARGUMENT_METHOD[SHOULD_EVALUATE_IDX]       = 3;
        VARIABLE_LAYOUT_ARGUMENT_METHOD[SHOULD_EVALUATE_FULLY_IDX] = 4;
        VARIABLE_LAYOUT_ARGUMENT_METHOD[RECEIVER_IDX]              = 5;
    }


    private void pumpTailCall(MethodAdapter ma, int[] layout) {
        if(runtime.configuration().doTailCallOptimization()) {
            ma.loadLocal(layout[THREAD_IDX]);
            ma.dynamicCall("seph:pumpTailCall", sig(SephObject.class, SephObject.class, SThread.class), BOOTSTRAP_METHOD);
        }
    }

    private Class[] argumentArrayFor(Arity arity) {
        if(arity.keyword > 0) {
            switch(arity.positional) {
            case 0:
                return new Class[]{SephObject.class, SThread.class, LexicalScope.class, String[].class, MethodHandle[].class};
            case 1:
                return new Class[]{SephObject.class, SThread.class, LexicalScope.class, MethodHandle.class, String[].class, MethodHandle[].class};
            case 2:
                return new Class[]{SephObject.class, SThread.class, LexicalScope.class, MethodHandle.class, MethodHandle.class, String[].class, MethodHandle[].class};
            case 3:
                return new Class[]{SephObject.class, SThread.class, LexicalScope.class, MethodHandle.class, MethodHandle.class, MethodHandle.class, String[].class, MethodHandle[].class};
            case 4:
                return new Class[]{SephObject.class, SThread.class, LexicalScope.class, MethodHandle.class, MethodHandle.class, MethodHandle.class, MethodHandle.class, String[].class, MethodHandle[].class};
            case 5:
                return new Class[]{SephObject.class, SThread.class, LexicalScope.class, MethodHandle.class, MethodHandle.class, MethodHandle.class, MethodHandle.class, MethodHandle.class, String[].class, MethodHandle[].class};
            default:
                return new Class[]{SephObject.class, SThread.class, LexicalScope.class, MethodHandle[].class, String[].class, MethodHandle[].class};
            }
        } else {
            switch(arity.positional) {
            case 0:
                return new Class[]{SephObject.class, SThread.class, LexicalScope.class};
            case 1:
                return new Class[]{SephObject.class, SThread.class, LexicalScope.class, MethodHandle.class};
            case 2:
                return new Class[]{SephObject.class, SThread.class, LexicalScope.class, MethodHandle.class, MethodHandle.class};
            case 3:
                return new Class[]{SephObject.class, SThread.class, LexicalScope.class, MethodHandle.class, MethodHandle.class, MethodHandle.class};
            case 4:
                return new Class[]{SephObject.class, SThread.class, LexicalScope.class, MethodHandle.class, MethodHandle.class, MethodHandle.class, MethodHandle.class};
            case 5:
                return new Class[]{SephObject.class, SThread.class, LexicalScope.class, MethodHandle.class, MethodHandle.class, MethodHandle.class, MethodHandle.class, MethodHandle.class};
            default:
                return new Class[]{SephObject.class, SThread.class, LexicalScope.class, MethodHandle[].class};
            }
        }
    }

    private Class[] argumentArrayFor2(Arity arity) {
        if(arity.keyword > 0) {
            switch(arity.positional) {
            case 0:
                return new Class[]{SephObject.class, SephObject.class, SThread.class, LexicalScope.class, String[].class, MethodHandle[].class};
            case 1:
                return new Class[]{SephObject.class, SephObject.class, SThread.class, LexicalScope.class, MethodHandle.class, String[].class, MethodHandle[].class};
            case 2:
                return new Class[]{SephObject.class, SephObject.class, SThread.class, LexicalScope.class, MethodHandle.class, MethodHandle.class, String[].class, MethodHandle[].class};
            case 3:
                return new Class[]{SephObject.class, SephObject.class, SThread.class, LexicalScope.class, MethodHandle.class, MethodHandle.class, MethodHandle.class, String[].class, MethodHandle[].class};
            case 4:
                return new Class[]{SephObject.class, SephObject.class, SThread.class, LexicalScope.class, MethodHandle.class, MethodHandle.class, MethodHandle.class, MethodHandle.class, String[].class, MethodHandle[].class};
            case 5:
                return new Class[]{SephObject.class, SephObject.class, SThread.class, LexicalScope.class, MethodHandle.class, MethodHandle.class, MethodHandle.class, MethodHandle.class, MethodHandle.class, String[].class, MethodHandle[].class};
            default:
                return new Class[]{SephObject.class, SephObject.class, SThread.class, LexicalScope.class, MethodHandle[].class, String[].class, MethodHandle[].class};
            }
        } else {
            switch(arity.positional) {
            case 0:
                return new Class[]{SephObject.class, SephObject.class, SThread.class, LexicalScope.class};
            case 1:
                return new Class[]{SephObject.class, SephObject.class, SThread.class, LexicalScope.class, MethodHandle.class};
            case 2:
                return new Class[]{SephObject.class, SephObject.class, SThread.class, LexicalScope.class, MethodHandle.class, MethodHandle.class};
            case 3:
                return new Class[]{SephObject.class, SephObject.class, SThread.class, LexicalScope.class, MethodHandle.class, MethodHandle.class, MethodHandle.class};
            case 4:
                return new Class[]{SephObject.class, SephObject.class, SThread.class, LexicalScope.class, MethodHandle.class, MethodHandle.class, MethodHandle.class, MethodHandle.class};
            case 5:
                return new Class[]{SephObject.class, SephObject.class, SThread.class, LexicalScope.class, MethodHandle.class, MethodHandle.class, MethodHandle.class, MethodHandle.class, MethodHandle.class};
            default:
                return new Class[]{SephObject.class, SephObject.class, SThread.class, LexicalScope.class, MethodHandle[].class};
            }
        }
    }


    private String sigFor(Arity arity) {
        return sig(SephObject.class, argumentArrayFor(arity));
    }

    private String sigFor2(Arity arity) {
        return sig(SephObject.class, argumentArrayFor2(arity));
    }

    private void compileIfStatement(MethodAdapter ma, Message current, int[] layout, boolean first, Message last, Arity arity) {
        ISeq seq = current.arguments().seq();
        Message conditional = (Message)seq.first();
        Message _then = null;
        Message _else = null;
        if((seq = seq.next()) != null) {
            _then = (Message)seq.first();
            if((seq = seq.next()) != null) {
                _else = (Message)seq.first();
            }
        }

        compileCode(ma, conditional, SENTINEL, layout);
        ma.interfaceCall(SephObject.class, "isTrue", boolean.class);
        Label elseBranch = new Label();
        Label endIf = new Label();
        ma.zero();

        ma.ifEqual(elseBranch);

        if(_then != null) {
            ma.loadLocal(layout[RECEIVER_IDX]);
            Message newLast = current == last ? findLast(_then) : SENTINEL;
            compileCode(ma, _then, newLast, layout);
            if(current != last) {
                pumpTailCall(ma, layout);
            }
        } else {
            ma.getStatic(seph.lang.Runtime.class, "NIL", SephObject.class);
        }
        ma.jump(endIf);
        ma.label(elseBranch);
        if(_else != null) {
            ma.loadLocal(layout[RECEIVER_IDX]);
            Message newLast = current == last ? findLast(_else) : SENTINEL;
            compileCode(ma, _else, newLast, layout);
            if(current != last) {
                pumpTailCall(ma, layout);
            }
        } else {
            ma.getStatic(seph.lang.Runtime.class, "NIL", SephObject.class);
        }
        ma.label(endIf);
    }


    private void loadFromDepth(int depth, int index, MethodAdapter ma) {
        int currentDepth = depth;
        while(currentDepth-- > 0) {
            ma.getField(LexicalScope.class, "parent", LexicalScope.class);
        }
    
        switch(index) {
        case 0:
            ma.cast(LexicalScope.One.class);
            ma.getField(LexicalScope.One.class, "value0", SephObject.class);
            break;
        case 1:
            ma.cast(LexicalScope.Two.class);
            ma.getField(LexicalScope.Two.class, "value1", SephObject.class);
            break;
        case 2:
            ma.cast(LexicalScope.Three.class);
            ma.getField(LexicalScope.Three.class, "value2", SephObject.class);
            break;
        case 3:
            ma.cast(LexicalScope.Four.class);
            ma.getField(LexicalScope.Four.class, "value3", SephObject.class);
            break;
        case 4:
            ma.cast(LexicalScope.Five.class);
            ma.getField(LexicalScope.Five.class, "value4", SephObject.class);
            break;
        case 5:
            ma.cast(LexicalScope.Six.class);
            ma.getField(LexicalScope.Six.class, "value5", SephObject.class);
            break;
        default:
            ma.cast(LexicalScope.Many.class);
            ma.getField(LexicalScope.Many.class, "values", SephObject[].class);
            ma.load(index-6);
            ma.loadArray();
            break;
        }
    }

    private void compileRegularMessageSend(MethodAdapter ma, Message current, int[] layout, boolean first, Message last, Arity arity, String name) {
        ScopeEntry se = null;
        Label noActivate = null;
        String messageType = "message";
        String possibleAdditional = "";

        if(first && (se = scope.find(name)) != null) {
            ma.loadLocal(layout[METHOD_SCOPE_IDX]);
           
            if(runtime.configuration().doLexicalMethodHandleLookup()) {
                ma.dynamicCall("seph:lookup:" + encode(name) + ":lexical:" + se.depth + ":" + se.index, sig(SephObject.class, LexicalScope.class), BOOTSTRAP_METHOD);
            } else {
                loadFromDepth(se.depth, se.index, ma);
            }

            if(!runtime.configuration().doLexicalMethodHandleInvoke()) {
                noActivate = new Label();
                ma.dup();
                ma.interfaceCall(SephObject.class, "isActivatable", boolean.class);
                ma.zero();
                ma.ifEqual(noActivate);
    
                ma.load(arity.positional);
                ma.load(arity.keyword == 0 ? 0 : 1);
                ma.interfaceCall(SephObject.class, "activationFor", MethodHandle.class, int.class, boolean.class);
           
                if(runtime.configuration().doTailCallOptimization() && current == last) {
                    ma.swap();
                    ma.load(0);
                }
            }

            ma.swap();
        }       

        ma.loadLocal(layout[THREAD_IDX]);
        ma.loadLocal(layout[METHOD_SCOPE_IDX]);
           
        Handle[] argMHrefs = compileArguments(ma, current.arguments(), layout, last);

        if(first && se != null) {
            // [recv, value, thread, scope, arg0, arg1]

            if(runtime.configuration().doLexicalMethodHandleInvoke()) {
                boolean fullPumping = false;

                messageType = "invoke";
                if(runtime.configuration().doTailCallOptimization() && current == last) {
                    messageType = "tailInvoke";
                    fullPumping = true;
                }

                ma.dynamicCall("seph:" + messageType + ":" + encode(name), sigFor2(arity), BOOTSTRAP_METHOD, argMHrefs);
                if(runtime.configuration().doTailCallOptimization()) {
                    if(fullPumping) {
                        if(layout[SHOULD_EVALUATE_FULLY_IDX] != 0) {
                            Label noPump = new Label();
                            ma.loadLocalInt(layout[SHOULD_EVALUATE_FULLY_IDX]);
                            ma.zero();
                            ma.ifEqual(noPump);
                            pumpTailCall(ma, layout);
                            ma.label(noPump);
                        }
                    } else {
                        pumpTailCall(ma, layout);
                    }
                }
            } else {
                if(runtime.configuration().doTailCallOptimization() && current == last) {
                    Label activate = new Label();
                    int len = argumentArrayFor(arity).length;
                    ma.load(len);
                    ma.newArray(Object.class);
                    for(int i = len - 1; i >= 0; i--) {
                        ma.dup_x1();
                        ma.swap();
                        ma.load(i);
                        ma.swap();
                        ma.storeArray();
                    }
                    ma.staticCall(MethodHandles.class, "insertArguments", MethodHandle.class, MethodHandle.class, int.class, Object[].class);
                    ma.loadLocal(layout[THREAD_IDX]);
                    ma.swap();
                    ma.putField(SThread.class, "tail", MethodHandle.class);
                    ma.getStatic(SThread.class, "TAIL_MARKER", SephObject.class);

                    if(layout[SHOULD_EVALUATE_FULLY_IDX] != 0) {
                        Label noPump = new Label();
                        ma.loadLocalInt(layout[SHOULD_EVALUATE_FULLY_IDX]);
                        ma.zero();
                        ma.ifEqual(noPump);
                        pumpTailCall(ma, layout);
                        ma.label(noPump);
                    }

                    ma.jump(activate);
                    ma.label(noActivate);

                    ma.swap();
                    ma.pop();

                    ma.label(activate);
                } else {
                    Label activate = new Label();
                    ma.virtualCall(MethodHandle.class, "invokeExact", sigFor(arity));

                    pumpTailCall(ma, layout);
                    ma.jump(activate);
                    ma.label(noActivate);

                    ma.swap();
                    ma.pop();
                    ma.label(activate);
                }
            }
        } else {
            boolean fullPumping = false;

            if(runtime.configuration().doTailCallOptimization() && current == last) {
                messageType = "tailMessage";
                fullPumping = true;
            }

            ma.dynamicCall("seph:" + messageType + ":" + encode(name) + possibleAdditional, sigFor(arity), BOOTSTRAP_METHOD, argMHrefs);
            if(runtime.configuration().doTailCallOptimization()) {
                if(fullPumping) {
                    if(layout[SHOULD_EVALUATE_FULLY_IDX] != 0) {
                        Label noPump = new Label();
                        ma.loadLocalInt(layout[SHOULD_EVALUATE_FULLY_IDX]);
                        ma.zero();
                        ma.ifEqual(noPump);
                        pumpTailCall(ma, layout);
                        ma.label(noPump);
                    }
                } else {
                    pumpTailCall(ma, layout);
                }
            }
        }
    }

    private void compileMessageSend(MethodAdapter ma, Message current, int[] layout, boolean first, Message last) {
        final Arity arity = countArguments(current.arguments());
        final String name = current.name().intern();
        if(name == "if" && arity.positional > 0 && arity.positional < 4) {
            compileIfStatement(ma, current, layout, first, last, arity);
        } else {
            compileRegularMessageSend(ma, current, layout, first, last, arity, name);
        }
    }

    private Message findLast(Message code) {
        Message current = code;
        Message lastReal = null;
        while(current != null) {
            if(!(current instanceof Terminator)) {
                lastReal = current;
            }
            current = current.next();
        }
        return lastReal;
    }

    private void compileCode(MethodAdapter ma, Message _code, Message last, int[] layout) {
        boolean first = true;

        Message current = _code;

        while(current != null) {
            ma.line(current.line());
            if(current.isLiteral()) {
                compileLiteral(ma, current);
                first = false;
            } else if(current instanceof Terminator) {
                compileTerminator(ma, current, layout);
                first = true;
            } else if(current instanceof Abstraction) {
                ma.pop();
                String aname = currentAssignment;
                if(aname == null) {
                    aname = "__inline__";
                }
                String newName = AbstractionCompiler.compile(this.runtime, RT.seq(current.arguments()), ((Abstraction)current).scope, scope, aname);
                ma.create(newName);
                ma.dup();
                ma.loadLocal(layout[METHOD_SCOPE_IDX]);
                ma.init(newName, Void.TYPE, LexicalScope.class);
                first = false;
            } else if(current instanceof Assignment) {
                compileAssignment(ma, (Assignment)current, layout);
                first = false;
            } else {
                compileMessageSend(ma, current, layout, first, last);
                first = false;
            }
            current = current.next();
        }
    }

    private void activateWithBody(MethodAdapter ma, int[] layout) {
        ma.loadLocal(layout[RECEIVER_IDX]);
        compileCode(ma, code, findLast(code), layout);
        ma.retValue();
        ma.end();
    }

    // Should only be called for up to five arguments
    private void activateWithMethodRealArity(final int arity) {
        final int[] layout = (int[])VARIABLE_LAYOUT_ACTIVATE_WITH.clone();
        layout[METHOD_SCOPE_IDX] += (arity-1);
        MethodAdapter ma = new MethodAdapter(cw.visitMethod(ACC_PUBLIC, encode(abstractionName), sigFor(new Arity(arity, 0)), null, null));
        ma.loadThis();
        ma.getField(className, "capture", LexicalScope.class);

        List<String> names = scope.names;
        ma.load(names.size());
        ma.newArray(String.class);
        int ix = 0;
        for(String s : names) {
            ma.dup();
            ma.load(ix++);
            ma.load(s);
            ma.storeArray();
        }
        ma.loadLocal(layout[RECEIVER_IDX]);
        ma.virtualCall(LexicalScope.class, "newScopeWith", LexicalScope.class, String[].class, SephObject.class);
        ma.storeLocal(layout[METHOD_SCOPE_IDX]);

        for(int i = 0; i < arity; i++) {
            String name = argNames.get(i);
            ma.loadLocal(layout[METHOD_SCOPE_IDX]);
            ScopeEntry se = scope.find(name);
            ma.load(se.depth);
            ma.load(se.index);
            ma.loadLocal(layout[ARGUMENTS_IDX] + i);
            ma.loadLocal(layout[SCOPE_IDX]);
            ma.loadLocal(layout[THREAD_IDX]);
            ma.one();
            ma.staticCall(ControlDefaultBehavior.class, "evaluateArgument", SephObject.class, Object.class, LexicalScope.class, SThread.class, boolean.class);
           
            ma.virtualCall(LexicalScope.class, "assign", void.class, int.class, int.class, SephObject.class);
        }

        activateWithBody(ma, layout);
    }


    private void activateWithMethodCollectedArgs() {
        MethodAdapter ma = new MethodAdapter(cw.visitMethod(ACC_PUBLIC, encode(abstractionName), sig(SephObject.class, SephObject.class, SThread.class, LexicalScope.class, MethodHandle[].class), null, null));

        ma.loadThis();
        ma.getField(className, "capture", LexicalScope.class);
        List<String> names = scope.names;
        ma.load(names.size());
        ma.newArray(String.class);
        int ix = 0;
        for(String s : names) {
            ma.dup();
            ma.load(ix++);
            ma.load(s);
            ma.storeArray();
        }
        ma.loadLocal(VARIABLE_LAYOUT_ACTIVATE_WITH[RECEIVER_IDX]);
        ma.virtualCall(LexicalScope.class, "newScopeWith", LexicalScope.class, String[].class, SephObject.class);
        ma.loadLocal(VARIABLE_LAYOUT_ACTIVATE_WITH[METHOD_SCOPE_IDX]);

        ma.loadLocal(VARIABLE_LAYOUT_ACTIVATE_WITH[ARGUMENTS_IDX]);
        ix = 0;
        for(String arg : argNames) {
            ma.dup();
            ma.loadLocal(VARIABLE_LAYOUT_ACTIVATE_WITH[METHOD_SCOPE_IDX]);
            ma.swap();
            ma.load(ix++);
            ma.loadArray();
            ma.loadLocal(VARIABLE_LAYOUT_ACTIVATE_WITH[SCOPE_IDX]);
            ma.loadLocal(VARIABLE_LAYOUT_ACTIVATE_WITH[THREAD_IDX]);
            ma.one();
            ma.staticCall(ControlDefaultBehavior.class, "evaluateArgument", SephObject.class, Object.class, LexicalScope.class, SThread.class, boolean.class);
            ma.load(arg);
            ma.swap();
            ma.virtualCall(LexicalScope.class, "directlyAssign", void.class, String.class, SephObject.class);
        }

        ma.pop();

        activateWithBody(ma, VARIABLE_LAYOUT_ACTIVATE_WITH);
    }

    private void activateWithMethodPassArgs(final int arity) {
        MethodAdapter ma = new MethodAdapter(cw.visitMethod(ACC_PUBLIC, encode(abstractionName), sig(SephObject.class, SephObject.class, SThread.class, LexicalScope.class, MethodHandle[].class), null, null));

        ma.loadThis();
        ma.loadLocal(VARIABLE_LAYOUT_ACTIVATE_WITH[RECEIVER_IDX]);
        ma.loadLocal(VARIABLE_LAYOUT_ACTIVATE_WITH[THREAD_IDX]);
        ma.loadLocal(VARIABLE_LAYOUT_ACTIVATE_WITH[SCOPE_IDX]);

        if(arity > 0) {
            ma.loadLocal(VARIABLE_LAYOUT_ACTIVATE_WITH[ARGUMENTS_IDX]);

            for(int i = 0; i < arity; i++) {
                ma.dup();
                ma.load(i);
                ma.loadArray();
                ma.swap();
            }
           
            ma.pop();
        }

        ma.virtualCall(className, encode(abstractionName), sigFor(new Arity(arity, 0)));

        ma.retValue();
        ma.end();
    }

    private void activateWithMethod() {
        int arity = argNames.size();
        if(arity <= 5) {
            activateWithMethodRealArity(arity);
            activateWithMethodPassArgs(arity);
        } else {
            activateWithMethodCollectedArgs();
        }
    }
   

    private void activationForMethod() {
        MethodAdapter ma = new MethodAdapter(cw.visitMethod(ACC_PUBLIC, "activationFor", sig(MethodHandle.class, int.class, boolean.class), null, null));

        Handle specific = new Handle(H_INVOKEVIRTUAL,  className, encode(abstractionName), sig(SephObject.class, argumentClassesFor(argNames.size(), false)));
        Handle generic = new Handle(H_INVOKEVIRTUAL,  className, encode(abstractionName), sig(SephObject.class, argumentClassesFor(-1, false)));

        ma.loadThis();
        ma.loadLocalInt(1);
        ma.loadLocalInt(2);
        ma.dynamicCall("seph:activationFor:" + encode(abstractionName) + ":" + argNames.size() + ":false", sig(MethodHandle.class, Object.class, int.class, boolean.class), BOOTSTRAP_METHOD, specific, generic);
        ma.retValue();
        ma.end();
    }
}// AbstractionCompiler
TOP

Related Classes of seph.lang.compiler.AbstractionCompiler$Arity

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.