Package se.arnetheduck.j2c.transform

Source Code of se.arnetheduck.j2c.transform.TransformUtil

package se.arnetheduck.j2c.transform;

import java.io.PrintWriter;
import java.lang.reflect.Modifier;
import java.text.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IInitializer;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ArrayCreation;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldAccess;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.IPackageBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.InfixExpression;
import org.eclipse.jdt.core.dom.InfixExpression.Operator;
import org.eclipse.jdt.core.dom.Initializer;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.NullLiteral;
import org.eclipse.jdt.core.dom.NumberLiteral;
import org.eclipse.jdt.core.dom.ParenthesizedExpression;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeParameter;
import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;

public final class TransformUtil {
  public static final String NATIVE = "native";
  public static final String STUB = "stub";

  public static final Map<String, String> primitives = new HashMap<String, String>() {
    {
      put("boolean", "java.lang.Boolean");
      put("char", "java.lang.Character");
      put("byte", "java.lang.Byte");
      put("short", "java.lang.Short");
      put("int", "java.lang.Integer");
      put("long", "java.lang.Long");
      put("float", "java.lang.Float");
      put("double", "java.lang.Double");
    }
  };

  public static final Map<String, String> reverses = new HashMap<String, String>() {
    {
      for (Map.Entry<String, String> x : primitives.entrySet()) {
        put(x.getValue(), x.getKey());
      }
    }
  };

  public static String packageHeader(String project, String packageName) {
    String prefix = packageName == null || packageName.isEmpty() ? ""
        : toFileName(packageName) + "/";

    return prefix + "fwd-" + project + ".hpp";
  }

  public static String qualifiedName(ITypeBinding tb) {
    IPackageBinding pkg = elementPackage(tb);
    return pkg == null || pkg.getName().isEmpty() ? CName.of(tb) : (CName
        .of(pkg) + "." + CName.of(tb));
  }

  /** The type of a variable, taking volatile into account */
  public static String varTypeCName(int modifiers, ITypeBinding tb,
      ITypeBinding type, DepInfo deps) {
    boolean vol = Modifier.isVolatile(modifiers);

    String ret = "";
    if (vol) {
      ret += "std::atomic< ";
      deps.setNeedsAtomic();
    }

    ret += relativeRef(tb, type, true);

    if (vol) {
      ret += " >";
    }

    return ret;
  }

  /** The type of a variable, taking volatile into account */
  public static String varTypeCName(int modifiers, ITypeBinding tb,
      DepInfo deps) {
    boolean vol = Modifier.isVolatile(modifiers);

    String ret = "";
    if (vol) {
      ret += "std::atomic< ";
      deps.setNeedsAtomic();
    }

    ret += qualifiedRef(tb, false);

    if (vol) {
      ret += " >";
    }

    return ret;
  }

  public static IPackageBinding elementPackage(ITypeBinding tb) {
    // When processing generics, only the erasure will have a package
    return tb.isArray() ? tb.getElementType().getErasure().getPackage()
        : tb.getErasure().getPackage();
  }

  public static String include(ITypeBinding tb) {
    return include(headerName(tb));
  }

  public static String include(String s) {
    return "#include <" + s + ">";
  }

  public static IPath headerPath(IPath root, ITypeBinding tb) {
    return headerPath(root, headerName(tb));
  }

  public static IPath headerPath(IPath root, String name) {
    return root.append("src").append(name);
  }

  public static String headerName(ITypeBinding tb) {
    if (tb.isArray()) {
      ITypeBinding ct = tb.getComponentType();
      if (ct.isPrimitive()) {
        return "Array.hpp";
      } else if (TransformUtil.same(ct, Object.class)) {
        return "ObjectArray.hpp";
      }
      return "SubArray.hpp";
    }

    return toFileName(qualifiedName(tb)) + ".hpp";
  }

  public static IPath implPath(IPath root, ITypeBinding tb, String suffix) {
    return root.append(suffix.length() == 0 ? "src" : suffix).append(
        implName(tb, suffix));
  }

  public static String implName(ITypeBinding tb, String suffix) {
    if (suffix.length() > 0) {
      suffix = "-" + suffix;
    }

    return toFileName(qualifiedName(tb)) + suffix + ".cpp";
  }

  public static String mainName(ITypeBinding tb) {
    return toFileName(qualifiedName(tb)) + "-main.cpp";
  }

  private static String toFileName(String s) {
    // Annoying character to escape
    return s.replaceAll("\\$", "_").replaceAll("\\.", "/");
  }

  /** JLS §4.12.5 Initial Values of Variables */
  public static String initialValue(IVariableBinding vb) {
    Object cv = constexprValue(vb);
    if (cv != null) {
      return checkConstant(cv);
    }

    if (isStatic(vb)) {
      // Statics don't need explicit initialization
      return null;
    }

    // The rest match C++'s default initialization values
    return "";
  }

  public static Object constantValue(VariableDeclarationFragment node) {
    Expression expr = node.getInitializer();
    if (expr == null) {
      return null;
    }

    Object v = expr.resolveConstantExpressionValue();
    if (v == null) {
      return null;
    }

    IVariableBinding vb = node.resolveBinding();
    if (!vb.getType().isPrimitive() && !(v instanceof String)) {
      return null;
    }

    if (!isFinal(vb)) {
      return null;
    }

    return v;
  }

  public static Object constexprValue(VariableDeclarationFragment node) {
    Object cv = constantValue(node);
    return cv instanceof String ? null : cv;
  }

  public static Object constantValue(IVariableBinding vb) {
    Object v = vb.getConstantValue();
    if (v == null) {
      return null;
    }

    if (!vb.getType().isPrimitive() && !(v instanceof String)) {
      return null;
    }

    if (!isFinal(vb)) {
      return null;
    }

    return v;
  }

  public static Object constexprValue(IVariableBinding vb) {
    Object cv = constantValue(vb);
    return cv instanceof String ? null : cv;
  }

  /**
   * We make static methods out of static non-constexpr variables to get a
   * chance to initialize the class before variable access
   */
  public static boolean asMethod(IVariableBinding vb) {
    return vb.isField() && isStatic(vb) && constexprValue(vb) == null
        && !vb.isEnumConstant();
  }

  public static String checkConstant(Object cv) {
    return checkConstant(cv, true);
  }

  public static String checkConstant(Object cv, boolean forceType) {
    if (cv instanceof Byte) {
      return "int8_t(" + cv + ")";
    }

    if (cv instanceof Character) {
      char ch = (Character) cv;

      if (ch == '\'') {
        return "u'\\''";
      }

      if (ch == '\\') {
        return "u'\\\\'";
      }

      if (ch >= 0xd800 && ch <= 0xdfff || ch == 0x0000 || ch == 0xffff) {
        // These are not valid for the \\u syntax
        // 0x0000 is a G++ bug:
        // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53690
        // 0xffff is a G++ bug:
        // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=41698
        return String.format("char16_t(0x%04x)", (int) ch);
      }

      if (ch < ' ' || ch > 127) {
        // These depend on source file charset, so play it safe
        return String.format("u'\\u%04x'", (int) ch);
      }

      return "u'" + ch + "'";
    }

    if (cv instanceof Integer) {
      // This is not quite correct - an unadorned integer literal in C++
      // is considered to be of int type, which is only guaranteed to hold
      // values in the range -32767..32767
      // so any larger values should have the 'L' suffix - investigate

      if ((Integer) cv == Integer.MIN_VALUE) {
        // In C++, the part after '-' is parsed first which overflows
        // so we do a trick
        return "int32_t(-0x7fffffff-1)";
      }

      if (!forceType) {
        return cv.toString();
      }

      return "int32_t(" + cv + ")";
    }

    if (cv instanceof Long) {
      if ((Long) cv == Long.MIN_VALUE) {
        // In C++, the part before '-' is parsed first which overflows
        // so we do a trick
        return "int64_t(-0x7fffffffffffffffLL-1)";
      }

      if (!forceType) {
        return cv + "LL";
      }

      return "int64_t(" + cv + "LL)";
    }

    if (cv instanceof Float) {
      float f = (Float) cv;
      if (Float.isNaN(f)) {
        return "std::numeric_limits<float>::quiet_NaN()";
      } else if (f == Float.POSITIVE_INFINITY) {
        return "std::numeric_limits<float>::infinity()";
      } else if (f == Float.NEGATIVE_INFINITY) {
        return "(-std::numeric_limits<float>::infinity())";
      }

      return cv + "f";
    }

    if (cv instanceof Double) {
      double f = (Double) cv;
      if (Double.isNaN(f)) {
        return "std::numeric_limits<double>::quiet_NaN()";
      } else if (f == Double.POSITIVE_INFINITY) {
        return "std::numeric_limits<double>::infinity()";
      } else if (f == Double.NEGATIVE_INFINITY) {
        return "(-std::numeric_limits<double>::infinity())";
      }

      return "" + cv;
    }

    if (cv instanceof Short) {
      if (!forceType) {
        return cv.toString();
      }

      return "int16_t(" + cv + ")";
    }

    return cv == null ? null : cv.toString();
  }

  /**
   * Clean up java-escaped string literals for C++.
   *
   * In java, it is valid to have lone UTF-16 surrogates - in C++, not.
   */
  public static String stringLiteral(String escaped) {
    Pattern p = Pattern.compile("\\\\u([dD][89abcdefABCDEF]..)");
    Matcher m = p.matcher(escaped);
    return m.replaceAll("\\\\x$1");
  }

  public static String fieldModifiers(ITypeBinding type, int modifiers,
      boolean header, boolean isConstExpr) {
    String ret = "";
    if (header
        && (Modifier.isStatic(modifiers) || type.isInterface() || isConstExpr)) {
      ret += "static ";
    }

    if (isConstExpr && (Modifier.isFinal(modifiers) || type.isInterface())) {
      ret += "constexpr ";
    }

    return ret;
  }

  public static String methodModifiers(IMethodBinding mb) {
    if (isStatic(mb)) {
      return "static ";
    }

    if (isPrivate(mb)) {
      return "";
    }

    if (isFinal(mb)) {
      return "";
    }

    if (needsSpecifier(mb)) {
      return "";
    }

    // Java methods virtual by default
    return "virtual ";
  }

  public static String methodSpecifiers(IMethodBinding mb) {
    if (Modifier.isAbstract(mb.getModifiers())) {
      return " = 0";
    }

    if (needsSpecifier(mb)) {
      /*
       * Can't do this because the method might need a bridge further down
       * the inheritance chain
       *
       * if (isFinal(mb)) { return " final"; }
       */
      return " override";
    }

    return "";
  }

  /** Does the method need a final/override specifier? */
  public static final boolean needsSpecifier(IMethodBinding mb) {
    List<IMethodBinding> baseMethods = TypeUtil.methods(
        TypeUtil.allBases(mb.getDeclaringClass(), null),
        TypeUtil.overrides(mb));

    for (IMethodBinding mb2 : baseMethods) {
      if (!needsBridge(mb, mb2)) {
        return true;
      }
    }

    return false;
  }

  public static String variableModifiers(ITypeBinding type, int modifiers) {
    return fieldModifiers(type, modifiers, false, false);
  }

  public static String typeArguments(Collection<Type> parameters) {
    if (parameters.isEmpty()) {
      return "";
    }

    return "/* <" + toCSV(parameters) + "> */";
  }

  public static String typeParameters(Collection<TypeParameter> parameters) {
    if (parameters.isEmpty()) {
      return "";
    }

    return "/* <" + toCSV(parameters) + "> */";
  }

  public static String throwsDecl(Collection<Name> parameters) {
    if (parameters.isEmpty()) {
      return "";
    }

    return " /* throws(" + toCSV(parameters) + ") */";
  }

  public static String annotations(Collection<Annotation> annotations) {
    if (annotations.isEmpty()) {
      return "";
    }

    return "/* " + toCSV(annotations, " ") + " */";
  }

  public static String toCSV(Collection<?> c) {
    return toCSV(c, ", ");
  }

  public static String toCSV(Collection<?> c, String separator) {
    StringBuilder builder = new StringBuilder();
    String s = "";
    for (Object o : c) {
      builder.append(s);
      s = separator;
      builder.append(o);
    }

    return builder.toString();
  }

  public static String primitive(String token) {
    return primitive(PrimitiveType.toCode(token));
  }

  public static String primitive(PrimitiveType.Code code) {
    if (code == PrimitiveType.BOOLEAN) {
      return "bool";
    } else if (code == PrimitiveType.BYTE) {
      return "int8_t";
    } else if (code == PrimitiveType.CHAR) {
      return "char16_t";
    } else if (code == PrimitiveType.DOUBLE) {
      return "double";
    } else if (code == PrimitiveType.FLOAT) {
      return "float";
    } else if (code == PrimitiveType.INT) {
      return "int32_t";
    } else if (code == PrimitiveType.LONG) {
      return "int64_t";
    } else if (code == PrimitiveType.SHORT) {
      return "int16_t";
    } else if (code == PrimitiveType.VOID) {
      return "void";
    } else {
      throw new RuntimeException("Unknown primitive type");
    }
  }

  public static boolean addDep(ITypeBinding dep, Collection<ITypeBinding> deps) {
    if (dep == null) {
      return false;
    }

    if (dep.isNullType() || isVoid(dep)) {
      return false;
    }

    dep = dep.getErasure();

    boolean found = false;
    // This ensures that the new dep is always added last (useful when
    // ordering bases)
    for (ITypeBinding d : deps) {
      if (d.isEqualTo(dep)) {
        deps.remove(d);
        found = true;
        break;
      }
    }

    deps.add(dep);
    return !found;
  }

  public static String ref(ITypeBinding tb) {
    return tb.isPrimitive() ? "" : "*";
  }

  public static String qualifiedRef(ITypeBinding tb, boolean global) {
    return CName.qualified(tb, global) + ref(tb);
  }

  public static String relativeRef(ITypeBinding tb, ITypeBinding root,
      boolean global) {
    return CName.relative(tb, root, global) + ref(tb);
  }

  public static String constVar(IVariableBinding vb) {
    if (isConstVar(vb)) {
      return "const ";
    }

    return "";
  }

  public static boolean isConstVar(IVariableBinding vb) {
    return !vb.isField() && isFinal(vb);
  }

  public static String ref(Type t) {
    return t.isPrimitiveType() ? "" : "*";
  }

  public static boolean isDefault(int modifiers) {
    return !Modifier.isPrivate(modifiers)
        && !Modifier.isProtected(modifiers)
        && !Modifier.isPublic(modifiers);
  }

  public static boolean isFinal(ITypeBinding tb) {
    return Modifier.isFinal(tb.getModifiers());
  }

  public static boolean isFinal(IVariableBinding vb) {
    return Modifier.isFinal(vb.getModifiers())
        || (vb.isField() && vb.getDeclaringClass().isInterface());
  }

  public static boolean isFinal(IMethodBinding mb) {
    return Modifier.isFinal(mb.getModifiers())
        || (mb.getDeclaringClass() != null && isFinal(mb
            .getDeclaringClass()));
  }

  public static boolean isStatic(ITypeBinding tb) {
    return Modifier.isStatic(tb.getModifiers());
  }

  public static boolean isStatic(IMethodBinding mb) {
    return Modifier.isStatic(mb.getModifiers());
  }

  public static boolean isStatic(FieldDeclaration declaration) {
    return Modifier.isStatic(declaration.getModifiers());
  }

  public static boolean isStatic(VariableDeclarationFragment fragment) {
    return isStatic(fragment.resolveBinding());
  }

  public static boolean isStatic(Initializer initializer) {
    return Modifier.isStatic(initializer.getModifiers());
  }

  public static boolean isPrivate(IMethodBinding mb) {
    return Modifier.isPrivate(mb.getModifiers());
  }

  public static boolean isStatic(IVariableBinding vb) {
    return Modifier.isStatic(vb.getModifiers())
        || (vb.isField() && vb.getDeclaringClass() != null && vb
            .getDeclaringClass().isInterface());
  }

  public static boolean hasOuterThis(ITypeBinding tb) {
    if (tb.isArray() || tb.isInterface() || tb.isAnnotation()) {
      return false;
    }

    if (tb.isLocal()) {
      IMethodBinding mb = tb.getDeclaringMethod();
      if (mb == null) {
        // Could be https://bugs.eclipse.org/bugs/show_bug.cgi?id=383486
        IJavaElement je = tb.getJavaElement();

        try {
          while (je != null) {
            if (je instanceof IMethod) {
              return !Flags.isStatic(((IMethod) je).getFlags());
            }

            if (je instanceof IInitializer) {
              return !Flags.isStatic(((IInitializer) je)
                  .getFlags());
            }

            if (je instanceof IField) {
              IField field = (IField) je;
              return !Flags.isStatic(field.getFlags())
                  && !Flags.isEnum(field.getFlags());
            }

            je = je.getParent();
          }
        } catch (JavaModelException e) {
          throw new Error(e);
        }

        return false;
      }

      return !isStatic(mb);
    }

    if (tb.isNested()) {
      return !isStatic(tb);
    }

    return false;
  }

  public static String outerThis(ITypeBinding tb) {
    return CName.relative(tb.getDeclaringClass(), tb, false) + " *"
        + outerThisName(tb);
  }

  public static String outerThisName(ITypeBinding tb) {
    return thisName(tb.getDeclaringClass());
  }

  public static String thisName(ITypeBinding tb) {
    return CName.of(tb) + "_this";
  }

  public static String indent(int n) {
    String ret = "";
    for (int i = 0; i < n; i++)
      ret += "    ";
    return ret;
  }

  public static String virtual(ITypeBinding type, ITypeBinding base) {
    if (isFinal(type)) {
      if (same(base, Object.class) && type.getInterfaces().length == 0) {
        // No interfaces, so Object only inherited once
        return "";
      }

      if (base.isInterface() && TypeUtil.countBases(type, base) == 1) {
        // Interface only inherited once in the chain
        return "";
      }
    }

    if (base.isInterface() || same(base, Object.class)) {
      // Might be inherited more than once
      return "virtual ";
    }

    return "";
  }

  public static void printi(PrintWriter out, int indent, String string) {
    out.print(indent(indent));
    out.print(string);
  }

  public static void printlni(PrintWriter out, int indent, String string) {
    printi(out, indent, string);
    out.println();
  }

  public static void printParams(PrintWriter out, ITypeBinding tb,
      IMethodBinding mb, boolean parens, DepInfo deps) {
    if (parens) {
      out.print("(");
    }

    for (int i = 0; i < mb.getParameterTypes().length; ++i) {
      if (i > 0)
        out.print(", ");

      ITypeBinding pb = mb.getParameterTypes()[i];
      deps.soft(pb);

      out.print(TransformUtil.relativeRef(pb, tb, true));
      out.print(" ");

      out.print(paramName(mb, i));
    }

    if (parens) {
      out.print(")");
    }
  }

  public static String paramName(IMethodBinding mb, int i) {
    try {
      IMethod md = (IMethod) mb.getJavaElement();
      return md == null ? "a" + i : CName
          .keywords(md.getParameterNames()[i]);
    } catch (JavaModelException e) {
      return "a" + i;
    }
  }

  /** Check if super-interface (or Object) has the same method already */
  public static boolean baseHasSame(IMethodBinding mb, ITypeBinding tb,
      ITypeBinding object) {
    for (ITypeBinding ib : tb.getInterfaces()) {
      for (IMethodBinding mb2 : ib.getDeclaredMethods()) {
        if (sameSignature(mb, mb2)) {
          return true;
        }
      }

      if (baseHasSame(mb, ib, object)) {
        return true;
      }
    }

    if (tb.isInterface()) {
      for (IMethodBinding mb2 : object.getDeclaredMethods()) {
        if (sameSignature(mb, mb2)) {
          return true;
        }
      }
    }

    return false;
  }

  public static boolean asBaseConstructor(IMethodBinding mb, ITypeBinding tb) {
    if (!mb.isConstructor()) {
      return false;
    }

    if (!Modifier.isPrivate(mb.getModifiers())) {
      return true;
    }

    IJavaElement mbje = mb.getJavaElement();
    IJavaElement tbje = tb.getJavaElement();
    if (mbje == null || tbje == null) {
      return false;
    }

    IJavaElement mbcu = mbje.getAncestor(IJavaElement.COMPILATION_UNIT);
    IJavaElement tbcu = tbje.getAncestor(IJavaElement.COMPILATION_UNIT);
    return mbcu != null && mbcu.equals(tbcu);
  }

  public static Collection<ITypeBinding> returnDeps(ITypeBinding type,
      IMethodBinding mb, ITypeBinding object) {
    List<ITypeBinding> ret = new ArrayList<ITypeBinding>();
    if (mb.isConstructor()) {
      return ret;
    }

    List<IMethodBinding> methods = TypeUtil.methods(
        TypeUtil.allBases(type, object), TypeUtil.overrides(mb));

    for (IMethodBinding mb2 : methods) {
      if (!mb2.isConstructor() && returnCovariant(mb, mb2)
          && !returnType(type, mb).isEqualTo(returnType(type, mb2))) {
        addDep(returnType(type, mb), ret);
      }
    }

    return ret;
  }

  public static String declareBridge(Transformer ctx, PrintWriter pw,
      ITypeBinding tb, IMethodBinding mb, DepInfo deps, String access) {
    List<IMethodBinding> methods = TypeUtil.methods(
        TypeUtil.allBases(tb, null), TypeUtil.overrides(mb));
    for (IMethodBinding mb2 : methods) {
      if (needsBridge(mb, mb2)) {
        mb2 = mb2.getMethodDeclaration();

        access = Header.printAccess(pw, mb2, access);
        pw.print(indent(1));

        printSignature(ctx, pw, tb, mb2, deps, false);

        pw.print(" override");

        pw.println(";");
        break;
      }
    }

    return access;
  }

  public static void defineBridge(Transformer ctx, PrintWriter pw,
      ITypeBinding tb, IMethodBinding mb, DepInfo deps) {
    List<IMethodBinding> methods = TypeUtil.methods(
        TypeUtil.allBases(tb, null), TypeUtil.overrides(mb));
    for (IMethodBinding mb2 : methods) {
      if (needsBridge(mb, mb2)) {
        mb2 = mb2.getMethodDeclaration();

        printSignature(ctx, pw, tb, mb2, deps, true);

        pw.println();
        pw.println("{ ");
        pw.print(indent(1));
        if (!isVoid(mb.getReturnType())) {
          pw.print("return ");

          if (!mb2.getReturnType().getErasure()
              .isEqualTo(mb.getReturnType().getErasure())) {
            deps.hard(mb.getReturnType());
          }
        }

        pw.print(CName.of(mb2));
        pw.print("(");
        for (int i = 0; i < mb2.getParameterTypes().length; ++i) {
          if (i > 0)
            pw.print(", ");
          ITypeBinding pb = mb.getParameterTypes()[i];
          ITypeBinding pb2 = mb2.getParameterTypes()[i];

          if (!pb.isEqualTo(pb2)) {
            deps.hard(pb);
            pw.print("dynamic_cast< ");
            pw.print(TransformUtil.relativeRef(pb, tb, true));
            pw.print(" >(");
            pw.print(paramName(mb2, i));
            pw.print(")");
          } else {
            pw.print(paramName(mb2, i));
          }
        }

        pw.println(");");
        pw.println("}");
        pw.println("");
        break;
      }
    }
  }

  private static boolean needsBridge(IMethodBinding mb, IMethodBinding mb2) {
    return !mb.isConstructor() && mb2 != null
        && !sameSignature(mb, mb2.getMethodDeclaration())
        && !returnCovariant(mb, mb2.getMethodDeclaration());
  }

  // There's a problem with IMethodBinding.isSubsignature, so we do it by hand
  // https://bugs.eclipse.org/bugs/show_bug.cgi?id=382907
  public static boolean isSubsignature(IMethodBinding a, IMethodBinding b) {
    return !a.isConstructor() && a.getName().equals(b.getName())
        && (sameParameters(a, b, false) || sameParameters(a, b, true));
  }

  private static boolean sameSignature(IMethodBinding mb, IMethodBinding mb2) {
    if (!mb.getName().equals(mb2.getName())) {
      return false;
    }

    return sameReturn(mb, mb2) && sameParameters(mb, mb2, true);
  }

  private static boolean sameReturn(IMethodBinding mb, IMethodBinding mb2) {
    return mb
        .getMethodDeclaration()
        .getReturnType()
        .getErasure()
        .isEqualTo(
            mb2.getMethodDeclaration().getReturnType().getErasure());

  }

  public static boolean sameParameters(IMethodBinding mb, IMethodBinding mb2,
      boolean erase) {
    if (mb.getParameterTypes().length != mb2.getParameterTypes().length) {
      return false;
    }

    if (erase) {
      mb = mb.getMethodDeclaration();
      mb2 = mb2.getMethodDeclaration();
    }

    for (int i = 0; i < mb.getParameterTypes().length; ++i) {
      if (!mb.getParameterTypes()[i].getErasure().isEqualTo(
          mb2.getParameterTypes()[i].getErasure())) {
        return false;
      }
    }

    return true;
  }

  public static ITypeBinding returnType(ITypeBinding type,
      MethodDeclaration md) {
    return returnType(type, md.resolveBinding());
  }

  public static boolean returnCovariant(IMethodBinding mb, IMethodBinding mb2) {
    return !sameReturn(mb, mb2) && sameParameters(mb, mb2, true);
  }

  public static void printSignature(Transformer ctx, PrintWriter pw,
      ITypeBinding tb, IMethodBinding mb, DepInfo deps, boolean qualified) {
    printSignature(ctx, pw, tb, mb, mb.getReturnType(), deps, qualified);
  }

  public static void printSignature(Transformer ctx, PrintWriter pw,
      ITypeBinding type, IMethodBinding mb, ITypeBinding rt,
      DepInfo deps, boolean qualified) {
    if (mb.isConstructor()) {
      pw.print("void ");
      if (qualified) {
        pw.print(CName.qualified(type, true));
        pw.print("::");
      }

      pw.print(CName.CTOR + "(");
      String sep = printEnumCtorParams(ctx, pw, type, "", deps);
      if (mb.getParameterTypes().length > 0) {
        pw.print(sep);
      }
    } else {
      deps.soft(rt);

      if (!qualified) {
        pw.print(methodModifiers(mb));
        pw.print(TransformUtil.relativeRef(rt, type, true));
      } else {
        pw.print(TransformUtil.qualifiedRef(rt, false));
      }

      pw.print(" ");

      if (qualified) {
        pw.print(CName.qualified(type, false));
        pw.print("::");
      }

      pw.print(CName.of(mb) + "(");
    }

    printParams(pw, type, mb, false, deps);
    pw.print(")");
  }

  public static String printExtraCtorParams(Transformer ctx, PrintWriter pw,
      ITypeBinding type, Collection<IVariableBinding> closures,
      DepInfo deps, boolean isDefaultInitCtor, IMethodBinding ctor) {
    String sep = "";
    if (hasOuterThis(type)) {
      pw.print(outerThis(type));
      sep = ", ";
    }

    if (closures != null) {
      for (IVariableBinding closure : closures) {
        pw.print(sep
            + varTypeCName(closure.getModifiers(),
                closure.getType(), type, deps) + " "
            + ctorClosureName(closure, type, ctor));
        sep = ", ";
      }
    }

    if (isDefaultInitCtor) {
      pw.print(sep + "const ::" + CName.DEFAULT_INIT_TAG + "&");
      sep = ", ";
    } else {
      sep = printEnumCtorParams(ctx, pw, type, sep, deps);
    }

    return sep;
  }

  // There may be a conflict between the name of a closure an a constructor
  // parameter - avoid this conflict by renaming the closure
  public static String ctorClosureName(IVariableBinding closure,
      ITypeBinding type, IMethodBinding ctor) {
    String ret = CName.of(closure, type);
    for (int i = 0; ctor != null && i < ctor.getParameterTypes().length; ++i) {
      if (ret.equals(paramName(ctor, i))) {
        ret = ret + "_";
        i = 0; // Start over
      }
    }
    return ret;
  }

  public static String printEnumCtorParams(Transformer ctx, PrintWriter pw,
      ITypeBinding type, String sep, DepInfo deps) {
    if (type.isEnum()) {
      pw.print(sep + "::java::lang::String* name, int ordinal");
      deps.soft(ctx.resolve(String.class));
      return ", ";
    }

    return sep;
  }

  public static void printEmptyCtorCall(PrintWriter pw, ITypeBinding type) {
    pw.print(CName.CTOR + "(");
    TransformUtil.printEnumCtorCallParams(pw, type, "");
    pw.print(")");
  }

  public static String printEnumCtorCallParams(PrintWriter pw,
      ITypeBinding type, String sep) {
    if (type.isEnum()) {
      pw.print(sep + "name, ordinal");
      return ", ";
    }

    return sep;
  }

  public static boolean isVoid(ITypeBinding tb) {
    return tb == null || tb.getName().equals("void");
  }

  public static boolean isMain(IMethodBinding mb) {
    return mb.getReturnType() != null
        && isVoid(mb.getReturnType())
        && mb.getName().equals("main")
        && mb.getParameterTypes().length == 1
        && mb.getParameterTypes()[0].isArray()
        && same(mb.getParameterTypes()[0].getComponentType(),
            String.class);
  }

  public static void printStringSupport(ITypeBinding tb, PrintWriter pw) {
    if (!same(tb, String.class)) {
      return;
    }

    pw.println("namespace java { namespace lang { String *operator \"\" _j(const char16_t *p, size_t n); } }");
    pw.println("using java::lang::operator \"\" _j;");
    pw.println();
  }

  public static boolean isPrimitiveArray(ITypeBinding tb) {
    return tb.isArray() && tb.getComponentType().isPrimitive();
  }

  public static boolean same(ITypeBinding type, Class<?> klazz) {
    return type.getErasure().getQualifiedName().equals(klazz.getName());
  }

  public static boolean variableErased(IVariableBinding b) {
    return !b.getType().isRawType()
        && !b.getType().isEqualTo(
            b.getVariableDeclaration().getType().getErasure());
  }

  public static boolean returnErased(IMethodBinding b) {
    return !b.getReturnType().isEqualTo(
        b.getMethodDeclaration().getReturnType().getErasure());
  }

  // If a method is return covariant and the returned type
  // is a subclass of the declaring class, we get a circular
  // include dependency in C++ - resolve by making the return
  // type not covariant
  public static ITypeBinding returnType(ITypeBinding type, IMethodBinding b) {
    b = b.getMethodDeclaration();
    ITypeBinding rtb = b.getReturnType();
    if (rtb == null || rtb.isPrimitive()) {
      return rtb;
    }

    rtb = rtb.getErasure();
    type = type.getErasure();
    if (type.isSubTypeCompatible(rtb) || rtb.isEqualTo(type)) {
      return rtb; // Always safe
    }

    ITypeBinding dc = b.getDeclaringClass().getErasure();
    if (!isDep(type, rtb, new HashSet<ITypeBinding>())) {
      return rtb;
    }

    List<IMethodBinding> methods = TypeUtil.methods(
        TypeUtil.allBases(dc, null), TypeUtil.overrides(b));

    for (IMethodBinding mb2 : methods) {
      if (returnCovariant(b, mb2)) {
        if (rtb.isSubTypeCompatible(type)) {
          return type;
        }

        return returnType(type, mb2); // We can be a little covariant at
                        // least
      }
    }

    return rtb;
  }

  /**
   * Check if a is in the dependencies of type
   */
  public static boolean isDep(ITypeBinding a, ITypeBinding type,
      Set<ITypeBinding> checked) {
    type = type.getErasure();
    if (!checked.add(type)) {
      return false; // Already seen this type
    }

    a = a.getErasure();

    List<ITypeBinding> bases = TypeUtil.allBases(type, null);
    // type depends on all its bases
    for (ITypeBinding tb : bases) {
      if (tb.getErasure().isEqualTo(a)) {
        return true;
      }

      if (isDep(a, tb, checked)) {
        return true;
      }
    }

    // type depends on its return type dependencies
    for (IMethodBinding mb : type.getDeclaredMethods()) {
      List<IMethodBinding> methods = TypeUtil.methods(bases,
          TypeUtil.overrides(mb));

      for (IMethodBinding mb2 : methods) {
        if (!mb2.isConstructor() && returnCovariant(mb, mb2)) {
          if (a.isSubTypeCompatible(mb.getMethodDeclaration()
              .getReturnType().getErasure())) {
            return true;
          }

          if (isDep(a, mb.getMethodDeclaration().getReturnType(),
              checked)) {
            return true;
          }
        }
      }
    }

    return false;
  }

  public static boolean baseDeclared(Transformer ctx, ITypeBinding type,
      IMethodBinding mb) {
    return (Modifier.isAbstract(mb.getModifiers()) || type.isInterface())
        && baseHasSame(mb, type, ctx.resolve(Object.class));
  }

  public static boolean needsEmptyCtor(boolean hasEmpty, boolean hasInit,
      ITypeBinding type) {
    if (hasEmpty)
      return false;
    if (hasInit)
      return true;
    if (same(type, Object.class))
      return true;
    return false;
  }

  public static String makeDefaultInitTag() {
    return "*static_cast< ::" + CName.DEFAULT_INIT_TAG + "* >(0)";
  }

  public static boolean isValueOf(IMethodBinding mb, ITypeBinding type) {
    return type.getErasure().isEqualTo(mb.getReturnType().getErasure())
        && mb.getName().equals("valueOf")
        && mb.getParameterTypes().length == 1
        && same(mb.getParameterTypes()[0], String.class);
  }

  public static boolean isValues(IMethodBinding mb, ITypeBinding type) {
    return mb.getReturnType().isArray()
        && type.getErasure().isEqualTo(
            mb.getReturnType().getComponentType().getErasure())
        && mb.getName().equals("values")
        && mb.getParameterTypes().length == 0;
  }

  /** Check if this fragment should be initialized in init/clinit */
  public static boolean initInInit(VariableDeclarationFragment fragment) {
    if (!(fragment.getParent() instanceof FieldDeclaration)) {
      return false;
    }

    if (fragment.getInitializer() == null) {
      return false;
    }

    if (TransformUtil.constexprValue(fragment) != null) {
      return false;
    }

    return true;
  }

  /** Can use the 'auto' keyword when declaring a variable */
  public static boolean canDeclareAuto(VariableDeclarationStatement s,
      VariableDeclarationFragment vdf) {
    if (vdf == null) {
      if (s.fragments().size() > 1) {
        return false;
      }

      vdf = (VariableDeclarationFragment) s.fragments().get(0);
    }

    return canDeclareAutoX(vdf);
  }

  /** Can use the 'auto' keyword when declaring a variable */
  public static boolean canDeclareAuto(VariableDeclarationExpression s,
      VariableDeclarationFragment vdf) {
    if (vdf == null) {
      if (s.fragments().size() > 1) {
        return false;
      }

      vdf = (VariableDeclarationFragment) s.fragments().get(0);
    }

    return canDeclareAutoX(vdf);
  }

  private static boolean canDeclareAutoX(VariableDeclarationFragment vdf) {
    IVariableBinding vb = vdf.resolveBinding();
    if (vb.isField())
      return false;

    Expression initializer = vdf.getInitializer();
    if (initializer == null)
      return false;

    if (isNullLiteral(initializer))
      return false;

    if (!vb.getType().getErasure()
        .isEqualTo(initializer.resolveTypeBinding().getErasure())) {
      return false;
    }

    return true;
  }

  /**
   * In most cases we play it safe and cast literals to their exact types, to
   * avoid issues where it might actually matter, but this adds unwanted noise
   * so this function returns false for constructs where it is safe to skip it
   * - the literal cast might still be added for other reasons though (@see
   * TransformUtil.checkConstant)
   */
  public static boolean needsType(NumberLiteral node, ITypeBinding object) {
    if (node.resolveBoxing()) {
      // Boxing results in an extra call to valueOf which is overloaded
      // for the unboxed type and String, resulting in ambiguity
      return true;
    }

    ASTNode parent = node.getParent();

    if (parent instanceof ReturnStatement) {
      return false;
    }

    if (parent instanceof InfixExpression) {
      InfixExpression ie = (InfixExpression) parent;
      Operator op = ie.getOperator();
      if (op.equals(InfixExpression.Operator.EQUALS)
          || op.equals(InfixExpression.Operator.GREATER)
          || op.equals(InfixExpression.Operator.GREATER_EQUALS)
          || op.equals(InfixExpression.Operator.LESS)
          || op.equals(InfixExpression.Operator.LESS_EQUALS)
          || op.equals(InfixExpression.Operator.NOT_EQUALS)) {
        // Only comparators - add, sub etc affect the type of the
        // resulting expression
        return false;
      }
    }

    if (parent instanceof Assignment) {
      return false;
    }

    if (parent instanceof MethodInvocation) {
      MethodInvocation mi = (MethodInvocation) parent;
      if (mi.arguments().contains(node)
          && !TypeUtil
              .isOverloaded(mi.resolveMethodBinding(), object)) {
        return false;
      }
    }

    return true;
  }

  public static boolean needsJavaCast(ITypeBinding source, ITypeBinding target) {
    // Cast needed for multiply bounded generic types (<T extends A & B>)
    // Primitive check to avoid returning true for boxing/unboxing
    return !source.isPrimitive()
        && !target.isPrimitive()
        && !source.getErasure().isAssignmentCompatible(
            target.getErasure());
  }

  public static boolean isNullLiteral(Expression e) {
    if (e instanceof NullLiteral)
      return true;
    if (e instanceof ParenthesizedExpression) {
      ParenthesizedExpression pe = (ParenthesizedExpression) e;
      return isNullLiteral(pe.getExpression());
    }
    return false;
  }

  public static boolean needsExtraParens(ArrayCreation node) {
    return needsExtraParensX(node);
  }

  public static boolean needsExtraParens(ClassInstanceCreation node) {
    return needsExtraParensX(node);
  }

  /**
   * Java gives 'new XXX().yyy()' different operator precedence, thus we need
   * extra parenthesis sometimes
   */
  private static boolean needsExtraParensX(Expression creation) {
    ASTNode parent = creation.getParent();
    if (parent instanceof MethodInvocation) {
      MethodInvocation mi = (MethodInvocation) parent;
      if (mi.getExpression() == creation) {
        // Here, -> binds tighter than new, thus we get an error without
        // parens.
        return true;
      }
    } else if (parent instanceof FieldAccess) {
      FieldAccess fi = (FieldAccess) parent;
      if (fi.getExpression() == creation) {
        // Here, -> binds tighter than new, thus we get an error without
        // parens.
        return true;
      }
    }

    return false;
  }

  public static void setNs(PrintWriter pw, List<String> pkg, Stack<String> cur) {
    while (pkg.size() < cur.size()
        || !cur.equals(pkg.subList(0, cur.size()))) {

      String ns = cur.pop();

      pw.print(indent(cur.size()) + "} // ");
      pw.print(ns);
      pw.println();
    }

    if (!cur.equals(pkg)) {
      pw.println();

      while (!cur.equals(pkg)) {
        String i = indent(cur.size());
        String ns = pkg.get(cur.size());

        pw.format("%snamespace %s\n", i, ns);
        pw.println(i + "{");

        cur.push(ns);
      }
    }
  }
}
TOP

Related Classes of se.arnetheduck.j2c.transform.TransformUtil

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.