Package org.cx4a.rsense.typing.runtime

Source Code of org.cx4a.rsense.typing.runtime.AnnotationResolver

package org.cx4a.rsense.typing.runtime;

import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Arrays;

import org.cx4a.rsense.ruby.Ruby;
import org.cx4a.rsense.ruby.Context;
import org.cx4a.rsense.ruby.IRubyObject;
import org.cx4a.rsense.ruby.RubyClass;
import org.cx4a.rsense.ruby.RubyModule;
import org.cx4a.rsense.ruby.Block;
import org.cx4a.rsense.typing.Graph;
import org.cx4a.rsense.typing.Template;
import org.cx4a.rsense.typing.TemplateAttribute;
import org.cx4a.rsense.typing.TypeSet;
import org.cx4a.rsense.typing.annotation.TypeExpression;
import org.cx4a.rsense.typing.annotation.TypeVariable;
import org.cx4a.rsense.typing.annotation.ClassType;
import org.cx4a.rsense.typing.annotation.MethodType;
import org.cx4a.rsense.typing.annotation.TypeIdentity;
import org.cx4a.rsense.typing.annotation.TypeUnion;
import org.cx4a.rsense.typing.annotation.TypeAny;
import org.cx4a.rsense.typing.annotation.TypeOptional;
import org.cx4a.rsense.typing.annotation.TypeTuple;
import org.cx4a.rsense.typing.annotation.TypeSplat;
import org.cx4a.rsense.typing.annotation.TypeApplication;
import org.cx4a.rsense.typing.annotation.TypeConstraint;
import org.cx4a.rsense.typing.vertex.Vertex;
import org.cx4a.rsense.typing.vertex.YieldVertex;
import org.cx4a.rsense.util.Logger;

public class AnnotationResolver {
    public enum Result {
        NOANNOT, UNRESOLVED, RESOLVED,
    };
   
    private Graph graph;
    private Ruby runtime;
    private TypeVarMap env;

    public AnnotationResolver(Graph graph) {
        this.graph = graph;
        this.runtime = graph.getRuntime();
        env = new TypeVarMap();
    }

    public Result resolveMethodAnnotation(Template template) {
        Method method = template.getMethod();
        if (method.getAnnotations() != null) {
            ClassType classType = RuntimeHelper.getEnclosingClassAnnotation(method.getModule());
            TemplateAttribute attr = template.getAttribute();
            IRubyObject receiver = attr.getMutableReceiver();
            IRubyObject[] args = attr.getMutableArgs();
            Vertex returnVertex = template.getReturnVertex();

            for (MethodType type : method.getAnnotations()) {
                List<TypeVariable> types = type.getTypes();
                List<TypeConstraint> constraints = type.getConstraints();
                MethodType.Signature sig = type.getSignature();
                if (sig != null) {
                    TypeExpression argType = sig.getArgType();
                    MethodType.Block block = sig.getBlock();
                    TypeExpression returnType = sig.getReturnType();

                    env.clear();
                    if (types != null) {
                        for (TypeVariable var : types) {
                            env.put(var, graph.createFreeVertex());
                        }
                    }

                    /*
                    Logger.debug("resolve annotation for %s", template.getMethod().getName());
                    Logger.debug("  class constraints: %s", resolveClassConstraints(template, classType, receiver));
                    Logger.debug("  receiver class constraints: %s", resolveReceiverClassConstraints(template, classType, receiver));
                    Logger.debug("  method arg: %s", resolveMethodArg(template, classType, argType, receiver, args));
                    Logger.debug("  method block: %s", resolveMethodBlock(template, classType, block, receiver));
                    Logger.debug("  method constraints: %s", resolveMethodConstraints(template, classType, constraints, receiver));
                    Logger.debug("  method return: %s", resolveMethodReturn(template, classType, returnType, receiver));
                    */
                    if (resolveClassConstraints(template, classType, receiver)
                        && resolveReceiverClassConstraints(template, classType, receiver)
                        && resolveMethodArg(template, classType, argType, receiver, args)
                        && resolveMethodBlock(template, classType, block, receiver)
                        && resolveMethodConstraints(template, classType, constraints, receiver)
                        && resolveMethodReturn(template, classType, returnType, receiver)) {
                        return Result.RESOLVED;
                    }
                }
            }
            return Result.UNRESOLVED;
        } else {
            return Result.NOANNOT;
        }
    }

    public boolean resolveClassConstraints(Template template, ClassType classType, IRubyObject receiver) {
        if (classType == null) { return true; }

        List<TypeConstraint> constraints = classType.getConstraints();
        if (constraints != null) {
            return resolveMethodConstraints(template, classType, constraints, receiver);
        }
        return true;
    }

    public boolean resolveReceiverClassConstraints(Template template, ClassType classType, IRubyObject receiver) {
        return receiver == null
            || resolveClassConstraints(template, RuntimeHelper.getClassAnnotation(receiver.getMetaClass()), receiver);
    }
   
    public boolean resolveMethodArg(Template template, ClassType classType, TypeExpression argType, IRubyObject receiver, IRubyObject[] args) {
        if (argType == null) {
            // arity check
            return args.length == 0;
        }

        switch (argType.getType()) {
        case TUPLE: {
            TypeTuple tuple = (TypeTuple) argType;
            List<TypeExpression> list = tuple.getList();
            if (!checkArity(list, args)) {
                //Logger.warn("Wrong number of argument");
                return false;
            }

            for (int i = 0; i < list.size(); i++) {
                argType = list.get(i);
                IRubyObject arg;
                switch (argType.getType()) {
                case SPLAT: {
                    int len = Math.max(args.length - i, 0);
                    Vertex[] elements = new Vertex[len];
                    for (int j = 0; j < len; j++) {
                        elements[j] = graph.createFreeSingleTypeVertex(args[j]);
                    }
                    arg = RuntimeHelper.createArray(graph, elements);
                    break;
                }
                default:
                    arg = i < args.length ? args[i] : runtime.getNil();
                }

                if (!resolveMethodArg(template, classType, argType, receiver, arg)) {
                    return false;
                }
            }

            return true;
        }
        default:
            if (!checkArity(Arrays.asList(argType), args)) {
                //Logger.warn("Wrong number of argument");
                return false;
            }
            IRubyObject arg = args.length > 0 ? args[0] : runtime.getNil();
            return resolveMethodArg(template, classType, argType, receiver, arg);
        }
    }

    public boolean resolveMethodArg(Template template, ClassType classType, TypeExpression argType, IRubyObject receiver, IRubyObject arg) {
        if (argType == null) { return true; }

        switch (argType.getType()) {
        case VARIABLE: {
            TypeVariable var = (TypeVariable) argType;
            TypeVarMap typeVarMap = RuntimeHelper.getTypeVarMap(receiver);
            if (classType != null
                && typeVarMap != null
                && classType.containsType(var)) {
                Vertex vertex = typeVarMap.get(var);
                if (vertex == null) {
                    vertex = graph.createFreeVertex();
                    typeVarMap.put(var, vertex);
                }
                vertex.addType(arg);
                graph.propagateEdges(vertex);
            } else {
                Vertex vertex = env.get(var);
                if (vertex == null) {
                    vertex = graph.createFreeVertex();
                    env.put(var, vertex);
                }
                vertex.addType(arg);
                graph.propagateEdges(vertex);
            }
            return true;
        }
        case APPLICATION: {
            TypeApplication app = (TypeApplication) argType;
            List<TypeExpression> types = app.getTypes();
            IRubyObject ret = resolveIdentity(template, app.getIdentity());
            if (!(ret instanceof RubyModule) || !arg.isKindOf((RubyModule) ret)) {
                return false;
            } else {
                RubyModule klass = (RubyModule) ret;
                ClassType klassType = RuntimeHelper.getClassAnnotation(klass);
                TypeVarMap typeVarMap = RuntimeHelper.getTypeVarMap(arg);
                if (klassType != null && typeVarMap != null) {
                    List<TypeVariable> vars = klassType.getTypes();
                    for (int i = 0; i < vars.size(); i++) {
                        TypeVariable var = vars.get(i);
                        Vertex vertex = typeVarMap.get(var);
                        if (i < types.size() && vertex != null) {
                            TypeExpression expr = types.get(i);
                            for (IRubyObject a : vertex.getTypeSet()) {
                                if (!resolveMethodArg(template, klassType, expr, receiver, a)) {
                                    return false;
                                }
                            }
                        }
                    }
                }
            }
            return true;
        }
        case SCOPED_IDENTITY:
        case ABSOLUTE_IDENTITY:
        case RELATIVE_IDENTITY: {
            IRubyObject guard = resolveIdentity(template, (TypeIdentity) argType);
            return (guard instanceof RubyClass) && arg.isKindOf((RubyClass) guard);
        }
        case UNION:
            for (TypeExpression expr : (TypeUnion) argType) {
                if (resolveMethodArg(template, classType, expr, receiver, arg)) {
                    return true;
                }
            }
            return false;
        case TUPLE: {
            TypeTuple tuple = (TypeTuple) argType;
            for (TypeExpression expr : tuple.getList()) {
                if (resolveMethodArg(template, classType, expr, receiver, arg)) {
                    return true;
                }
            }
            return false;
        }
        case SPLAT: {
            TypeSplat splat = (TypeSplat) argType;
            TypeExpression expr = splat.getExpression();
            if (expr == null) {
                return true;
            } else if (expr.getType() == TypeExpression.Type.VARIABLE) {
                return resolveMethodArg(template, classType, expr, receiver, arg);
            } else if (arg instanceof Array) {
                Array array = (Array) arg;
                for (Vertex v : array.getElements()) {
                    for (IRubyObject a : v.getTypeSet()) {
                        if (!resolveMethodArg(template, classType, expr, receiver, a)) {
                            return false;
                        }
                    }
                }
                return true;
            }
            return false;
        }
        case OPTIONAL: {
            return arg.isNil() ? true : resolveMethodArg(template, classType, ((TypeOptional) argType).getExpression(), receiver, arg);
        }
        case ANY:
            return true;
        default:
            Logger.error("unknown arg type: %s", argType);
        }
        return false;
    }

    public boolean resolveMethodConstraints(Template template, ClassType classType, List<TypeConstraint> constraints, IRubyObject receiver) {
        if (constraints == null) { return true; }

        for (TypeConstraint cons : constraints) {
            if (cons.getType() == TypeExpression.Type.SUBTYPE_CONS) {
                TypeExpression lhs = cons.lhs();
                TypeExpression rhs = cons.rhs();
                TypeSet ts = processMethodReturn(template, classType, lhs, receiver);
                if (ts == null) {
                    return false;
                } else if (ts.isEmpty() && lhs.getType() == TypeExpression.Type.VARIABLE) {
                    // null can be any type
                    ts = processMethodReturn(template, classType, rhs, receiver);
                    for (IRubyObject arg : ts) {
                        if (!resolveMethodArg(template, classType, lhs, receiver, arg)) {
                            return false;
                        }
                    }
                    return true;
                }
                for (IRubyObject arg : ts) {
                    if (!resolveMethodArg(template, classType, rhs, receiver, arg)) {
                        return false;
                    }
                }
            }
        }
        return true;
    }
   
    public boolean resolveMethodBlock(Template template, ClassType classType, MethodType.Block blockType, IRubyObject receiver) {
        Block block = template.getAttribute().getBlock();

        if (block == null && blockType == null) {
            return true;
        } else if ((block != null) ^ (blockType != null)) {
            return false;
        }

        MethodType.Signature blockSig = blockType.getSignature();
        TypeExpression blockArgType = blockSig.getArgType();
        TypeExpression returnArgType = blockSig.getReturnType();
        TypeSet[] args = processMethodBlockArg(template, classType, blockArgType, receiver);


        // FIXME checkArity

        boolean succeed = false;
        Vertex returnVertex;
        if (args.length == 1) {
            YieldVertex vertex = new YieldVertex(null, template, block, graph.createFreeVertex(args[0]), true);
            returnVertex = RuntimeHelper.yield(graph, vertex);
        } else {
            Vertex[] elements = new Vertex[args.length];
            for (int i = 0; i < args.length; i++) {
                elements[i] = graph.createFreeVertex();
                elements[i].addTypes(args[i]);
            }
            YieldVertex vertex = new YieldVertex(null, template, block, graph.createFreeSingleTypeVertex(RuntimeHelper.createArray(graph, elements)), true);
            returnVertex = RuntimeHelper.yield(graph, vertex);
        }

        if (returnVertex != null) {
            succeed = true;
            for (IRubyObject arg : returnVertex.getTypeSet()) {
                resolveMethodArg(template, classType, returnArgType, receiver, arg);
            }
        }
        return succeed;
    }

    public TypeSet[] processMethodBlockArg(Template template, ClassType classType, TypeExpression argType, IRubyObject receiver) {
        if (argType == null) { return new TypeSet[0]; }
        List<TypeSet> args = new ArrayList<TypeSet>();
        switch (argType.getType()) {
        case TUPLE: {
            TypeTuple tuple = (TypeTuple) argType;
            for (TypeExpression expr : tuple.getList()) {
                args.add(processMethodReturn(template, classType, expr, receiver));
            }
            break;
        }
        default:
            args.add(processMethodReturn(template, classType, argType, receiver));
        }

        return args.toArray(new TypeSet[0]);
    }
   
    public TypeSet processMethodReturn(Template template, ClassType classType, TypeExpression returnType, IRubyObject receiver) {
        if (returnType == null) { return TypeSet.EMPTY; }

        TypeSet result = new TypeSet();
        switch (returnType.getType()) {
        case VARIABLE: {
            TypeVariable var = (TypeVariable) returnType;
            if (var.getName().equals("self")) {
                result.add(receiver);
            } else if (var.getName().equals("nil")) {
                result.add(runtime.getNil());
            } else {
                Vertex vertex = env.get(var);
                TypeVarMap typeVarMap = RuntimeHelper.getTypeVarMap(receiver);
                if (vertex != null) {
                    result.addAll(vertex.getTypeSet());
                } else if (classType != null
                           && typeVarMap != null
                           && classType.containsType(var)) {
                    vertex = typeVarMap.get(var);
                    if (vertex != null) {
                        result.addAll(vertex.getTypeSet());
                    } else {
                        result.add(runtime.getNil());
                    }
                }
            }
            return result;
        }
        case APPLICATION: {
            TypeApplication app = (TypeApplication) returnType;
            List<TypeExpression> types = app.getTypes();
            IRubyObject ret = resolveIdentity(template, app.getIdentity());
            if (ret != null && ret instanceof RubyClass) {
                RubyClass klass = (RubyClass) ret;
                ret = graph.newInstanceOf(klass);
                ClassType klassType = RuntimeHelper.getClassAnnotation(klass);
                TypeVarMap typeVarMap = RuntimeHelper.getTypeVarMap(ret);
                if (klassType != null && typeVarMap != null) {
                    List<TypeVariable> vars = klassType.getTypes();
                    for (int i = 0; i < vars.size(); i++) {
                        TypeVariable var = vars.get(i);
                        if (i < types.size()) {
                            TypeExpression expr = types.get(i);
                            TypeSet ts = processMethodReturn(template, classType, expr, receiver);
                            Vertex vertex = graph.createFreeVertex();
                            vertex.addTypes(ts);
                            typeVarMap.put(var, vertex);
                        }
                    }
                }
                result.add(ret);
            }
            return result;
        }
        case SCOPED_IDENTITY:
        case ABSOLUTE_IDENTITY:
        case RELATIVE_IDENTITY: {
            IRubyObject ret = resolveIdentity(template, (TypeIdentity) returnType);
            if (ret != null && ret instanceof RubyClass) {
                ret = graph.newInstanceOf((RubyClass) ret);
                result.add(ret);
            }
            return result;
        }
        case UNION:
            for (TypeExpression expr : (TypeUnion) returnType) {
                TypeSet ts = processMethodReturn(template, classType, expr, receiver);
                if (ts != null) {
                    result.addAll(ts);
                }
            }
            return result;
        case TUPLE: {
            TypeTuple tupleType = (TypeTuple) returnType;
            List<TypeExpression> exprs = tupleType.getList();
            if (exprs != null) {
                Vertex[] elements = new Vertex[exprs.size()];
                for (int i = 0; i < exprs.size(); i++) {
                    TypeSet ts = processMethodReturn(template, classType, exprs.get(i), receiver);
                    if (ts == null) {
                        return result;
                    }
                    elements[i] = graph.createFreeVertex();
                    elements[i].addTypes(ts);
                }
                result.add(RuntimeHelper.createArray(graph, elements));
            }
            return result;
        }
        case ANY:
        case OPTIONAL:
        case SPLAT:
            return result;
        default:
            Logger.error("Unknown return type");
        }
        return null;
    }
   
    public boolean resolveMethodReturn(Template template, ClassType classType, TypeExpression returnType, IRubyObject receiver) {
        if (returnType == null) { return true; }

        TypeSet ts = processMethodReturn(template, classType, returnType, receiver);
        if (ts == null) {
            return false;
        }

        template.getReturnVertex().addTypes(ts);
        return true;
    }

    public IRubyObject resolveIdentity(Template template, TypeIdentity ident) {
        return resolveIdentity(template, ident, template.getFrame().getModule());
    }

    public IRubyObject resolveIdentity(Template template, TypeIdentity ident, RubyModule module) {
        if (ident.getType() == TypeExpression.Type.ABSOLUTE_IDENTITY) {
            return resolveIdentity(template, ident.getPath(), runtime.getObject());
        }

        IRubyObject value = module.getConstant(ident.getName());
        if (value instanceof VertexHolder) {
            module = null;
            for (IRubyObject t : ((VertexHolder) value).getVertex().getTypeSet()) {
                if (t instanceof RubyModule) {
                    module = (RubyModule) t;
                    break;
                }
            }
        } else if (value instanceof RubyModule) {
            module = (RubyModule) value;
        } else {
            return value;
        }

        if (module != null && ident.getType() == TypeExpression.Type.SCOPED_IDENTITY) {
            return resolveIdentity(template, ident.getPath(), module);
        } else {
            return module;
        }
    }

    public boolean checkArity(List<TypeExpression> list, IRubyObject[] args) {
        int preCount = 0;
        int postCount = 0;

        for (int i = 0; i < list.size(); i++) {
            TypeExpression argType = list.get(i);
            switch (argType.getType()) {
            case SPLAT:
                return true;
            case OPTIONAL:
                postCount++;
                break;
            default:
                preCount++;
                if (args.length <= i) {
                    return false;
                }
            }
        }

        return preCount + postCount >= args.length;
    }
}
TOP

Related Classes of org.cx4a.rsense.typing.runtime.AnnotationResolver

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.