All Downloads are FREE. Search and download functionalities are using the official Maven repository.

framework.src.org.checkerframework.framework.stub.ToIndexFileConverter Maven / Gradle / Ivy

Go to download

The Checker Framework enhances Java’s type system to make it more powerful and useful. This lets software developers detect and prevent errors in their Java programs. The Checker Framework includes compiler plug-ins ("checkers") that find bugs or verify their absence. It also permits you to write your own compiler plug-ins.

There is a newer version: 3.42.0
Show newest version
package org.checkerframework.framework.stub;

import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.checkerframework.stubparser.JavaParser;
import org.checkerframework.stubparser.ast.CompilationUnit;
import org.checkerframework.stubparser.ast.ImportDeclaration;
import org.checkerframework.stubparser.ast.IndexUnit;
import org.checkerframework.stubparser.ast.PackageDeclaration;
import org.checkerframework.stubparser.ast.TypeParameter;
import org.checkerframework.stubparser.ast.body.AnnotationDeclaration;
import org.checkerframework.stubparser.ast.body.BodyDeclaration;
import org.checkerframework.stubparser.ast.body.ClassOrInterfaceDeclaration;
import org.checkerframework.stubparser.ast.body.ConstructorDeclaration;
import org.checkerframework.stubparser.ast.body.EnumConstantDeclaration;
import org.checkerframework.stubparser.ast.body.EnumDeclaration;
import org.checkerframework.stubparser.ast.body.FieldDeclaration;
import org.checkerframework.stubparser.ast.body.InitializerDeclaration;
import org.checkerframework.stubparser.ast.body.MethodDeclaration;
import org.checkerframework.stubparser.ast.body.Parameter;
import org.checkerframework.stubparser.ast.body.TypeDeclaration;
import org.checkerframework.stubparser.ast.body.VariableDeclarator;
import org.checkerframework.stubparser.ast.expr.AnnotationExpr;
import org.checkerframework.stubparser.ast.expr.Expression;
import org.checkerframework.stubparser.ast.expr.ObjectCreationExpr;
import org.checkerframework.stubparser.ast.expr.VariableDeclarationExpr;
import org.checkerframework.stubparser.ast.stmt.BlockStmt;
import org.checkerframework.stubparser.ast.type.ClassOrInterfaceType;
import org.checkerframework.stubparser.ast.type.PrimitiveType;
import org.checkerframework.stubparser.ast.type.ReferenceType;
import org.checkerframework.stubparser.ast.type.Type;
import org.checkerframework.stubparser.ast.type.VoidType;
import org.checkerframework.stubparser.ast.type.WildcardType;
import org.checkerframework.stubparser.ast.visitor.GenericVisitorAdapter;
import org.checkerframework.framework.util.PluginUtil;

import annotations.Annotation;
import annotations.el.AClass;
import annotations.el.ADeclaration;
import annotations.el.AElement;
import annotations.el.AField;
import annotations.el.AMethod;
import annotations.el.AScene;
import annotations.el.ATypeElement;
import annotations.el.AnnotationDef;
import annotations.el.BoundLocation;
import annotations.el.InnerTypeLocation;
import annotations.el.LocalLocation;
import annotations.field.AnnotationFieldType;
import annotations.io.IndexFileWriter;

import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry;

/**
 * Program to convert a stub file into index files (JAIFs).
 * Note that the resulting index files will not include annotation
 * definitions, for which stubfiles do not generally provide complete
 * information.
 *
 * @author dbro
 */
public class ToIndexFileConverter extends GenericVisitorAdapter {
  private static Pattern packagePattern = Pattern.compile(
      "\\bpackage *+((?:[^.]*+[.] *+)*+[^ ]*) *+;",
      Pattern.DOTALL);
  private static Pattern importPattern = Pattern.compile(
      "\\bimport *+((?:[^.]*+[.] *+)*+[^ ]*) *+;",
      Pattern.DOTALL);

  private final String pkgName;
  private final List imports;
  private final AScene scene;

  public ToIndexFileConverter(PackageDeclaration pkgDecl,
      List importDecls, AScene scene) {
    this.scene = scene;
    if (pkgDecl == null) {
      pkgName = "";
    } else {
      Matcher m = packagePattern.matcher(pkgDecl.toString());
      String s = m.find() ? m.group(1) : null;
      pkgName = s == null ? "" : s;
    }
    if (importDecls == null) {
      imports = Collections.emptyList();
    } else {
      ArrayList imps =
          new ArrayList(importDecls.size());
      for (ImportDeclaration decl : importDecls) {
        if (!decl.isStatic()) {
          Matcher m = importPattern.matcher(decl.toString());
          if (m.find()) {
            String s = m.group(1);
            if (s != null) { imps.add(s); }
          }
        }
      }
      imps.trimToSize();
      imports = Collections.unmodifiableList(imps);
    }
  }

  /**
   * Parse stub files and write out equivalent JAIFs.
   * Note that the results do not include annotation definitions, for
   * which stubfiles do not generally provide complete information.
   *
   * @param args names of stub files to be converted
   */
  public static void main(String[] args) {
    if (args.length > 0) {
      for (int i = 0; i < args.length; i++) {
        String f0 = args[i];
        String f1 = (f0.endsWith(".astub") ? f0.substring(0, f0.length()-5)
            : f0) + "jaif";
        try (InputStream in = new FileInputStream(f0)) {
          try (FileOutputStream out = new FileOutputStream(f1)) {
            IndexUnit iu = JavaParser.parse(in);
            AScene scene = extractScene(iu);
            try (Writer w = new BufferedWriter(new OutputStreamWriter(out))) {
              IndexFileWriter.write(scene, w);
            } catch (Exception e) {
              e.printStackTrace();  // TODO
            }
          } catch (Exception e) {
            e.printStackTrace();  // TODO
          }
        } catch (Exception e) {
          e.printStackTrace();  // TODO
        }
      }
    } else {
      try {
        IndexUnit iu = JavaParser.parse(System.in);
        AScene scene = extractScene(iu);
        try (Writer w = new BufferedWriter(new OutputStreamWriter(System.out))) {
          IndexFileWriter.write(scene, w);
        } catch (Exception e) {
          e.printStackTrace();  // TODO
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }

  /**
   * Entry point of recursive-descent IndexUnit to AScene transformer.
   *
   * @param iu {@link IndexUnit} representing stubfile
   * @return {@link AScene} containing annotations from stubfile
   */
  private static AScene extractScene(IndexUnit iu) {
    AScene scene = new AScene();
    for (CompilationUnit cu : iu.getCompilationUnits()) {
      List typeDecls = cu.getTypes();
      if (typeDecls != null) {
        List impDecls = cu.getImports();
        PackageDeclaration pkgDecl = cu.getPackage();
        for (TypeDeclaration typeDecl : typeDecls) {
          ToIndexFileConverter converter =
              new ToIndexFileConverter(pkgDecl, impDecls, scene);
          String pkgName = converter.pkgName;
          String name = typeDecl.getName();
          if (!pkgName.isEmpty()) { name = pkgName + "." + name; }
          typeDecl.accept(converter, scene.classes.vivify(name));
        }
      }
    }
    scene.prune();
    return scene;
  }

  private static Annotation extractAnnotation(AnnotationExpr expr) {
    //String exprName = expr.getName().getName();
    String exprName = expr.toString().substring(1);  // 1 for '@'
    //String exprName = resolve(expr.getName().getName());
    AnnotationDef def = new AnnotationDef(exprName);
    def.setFieldTypes(Collections.emptyMap());
    return new Annotation(def, Collections.emptyMap());
  }

  public Void visit(AnnotationDeclaration decl, AElement elem) {
    return null;
  }

  @Override
  public Void visit(ClassOrInterfaceDeclaration decl, AElement elem) {
    visitDecl(decl, (ADeclaration) elem);
    return super.visit(decl, elem);
  }

  @Override
  public Void visit(ConstructorDeclaration decl, AElement elem) {
    int i = 0;
    List params = decl.getParameters();
    List rcvrAnnos = decl.getReceiverAnnotations();
    BlockStmt body = decl.getBlock();
    StringBuilder sb = new StringBuilder("(");
    AClass clazz = (AClass) elem;
    AMethod method;
    if (params != null) {
      for (Parameter param : params) {
        Type ptype = param.getType();
        sb.append(getJVML(ptype));
      }
    }
    sb.append(")V");
    method = clazz.methods.vivify(sb.toString());
    visitDecl(decl, method);
    if (params != null) {
      for (Parameter param : params) {
        AField field = method.parameters.vivify(i++);
        visitType(param.getType(), field.type);
      }
    }
    if (rcvrAnnos != null) {
      for (AnnotationExpr expr : rcvrAnnos) {
        Annotation anno = extractAnnotation(expr);
        method.receiver.tlAnnotationsHere.add(anno);
      }
    }
    return body == null ? null : body.accept(this, method);
    //return super.visit(decl, elem);
  }

  @Override
  public Void visit(EnumConstantDeclaration decl, AElement elem) {
    AField field = ((AClass) elem).fields.vivify(decl.getName());
    visitDecl(decl, field);
    return super.visit(decl, field);
  }

  @Override
  public Void visit(EnumDeclaration decl, AElement elem) {
    visitDecl(decl, (ADeclaration) elem);
    return super.visit(decl, elem);
  }

  @Override
  public Void visit(FieldDeclaration decl, AElement elem) {
    for (VariableDeclarator v : decl.getVariables()) {
      AClass clazz = (AClass) elem;
      AField field = clazz.fields.vivify(v.getId().getName());
      visitDecl(decl, field);
      visitType(decl.getType(), field.type);
    }
    return null;
  }

  @Override
  public Void visit(InitializerDeclaration decl, AElement elem) {
    BlockStmt block = decl.getBlock();
    AClass clazz = (AClass) elem;
    block.accept(this,
        clazz.methods.vivify(decl.isStatic() ? "" : ""));
    return null;
  }

  @Override
  public Void visit(MethodDeclaration decl, AElement elem) {
    Type type = decl.getType();
    List params = decl.getParameters();
    List typeParams = decl.getTypeParameters();
    List rcvrAnnos = decl.getReceiverAnnotations();
    BlockStmt body = decl.getBody();
    StringBuilder sb = new StringBuilder(decl.getName()).append('(');
    AClass clazz = (AClass) elem;
    AMethod method;
    if (params != null) {
      for (Parameter param : params) {
        Type ptype = param.getType();
        sb.append(getJVML(ptype));
      }
    }
    sb.append(')').append(getJVML(type));
    method = clazz.methods.vivify(sb.toString());
    visitDecl(decl, method);
    visitType(type, method.returnType);
    if (params != null) {
      int i = 0;
      for (Parameter param : params) {
        AField field = method.parameters.vivify(i++);
        visitType(param.getType(), field.type);
      }
    }
    if (rcvrAnnos != null) {
      for (AnnotationExpr expr : rcvrAnnos) {
        Annotation anno = extractAnnotation(expr);
        method.receiver.type.tlAnnotationsHere.add(anno);
      }
    }
    if (typeParams != null) {
      int i = 0;
      for (TypeParameter typeParam : typeParams) {
        List bounds = typeParam.getTypeBound();
        if (bounds != null) {
          int j = 0;
          for (ClassOrInterfaceType bound : bounds) {
            BoundLocation loc = new BoundLocation(i, j);
            bound.accept(this, method.bounds.vivify(loc));
            ++j;
          }
        }
        ++i;
      }
    }
    return body == null ? null : body.accept(this, method);
  }

  @Override
  public Void visit(ObjectCreationExpr expr, AElement elem) {
    ClassOrInterfaceType type = expr.getType();
    AClass clazz = scene.classes.vivify(type.getName());
    Expression scope = expr.getScope();
    List typeArgs = expr.getTypeArgs();
    List args = expr.getArgs();
    List decls = expr.getAnonymousClassBody();
    if (scope != null) {
      scope.accept(this, elem);
    }
    if (args != null) {
      for (Expression arg : args) {
        arg.accept(this, elem);
      }
    }
    if (typeArgs != null) {
      for (Type typeArg : typeArgs) {
        typeArg.accept(this, elem);
      }
    }
    type.accept(this, clazz);
    if (decls != null) {
      for (BodyDeclaration decl : decls) {
        decl.accept(this, clazz);
      }
    }
    return null;
  }

  @Override
  public Void visit(VariableDeclarationExpr expr, AElement elem) {
    List annos = expr.getAnnotations();
    AMethod method = (AMethod) elem;
    int i = 0;
    for (VariableDeclarator decl : expr.getVars()) {
      LocalLocation loc = new LocalLocation(decl.getId().getName(), i);
      AField field = method.body.locals.vivify(loc);
      visitType(expr.getType(), field.type);
      if (annos != null) {
        for (AnnotationExpr annoExpr : annos) {
          Annotation anno = extractAnnotation(annoExpr);
          field.tlAnnotationsHere.add(anno);
        }
      }
      ++i;
    }
    return null;
  }

  private Void visitDecl(BodyDeclaration decl, ADeclaration elem) {
    List annoExprs = decl.getAnnotations();
    if (annoExprs != null) {
      for (AnnotationExpr annoExpr : annoExprs) {
        Annotation anno = extractAnnotation(annoExpr);
        elem.tlAnnotationsHere.add(anno);
      }
    }
    return null;
  }

  private Void visitType(Type type, final ATypeElement elem) {
    List exprs = type.getAnnotations();
    if (exprs != null) {
      for (AnnotationExpr expr : exprs) {
        Annotation anno = extractAnnotation(expr);
        elem.tlAnnotationsHere.add(anno);
      }
    }
    visitInnerTypes(type, elem);
    return null;
  }

  private static Void visitInnerTypes(Type type, final ATypeElement elem) {
    return visitInnerTypes(type, elem,
        InnerTypeLocation.EMPTY_INNER_TYPE_LOCATION);
  }

  private static Void visitInnerTypes(Type type, final ATypeElement elem,
      InnerTypeLocation loc) {
    return type.accept(new GenericVisitorAdapter() {
      @Override
      public Void visit(ClassOrInterfaceType type, InnerTypeLocation loc) {
        int i = 0;
        for (Type inner : type.getTypeArgs()) {
          InnerTypeLocation ext = extendedTypePath(loc, 3, i++);
          visitInnerType(inner, ext);
        }
        return null;
      }

      @Override
      public Void visit(ReferenceType type, InnerTypeLocation loc) {
        InnerTypeLocation ext = loc;
        int n = type.getArrayCount();
        for (int i = 0; i < n; i++) {
          ext = extendedTypePath(ext, 1, 0);
          for (AnnotationExpr expr : type.getAnnotationsAtLevel(i)) {
            ATypeElement typeElem = elem.innerTypes.vivify(ext);
            Annotation anno = extractAnnotation(expr);
            typeElem.tlAnnotationsHere.add(anno);
          }
        }
        return null;
      }

      @Override
      public Void visit(WildcardType type, InnerTypeLocation loc) {
        ReferenceType lower = type.getExtends();
        ReferenceType upper = type.getSuper();
        if (lower != null) {
          InnerTypeLocation ext = extendedTypePath(loc, 2, 0);
          visitInnerType(lower, ext);
        }
        if (upper != null) {
          InnerTypeLocation ext = extendedTypePath(loc, 2, 0);
          visitInnerType(upper, ext);
        }
        return null;
      }

      private void visitInnerType(Type type, InnerTypeLocation loc) {
        visitInnerType(type, loc, type.getAnnotations());
      }

      private void visitInnerType(Type type, InnerTypeLocation loc,
          Collection annos) {
        ATypeElement typeElem = elem.innerTypes.vivify(loc);
        for (AnnotationExpr expr : annos) {
          Annotation anno = extractAnnotation(expr);
          typeElem.tlAnnotationsHere.add(anno);
          type.accept(this, loc);
        }
      }

      private InnerTypeLocation extendedTypePath(InnerTypeLocation loc,
          int tag, int arg) {
        List path =
            new ArrayList(loc.location.size()+1);
        path.addAll(loc.location);
        path.add(TypePathEntry.fromBinary(tag, arg));
        return new InnerTypeLocation(path);
      }
    }, loc);
  }

  /**
   * Computes a type's "binary name".
   *
   * @param type the type
   * @return the type's binary name
   */
  private String getJVML(Type type) {
    return type.accept(new GenericVisitorAdapter() {
      @Override
      public String visit(ClassOrInterfaceType type, Void v) {
        String typeName = type.getName();
        String name = resolve(typeName);
        if (name != null) {
          String[] parts = name.split("\\.");
          StringBuilder sb = new StringBuilder("L").append(parts[0]);
          for (int i = 1; i < parts.length; i++) {
            sb.append('/').append(parts[i]);
          }
          sb.append(";");
          return sb.toString();
        }
        return "L" + typeName + ";";
      }

      @Override
      public String visit(PrimitiveType type, Void v) {
        switch (type.getType()) {
        case Boolean:
          return "Z";
        case Byte:
          return "B";
        case Char:
          return "C";
        case Double:
          return "D";
        case Float:
          return "F";
        case Int:
          return "I";
        case Long:
          return "J";
        case Short:
          return "S";
        default:
          throw new IllegalArgumentException(
              "baseTypeName(): unknown primitive type " + type);
        }
      }

      @Override
      public String visit(ReferenceType type, Void v) {
        String typeName = type.getType().accept(this, null);
        StringBuilder sb = new StringBuilder();
        int n = type.getArrayCount();
        while (--n >= 0) { sb.append("["); }
        sb.append(typeName);
        return sb.toString();
      }

      @Override
      public String visit(VoidType type, Void v) {
        return "V";
      }

      @Override
      public String visit(WildcardType type, Void v) {
        return "Ljava/lang/Object;";
      }
    }, null);
  }

  /**
   * Finds the fully qualified name of the class with the given name.
   *
   * @param className possibly unqualified name of class
   * @return fully qualified name of class that {@code className}
   * identifies in the current context
   */
  private String resolve(String className) {
    String qualifiedName;
    Class resolved = null;

    if (pkgName.isEmpty()) {
      qualifiedName = className;
      resolved = loadClass(qualifiedName);
      if (resolved == null) {
        qualifiedName = "java.lang." + className;
        resolved = loadClass(qualifiedName);
      }
    } else {
      qualifiedName = pkgName + "." + className;
      resolved = loadClass(qualifiedName);
      if (resolved == null) {
        qualifiedName = "java.lang." + className;
        resolved = loadClass(qualifiedName);
        if (resolved == null) {
          qualifiedName = className;
          resolved = loadClass(qualifiedName);
        }
      }
    }

    if (resolved == null) {
      for (String declName : imports) {
        qualifiedName = mergePrefix(declName, className);
        if (qualifiedName != null) { return qualifiedName; }
      }
      return className;
    }
    return qualifiedName;
  }

  private static String mergePrefix(String prefix, String base) {
    if (prefix.isEmpty() || prefix.equals(base)) {
      return base;
    } else {
      String[] a0 = prefix.split("\\.");
      String[] a1 = base.split("\\.");
      String prefixEnd = a0[a0.length-1];
      if ("*".equals(prefixEnd)) {
        int n = a0.length-1;
        String[] a = Arrays.copyOf(a0, n + a1.length);
        for (int i = 0; i < a1.length; i++) { a[n+i] = a1[i]; }
        return PluginUtil.join(".", a);
      } else {
        int i = a0.length;
        int n = i - a1.length;
        while (--i >= n) {
          if (!a1[i-n].equals(a0[i])) { return null; }
        }
        return prefix;
      }
    }
  }

  private static Class loadClass(String className) {
    if (className != null) {
      try {
        return Class.forName(className, false, null);
      } catch (ClassNotFoundException e) {}
    }
    return null;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy