blob: e63ad4dde47b753984a8939a4fcc6f3f4b9ecbe0 [file] [log] [blame]
/*
* 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.javac.JdtUtil;
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.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.base.Preconditions;
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.SyntheticMethodBinding;
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<String> argNames = new ArrayList<String>();
private final Map<String, JField> fields = new HashMap<String, JField>();
private final Map<String, JMethod> methods = new HashMap<String, JMethod>();
private final Map<String, JField> sourceFields = new HashMap<String, JField>();
private final Map<String, JMethod> sourceMethods = new HashMap<String, JMethod>();
private final Map<String, JReferenceType> sourceTypes = new HashMap<String, JReferenceType>();
private final Interner<String> stringInterner = StringInterner.get();
private final Map<String, JType> types = new HashMap<String, JType>();
{
put(JPrimitiveType.BOOLEAN, JPrimitiveType.BYTE, JPrimitiveType.CHAR, JPrimitiveType.DOUBLE,
JPrimitiveType.FLOAT, JPrimitiveType.INT, JPrimitiveType.LONG, JPrimitiveType.SHORT,
JPrimitiveType.VOID, JReferenceType.NULL_TYPE);
}
public void clearSource() {
sourceFields.clear();
sourceMethods.clear();
sourceTypes.clear();
}
public JField get(FieldBinding binding) {
binding = binding.original();
String key = JdtUtil.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 = JdtUtil.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 binding instanceof SyntheticMethodBinding || method.isExternal();
methods.put(key, method);
}
return method;
}
public JType get(TypeBinding binding) {
binding = binding.erasure();
String key = JdtUtil.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.isPrimitiveType() || type.isNullType() || 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 {
assert binding.isClass() || binding.isInterface() || binding.isEnum()
: "Expecting a declared type binding but got " + binding;
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, GwtAstBuilder.CLINIT_METHOD_NAME, 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) {
// Make sure that the enclosing type of the field binding corresponds to the enclosing type of
// field to avoid associating a field binding with the wrong JField.
Preconditions.checkArgument(get(binding.declaringClass.erasure()) == field.getEnclosingType());
String key = JdtUtil.signature(binding);
JField oldField = sourceFields.put(key, field);
// Make sure this is called at most once per field binding.
Preconditions.checkState(oldField == null);
}
public void setMethod(MethodBinding binding, JMethod method) {
// Make sure that the enclosing type of the method binding corresponds to the enclosing type of
// method to avoid associating a method binding with the wrong JMethod.
Preconditions.checkArgument(get(binding.declaringClass.erasure()) == method.getEnclosingType());
String key = JdtUtil.signature(binding);
JMethod oldMethod = sourceMethods.put(key, method);
// Make sure this is called at most once per method binding.
Preconditions.checkState(oldMethod == null);
}
public void setSourceType(SourceTypeBinding binding, JDeclaredType type) {
String key = JdtUtil.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, AccessModifier.fromMethodBinding(b));
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),
AccessModifier.fromFieldBinding(binding));
enclosingType.addField(field);
return field;
}
private JParameter createParameter(SourceInfo info, TypeBinding paramType,
JMethod enclosingMethod, int argPosition) {
ensureArgNames(argPosition);
return enclosingMethod.createFinalParameter(info, argNames.get(argPosition), get(paramType));
}
private JDeclaredType createType(ReferenceBinding binding) {
String name = JdtUtil.getQualifiedSourceName(binding);
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) {
method.createFinalParameter(info, argNames.get(argPosition++), get(argType));
}
}
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) {
method.createFinalParameter(info, paramNames[i++], get(argType));
}
}
method.freezeParamTypes();
}
private void put(JType... baseTypes) {
for (JType type : baseTypes) {
types.put(type.getName(), type);
}
}
}