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

com.google.gwt.dev.jjs.impl.ReferenceMapper Maven / Gradle / Ivy

There is a newer version: 2.10.0
Show newest version
/*
 * Copyright 2010 Google Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.gwt.dev.jjs.impl;

import com.google.gwt.dev.jjs.InternalCompilerException;
import com.google.gwt.dev.jjs.SourceInfo;
import com.google.gwt.dev.jjs.SourceOrigin;
import com.google.gwt.dev.jjs.ast.AccessModifier;
import com.google.gwt.dev.jjs.ast.JArrayType;
import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JConstructor;
import com.google.gwt.dev.jjs.ast.JDeclaredType;
import com.google.gwt.dev.jjs.ast.JEnumType;
import com.google.gwt.dev.jjs.ast.JField;
import com.google.gwt.dev.jjs.ast.JInterfaceType;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JNullType;
import com.google.gwt.dev.jjs.ast.JParameter;
import com.google.gwt.dev.jjs.ast.JPrimitiveType;
import com.google.gwt.dev.jjs.ast.JReferenceType;
import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.util.StringInterner;
import com.google.gwt.thirdparty.guava.common.collect.Interner;


import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.SyntheticArgumentBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Creates unresolved references to types, fields, and methods.
 */
public class ReferenceMapper {

  private final List argNames = new ArrayList();
  private final Map fields = new HashMap();
  private final Map methods = new HashMap();
  private final Map sourceFields = new HashMap();
  private final Map sourceMethods = new HashMap();
  private final Map sourceTypes = new HashMap();
  private final Interner stringInterner = StringInterner.get();
  private final Map types = new HashMap();

  {
    put(JPrimitiveType.BOOLEAN, JPrimitiveType.BYTE, JPrimitiveType.CHAR, JPrimitiveType.DOUBLE,
        JPrimitiveType.FLOAT, JPrimitiveType.INT, JPrimitiveType.LONG, JPrimitiveType.SHORT,
        JPrimitiveType.VOID, JNullType.INSTANCE);
  }

  public void clearSource() {
    sourceFields.clear();
    sourceMethods.clear();
    sourceTypes.clear();
  }

  public JField get(FieldBinding binding) {
    binding = binding.original();
    String key = signature(binding);
    JField sourceField = sourceFields.get(key);
    if (sourceField != null) {
      assert !sourceField.isExternal();
      return sourceField;
    }
    JField field = fields.get(key);
    if (field == null) {
      field = createField(binding);
      assert field.isExternal();
      fields.put(key, field);
    }
    return field;
  }

  public JMethod get(MethodBinding binding) {
    binding = binding.original();
    String key = signature(binding);
    JMethod sourceMethod = sourceMethods.get(key);
    if (sourceMethod != null) {
      assert !sourceMethod.isExternal();
      return sourceMethod;
    }
    JMethod method = methods.get(key);
    if (method == null) {
      if (binding.isConstructor()) {
        method = createConstructor(SourceOrigin.UNKNOWN, binding);
      } else {
        method = createMethod(SourceOrigin.UNKNOWN, binding, null);
      }
      assert method.isExternal();
      methods.put(key, method);
    }
    return method;
  }

  public JType get(TypeBinding binding) {
    binding = binding.erasure();
    String key = signature(binding);
    JReferenceType sourceType = sourceTypes.get(key);
    if (sourceType != null) {
      assert !sourceType.isExternal();
      return sourceType;
    }

    JType type = types.get(key);
    if (type != null) {
      assert type instanceof JPrimitiveType || type == JNullType.INSTANCE || type.isExternal();
      return type;
    }
    assert !(binding instanceof BaseTypeBinding);

    if (binding instanceof ArrayBinding) {
      ArrayBinding arrayBinding = (ArrayBinding) binding;
      JArrayType arrayType = new JArrayType(get(arrayBinding.elementsType()));
      if (arrayType.isExternal()) {
        types.put(key, arrayType);
      } else {
        sourceTypes.put(key, arrayType);
      }
      return arrayType;
    } else {
      ReferenceBinding refBinding = (ReferenceBinding) binding;
      JDeclaredType declType = createType(refBinding);
      try {
        if (declType instanceof JClassType) {
          ReferenceBinding superclass = refBinding.superclass();
          if (superclass != null && superclass.isValidBinding()) {
            ((JClassType) declType).setSuperClass((JClassType) get(superclass));
          }
        }
        ReferenceBinding[] superInterfaces = refBinding.superInterfaces();
        if (superInterfaces != null) {
          for (ReferenceBinding intf : superInterfaces) {
            if (intf.isValidBinding()) {
              declType.addImplements((JInterfaceType) get(intf));
            }
          }
        }
      } catch (AbortCompilation ignored) {
        /*
         * The currently-compiling unit has no errors; however, we're running
         * into a case where it references something with a bad hierarchy. This
         * doesn't cause an error in the current unit, but it does mean we run
         * into a wall here trying to construct the hierarchy. Catch the error
         * so that compilation can proceed; the error units themselves will
         * eventually cause the full compile to error out.
         */
      }
      // Emulate clinit method for super clinit calls.
      JMethod clinit =
          new JMethod(SourceOrigin.UNKNOWN, "$clinit", declType, JPrimitiveType.VOID, false, true,
              true, AccessModifier.PRIVATE);
      clinit.freezeParamTypes();
      clinit.setSynthetic();
      declType.addMethod(clinit);
      declType.setExternal(true);
      types.put(key, declType);
      return declType;
    }
  }

  public void setField(FieldBinding binding, JField field) {
    String key = signature(binding);
    sourceFields.put(key, field);
  }

  public void setMethod(MethodBinding binding, JMethod method) {
    String key = signature(binding);
    sourceMethods.put(key, method);
  }

  public void setSourceType(SourceTypeBinding binding, JDeclaredType type) {
    String key = signature(binding);
    sourceTypes.put(key, type);
  }

  JMethod createConstructor(SourceInfo info, MethodBinding b) {
    JDeclaredType enclosingType = (JDeclaredType) get(b.declaringClass);
    JMethod method = new JConstructor(info, (JClassType) enclosingType);
    enclosingType.addMethod(method);

    /*
     * Don't need to synthesize enum intrinsic args because enum ctors can only
     * be called locally.
     */

    int argPosition = 0;

    ReferenceBinding declaringClass = b.declaringClass;
    if (declaringClass.isNestedType() && !declaringClass.isStatic()) {
      // add synthetic args for outer this
      if (declaringClass.syntheticEnclosingInstanceTypes() != null) {
        for (ReferenceBinding argType : declaringClass.syntheticEnclosingInstanceTypes()) {
          createParameter(info, argType, method, argPosition++);
        }
      }
    }

    // User args.
    argPosition = mapParameters(info, method, b, argPosition);

    if (declaringClass.isNestedType() && !declaringClass.isStatic()) {
      // add synthetic args for locals
      if (declaringClass.syntheticOuterLocalVariables() != null) {
        for (SyntheticArgumentBinding arg : declaringClass.syntheticOuterLocalVariables()) {
          createParameter(info, arg.type, method, argPosition++);
        }
      }
    }

    mapExceptions(method, b);
    if (b.isSynthetic()) {
      method.setSynthetic();
    }
    return method;
  }

  JMethod createMethod(SourceInfo info, MethodBinding b, String[] paramNames) {
    JDeclaredType enclosingType = (JDeclaredType) get(b.declaringClass);
    JMethod method =
        new JMethod(info, intern(b.selector), enclosingType, get(b.returnType), b.isAbstract(), b
            .isStatic(), b.isFinal(), AccessModifier.fromMethodBinding(b));
    enclosingType.addMethod(method);
    if (paramNames == null) {
      mapParameters(info, method, b, 0);
    } else {
      mapParameters(info, method, b, paramNames);
    }
    mapExceptions(method, b);
    if (b.isSynthetic()) {
      method.setSynthetic();
    }
    return method;
  }

  private JField createField(FieldBinding binding) {
    JDeclaredType enclosingType = (JDeclaredType) get(binding.declaringClass);
    JField field =
        new JField(SourceOrigin.UNKNOWN, intern(binding.name), enclosingType, get(binding.type),
            binding.isStatic(), GwtAstBuilder.getFieldDisposition(binding));
    enclosingType.addField(field);
    return field;
  }

  private JParameter createParameter(SourceInfo info, TypeBinding paramType,
      JMethod enclosingMethod, int argPosition) {
    JType type = get(paramType);
    ensureArgNames(argPosition);
    JParameter param =
        new JParameter(info, argNames.get(argPosition), type, true, false, enclosingMethod);
    enclosingMethod.addParam(param);
    return param;
  }

  private JParameter createParameter(SourceInfo info, TypeBinding paramType,
      JMethod enclosingMethod, String name) {
    JParameter param = new JParameter(info, name, get(paramType), true, false, enclosingMethod);
    enclosingMethod.addParam(param);
    return param;
  }

  private JDeclaredType createType(ReferenceBinding binding) {
    String name = GwtAstBuilder.dotify(binding.compoundName);
    SourceInfo info = SourceOrigin.UNKNOWN;
    if (binding.isClass()) {
      return new JClassType(info, name, binding.isAbstract(), binding.isFinal());
    } else if (binding.isInterface() || binding.isAnnotationType()) {
      return new JInterfaceType(info, name);
    } else if (binding.isEnum()) {
      if (binding.isAnonymousType()) {
        // Don't model an enum subclass as a JEnumType.
        return new JClassType(info, name, false, true);
      } else {
        return new JEnumType(info, name, binding.isAbstract());
      }
    } else {
      throw new InternalCompilerException("ReferenceBinding is not a class, interface, or enum.");
    }
  }

  private void ensureArgNames(int required) {
    for (int i = argNames.size(); i <= required; ++i) {
      argNames.add(intern("arg" + i));
    }
  }

  private String intern(char[] cs) {
    return intern(String.valueOf(cs));
  }

  private String intern(String s) {
    return stringInterner.intern(s);
  }

  private void mapExceptions(JMethod method, MethodBinding binding) {
    for (ReferenceBinding thrownBinding : binding.thrownExceptions) {
      JClassType type = (JClassType) get(thrownBinding);
      method.addThrownException(type);
    }
  }

  private int mapParameters(SourceInfo info, JMethod method, MethodBinding binding, int argPosition) {
    if (binding.parameters != null) {
      ensureArgNames(argPosition + binding.parameters.length);
      for (TypeBinding argType : binding.parameters) {
        createParameter(info, argType, method, argNames.get(argPosition++));
      }
    }
    method.freezeParamTypes();
    return argPosition;
  }

  private void mapParameters(SourceInfo info, JMethod method, MethodBinding binding,
      String[] paramNames) {
    if (binding.parameters != null) {
      int i = 0;
      for (TypeBinding argType : binding.parameters) {
        createParameter(info, argType, method, paramNames[i++]);
      }
    }
    method.freezeParamTypes();
  }

  private void put(JType... baseTypes) {
    for (JType type : baseTypes) {
      types.put(type.getName(), type);
    }
  }

  private String signature(FieldBinding binding) {
    StringBuilder sb = new StringBuilder();
    sb.append(binding.declaringClass.constantPoolName());
    sb.append('.');
    sb.append(binding.name);
    sb.append(':');
    sb.append(binding.type.signature());
    return sb.toString();
  }

  private String signature(MethodBinding binding) {
    StringBuilder sb = new StringBuilder();
    sb.append(binding.declaringClass.constantPoolName());
    sb.append('.');
    sb.append(binding.selector);
    sb.append('(');
    for (TypeBinding paramType : binding.parameters) {
      sb.append(paramType.signature());
    }
    sb.append(')');
    sb.append(binding.returnType.signature());
    return sb.toString();
  }

  private String signature(TypeBinding binding) {
    if (binding.isBaseType()) {
      return String.valueOf(binding.sourceName());
    } else {
      return String.valueOf(binding.constantPoolName());
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy