Package se.arnetheduck.j2c.transform

Source Code of se.arnetheduck.j2c.transform.CName$RelativeBinding

package se.arnetheduck.j2c.transform;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.jdt.core.dom.IBinding;
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;

public class CName {
  /**
   * Name of fake static initializer - method that intializes static fields
   * and runs static initializer blocks
   */
  public static final String STATIC_INIT = "clinit";

  /**
   * Name of fake instance initializer - method that initializes fields and
   * runs instance initializer blocks
   */
  public static final String INSTANCE_INIT = "init";

  /**
   * Name of fake constructor containing the body of the Java constructor,
   * necessary to simulate Java initialization order
   */
  public static final String CTOR = "ctor";

  /** Virtual method that returns the dynamic class of the current object */
  public static final String GET_CLASS = "getClass0";

  /**
   * The default-init constructor takes care of the first phase of Java object
   * initialization, namely of calling clinit to make sure the class and all
   * its bases are initialized.
   *
   * See §12.4.1 and §12.5
   */
  public static final String DEFAULT_INIT_TAG = "default_init_tag";

  /**
   * Cast function that throws an appropriate exception if a cast fails
   */
  public static final String JAVA_CAST = "java_cast";

  /**
   * Null pointer check function - throws NPE if the passed pointer is null
   */
  public static final String NPC = "npc";

  /** C++ keywords + special method names - java keywords */
  public static Collection<String> keywords = Arrays.asList("alignas",
      "alignof", "and", "and_eq", "asm", "auto", "bitand", "bitor",
      "bool", "char16_t", "char32_t", "compl", "const", "constexpr",
      "const_cast", "decltype", "delete", "dynamic_cast", "explicit",
      "export", "extern", "friend", "goto", "inline", "mutable",
      "namespace", "noexcept", "not", "not_eq", "nullptr", "operator",
      "or", "or_eq", "register", "reinterpret_cast", "signed", "sizeof",
      "static_assert", "static_cast", "struct", "template",
      "thread_local", "typedef", "typeid", "typename", "union",
      "unsigned", "using", "virtual", "wchar_t", "xor", "xor_eq", CTOR,
      INSTANCE_INIT, STATIC_INIT, GET_CLASS, DEFAULT_INIT_TAG, JAVA_CAST,
      "int8_t", "int16_t", "int32_t", "int64_t", "char16_t", "NULL",
      "npc", "EOF");

  public static String qualified(ITypeBinding tb, boolean global) {
    IPackageBinding pkg = TransformUtil.elementPackage(tb);
    return (global && !tb.isPrimitive() ? "::" : "")
        + (pkg == null || pkg.getName().isEmpty() ? of(tb) : (of(pkg)
            + "." + of(tb))).replace(".", "::");
  }

  public static String relative(ITypeBinding tb, ITypeBinding root,
      boolean global) {
    if (!samePackage(tb, root)) {
      return qualified(tb, global);
    }

    String tbn = of(tb);
    if (tbn.equals(Object.class.getSimpleName())
        && !TransformUtil.same(tb, Object.class)) {
      // Intefaces have null superclass but inherit (conceptually) from
      // Object
      return qualified(tb, global);
    }

    // Clash between method and type name
    for (IMethodBinding mb : root.getDeclaredMethods()) {
      if (!mb.isConstructor() && of(mb).equals(tbn)) {
        return qualified(tb, global);
      }
    }

    // Clash between field and type name
    for (IVariableBinding vb : root.getDeclaredFields()) {
      if (of(vb).equals(tbn)) {
        return qualified(tb, global);
      }
    }

    // In C++, unqualified names in a class are looked up in base
    // classes before the own namespace
    List<ITypeBinding> bases = TypeUtil.allBases(root, null);
    for (ITypeBinding sb : bases) {
      if (tb.getErasure().isEqualTo(sb.getErasure())) {
        return tbn;
      }

      if (of(sb).equals(tbn)) {
        return qualified(tb, global);
      }
    }

    return tbn;
  }

  public static boolean samePackage(ITypeBinding tb0, ITypeBinding tb1) {
    IPackageBinding p0 = TransformUtil.elementPackage(tb0);
    IPackageBinding p1 = TransformUtil.elementPackage(tb1);
    if (p0 == null) {
      return p1 == null;
    }

    return p1 != null && p0.isEqualTo(p1);
  }

  private static class RelativeBinding {
    public final IBinding binding;
    public final ITypeBinding relativeTo;

    public RelativeBinding(IBinding binding, ITypeBinding relativeTo) {
      assert binding != null;
      assert relativeTo != null;
      this.binding = binding;
      this.relativeTo = relativeTo;
    }

    @Override
    public boolean equals(Object other) {
      return other != null && other instanceof RelativeBinding &&
           ((RelativeBinding) other).binding == binding &&
           ((RelativeBinding) other).relativeTo == relativeTo;
    }

    @Override
    public int hashCode() {
      return (binding != null ? binding.hashCode() : 0)
          ^
           (relativeTo != null ? relativeTo.hashCode() : 0);
    }
  }

  private static Pattern bin = Pattern.compile("\\$\\d+");
  private static Map<IBinding, String> cache = Collections
      .synchronizedMap(new WeakHashMap<IBinding, String>());
  // We need a separate cache for variables that might be closed over.
  private static Map<RelativeBinding, String> varCache = Collections
      .synchronizedMap(new WeakHashMap<RelativeBinding, String>());

  public static String of(ITypeBinding tb) {
    String name = cache.get(tb);
    if (name != null) {
      return name;
    }

    ITypeBinding tbe = tb.getErasure();
    if (tb.isArray()) {
      name = of(tb.getComponentType()) + "Array";
    } else if (tb.isPrimitive()) {
      name = TransformUtil.primitive(tb.getName());
    } else if (tbe.isLocal()) {
      StringBuilder ret = new StringBuilder();

      Matcher match = bin.matcher(tbe.getBinaryName());
      String sep = "";

      if (tbe.getDeclaringClass() != null) {
        ret.append(of(tbe.getDeclaringClass()));
        sep = "_";
      }

      if (tbe.getDeclaringMethod() != null
          && tbe.getDeclaringMethod().getName().length() > 0) {
        ret.append(sep + tbe.getDeclaringMethod().getName());
        sep = "_";
      }

      if (tbe.getName() != null && tbe.getName().length() > 0) {
        ret.append(sep + tbe.getName());
        sep = "_";
      }

      while (match.find()) {
        ret.append(match.group(0).replaceAll("\\$", "_"));
      }

      name = ret.toString();
    } else if (tbe.isNested()) {
      name = of(tbe.getDeclaringClass()) + "_" + tbe.getName();
    } else {
      name = keywords(tbe.getName());
    }

    cache.put(tb, name);
    cache.put(tbe, name);

    return name;
  }

  public static String of(IMethodBinding mb) {
    String name = cache.get(mb);
    if (name != null) {
      return name;
    }

    // private methods mess up using statements that import methods
    // from base classes
    name = CName.keywords(mb.getName());

    IMethodBinding lastOverride = couldOverrideDefault(mb);
    if (lastOverride != null) {
      name = of(lastOverride.getDeclaringClass()) + "_" + name;
    }

    // Methods can have the same name as the constructor without being a
    // constructor!
    while (!mb.isConstructor() && hasName(mb.getDeclaringClass(), name)) {
      name = name + "_";
    }

    cache.put(mb, name);
    cache.put(mb.getMethodDeclaration(), name);

    return name;
  }

  private static boolean hasName(ITypeBinding tb, String ret) {
    if (tb.getName().equals(ret)) {
      return true;
    }

    if (tb.getSuperclass() != null && hasName(tb.getSuperclass(), ret)) {
      return true;
    }

    for (ITypeBinding ib : tb.getInterfaces()) {
      if (hasName(ib, ret)) {
        return true;
      }
    }

    return false;
  }

  /**
   * In Java, if a method has the same name as a package-private method in a
   * base class, it will not override the base class member. In C++, we have
   * nothing of the sort so we have to rename the non-overriding method
   */
  private static IMethodBinding couldOverrideDefault(IMethodBinding mb) {
    ITypeBinding tb = mb.getDeclaringClass();
    if (tb != null) {
      IMethodBinding last = mb;
      for (ITypeBinding tbx = tb.getSuperclass(); tbx != null; tbx = tbx
          .getSuperclass()) {
        boolean samePackage = samePackage(tb, tbx);

        for (IMethodBinding mbx : tbx.getDeclaredMethods()) {
          if (!samePackage
              && TransformUtil.isDefault(mbx.getModifiers())
              && mbx.getName().equals(mb.getName())
              && TransformUtil.sameParameters(mb, mbx, true)) {
            return last;
          }

          if (last.overrides(mbx)) {
            // This will give the final name of the method
            last = mbx;
          }
        }
      }
    }

    return null;
  }

  /**
   * Does the same thing as {@link #of(IVariableBinding)} but for the purposes
   * of removing field/method name collisions, looks for conflicts in the
   * given type binding (i.e. class). This is needed when handling closed over
   * variables which have the original definition in one class, but will be
   * instantiated in a separate class that may have a method with the same
   * name:
   *
   * final Object o = ...; return new Thing() { public Object o() { return o;
   * } }
   */
  public static String of(IVariableBinding vb, ITypeBinding relativeTo) {
    String name = varCache.get(new RelativeBinding(vb, relativeTo));
    if (name != null) {
      return name;
    }

    name = keywords(vb.getName());

    if (relativeTo != null) {
      // Find all methods up the inheritance hierarchy.
      List<ITypeBinding> supers = TypeUtil.superClasses(relativeTo);
      supers.add(relativeTo);
      List<IMethodBinding> allMethods = TypeUtil.methods(supers);

      outer: for (;;) {
        for (IMethodBinding mb : allMethods) {
          if (of(mb).equals(name)) {
            // Methods and variables can have the same name so we
            // postfix the variable
            name = name + "_";
            continue outer;
          }
        }

        break;
      }
    }

    varCache.put(new RelativeBinding(vb, relativeTo), name);
    varCache.put(new RelativeBinding(vb.getVariableDeclaration(), relativeTo), name);

    return name;
  }

  /**
   * Formats a variable name appropriately, with a cache for performance and
   * addition of trailing underscores to avoid field/method name conflicts.
   */
  public static String of(IVariableBinding vb) {
    return of(vb, vb.getDeclaringClass());
  }

  public static String of(IPackageBinding pb) {
    String[] n = pb.getNameComponents();
    StringBuilder ret = new StringBuilder();
    String sep = "";
    for (String element : n) {
      ret.append(sep);
      sep = ".";
      ret.append(CName.keywords(element));
    }

    return ret.toString();
  }

  public static String packageOf(ITypeBinding tb) {
    IPackageBinding pkg = TransformUtil.elementPackage(tb);
    return pkg == null ? "" : of(pkg);
  }

  /** Filter out C++ keywords and other reserved names */
  public static String keywords(String name) {
    if (name.startsWith("__")) {
      name = "j" + name;
    }

    if (name.endsWith("Array")) {
      String n = name;
      String suffix = "";
      while (n.endsWith("Array")) {
        suffix += "_";
        n = n.substring(0, n.length() - "Array".length());
      }

      return name + suffix;
    }

    if (keywords.contains(name)) {
      return name + "_";
    }

    return name;
  }
}
TOP

Related Classes of se.arnetheduck.j2c.transform.CName$RelativeBinding

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.