blob: cb2390db0d1ff4c45cf200312a26cccea508817f [file] [log] [blame]
/*
* Copyright 2008 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.ast.JConstructor;
import com.google.gwt.dev.jjs.ast.JDeclaredType;
import com.google.gwt.dev.jjs.ast.JField;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JNode;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.util.collect.IdentityHashSet;
import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedFieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
/**
* Contains the list of the top-level and array types.
*/
public class TypeMap {
/**
* Maps Eclipse AST nodes to our JNodes.
*/
private final Map<Binding, JNode> crossRefMap = new IdentityHashMap<Binding, JNode>();
private final Map<String, JDeclaredType> externalTypesByName =
new HashMap<String, JDeclaredType>();
/**
* Centralizes creation and singleton management.
*/
private final JProgram program;
public TypeMap(JProgram program) {
this.program = program;
}
public JNode get(Binding binding) {
return get(binding, true);
}
public JProgram getProgram() {
return program;
}
public void put(Binding binding, JNode to) {
if (binding == null) {
throw new InternalCompilerException("Trying to put null into typeMap.");
}
Object old = crossRefMap.put(binding, to);
assert (old == null);
if (to instanceof JDeclaredType) {
JDeclaredType type = (JDeclaredType) to;
if (type.isExternal()) {
externalTypesByName.put(type.getName(), type);
}
}
}
public JNode tryGet(Binding binding) {
return get(binding, false);
}
private boolean equals(MethodBinding binding, JMethod method) {
if (!(method instanceof JConstructor && binding.isConstructor())
&& !method.getName().equals(String.valueOf(binding.constantPoolName()))) {
return false;
}
List<JType> paramTypes = method.getOriginalParamTypes();
TypeBinding[] bindingParams = binding.parameters;
if (paramTypes.size() != bindingParams.length) {
return false;
}
for (int i = 0; i < bindingParams.length; ++i) {
TypeBinding bindingParam = bindingParams[i];
if (paramTypes.get(i) != get(bindingParam)) {
return false;
}
}
return method.getType() == get(binding.returnType);
}
private JNode get(Binding binding, boolean failOnNull) {
if (binding instanceof TypeVariableBinding) {
TypeVariableBinding tvb = (TypeVariableBinding) binding;
return get(tvb.erasure(), failOnNull);
} else if (binding instanceof ParameterizedTypeBinding) {
ParameterizedTypeBinding ptb = (ParameterizedTypeBinding) binding;
return get(ptb.erasure(), failOnNull);
} else if (binding instanceof ParameterizedMethodBinding) {
ParameterizedMethodBinding pmb = (ParameterizedMethodBinding) binding;
return get(pmb.original(), failOnNull);
} else if (binding instanceof ParameterizedFieldBinding) {
ParameterizedFieldBinding pfb = (ParameterizedFieldBinding) binding;
return get(pfb.original(), failOnNull);
} else if (binding instanceof WildcardBinding) {
WildcardBinding wcb = (WildcardBinding) binding;
return get(wcb.erasure(), failOnNull);
}
JNode result = internalGet(binding, failOnNull);
if (result == null && failOnNull) {
InternalCompilerException ice = new InternalCompilerException("Failed to get JNode");
ice.addNode(binding.getClass().getName(), binding.toString(), null);
throw ice;
}
return result;
}
private JField getFieldForBinding(JDeclaredType type, FieldBinding binding) {
for (JField field : type.getFields()) {
if (field.getName().equals(String.valueOf(binding.name))) {
return field;
}
}
return null;
}
private JMethod getMethodForBinding(JDeclaredType type, MethodBinding binding) {
for (JMethod method : type.getMethods()) {
if (equals(binding, method)) {
return method;
}
}
return null;
}
/**
* Returns a list of JNodes that have the same name as the JDT Binding. This
* method is only used during debugging sessions from the interactive
* expression evaluator.
*/
@SuppressWarnings("unused")
private List<JNode> haveSameName(Binding binding) {
IdentityHashSet<JNode> nodes = new IdentityHashSet<JNode>();
for (Binding b : crossRefMap.keySet()) {
if (String.valueOf(b.readableName()).equals(String.valueOf(binding.readableName()))) {
nodes.add(crossRefMap.get(b));
}
}
return new ArrayList<JNode>(nodes);
}
private JNode internalGet(Binding binding, boolean failOnNull) {
JNode cached = crossRefMap.get(binding);
if (cached != null) {
return cached;
} else if (binding instanceof BaseTypeBinding) {
BaseTypeBinding baseTypeBinding = (BaseTypeBinding) binding;
// see org.eclipse.jdt.internal.compiler.lookup.TypeIds constants
switch (baseTypeBinding.id) {
case TypeIds.T_JavaLangObject:
// here for consistency, should already be cached
return program.getTypeJavaLangObject();
case TypeIds.T_char:
return program.getTypePrimitiveChar();
case TypeIds.T_byte:
return program.getTypePrimitiveByte();
case TypeIds.T_short:
return program.getTypePrimitiveShort();
case TypeIds.T_boolean:
return program.getTypePrimitiveBoolean();
case TypeIds.T_void:
return program.getTypeVoid();
case TypeIds.T_long:
return program.getTypePrimitiveLong();
case TypeIds.T_double:
return program.getTypePrimitiveDouble();
case TypeIds.T_float:
return program.getTypePrimitiveFloat();
case TypeIds.T_int:
return program.getTypePrimitiveInt();
case TypeIds.T_JavaLangString:
// here for consistency, should already be cached
return program.getTypeJavaLangString();
case TypeIds.T_null:
return program.getTypeNull();
case TypeIds.T_undefined:
default:
return null;
}
} else if (binding instanceof ArrayBinding) {
ArrayBinding arrayBinding = (ArrayBinding) binding;
JType elementType = (JType) get(arrayBinding.elementsType(), failOnNull);
if (elementType == null) {
return null;
}
return program.getTypeArray(elementType);
} else if (binding instanceof BinaryTypeBinding) {
BinaryTypeBinding binaryBinding = (BinaryTypeBinding) binding;
String name = BuildTypeMap.dotify(binaryBinding.compoundName);
// There may be many BinaryTypeBindings for a single binary type
JDeclaredType type = externalTypesByName.get(name);
if (type != null) {
put(binding, type);
}
return type;
} else if (binding instanceof MethodBinding) {
MethodBinding b = (MethodBinding) binding;
JMethod cachedMethod = (JMethod) crossRefMap.get(b);
if (cachedMethod == null) {
JDeclaredType type = (JDeclaredType) get(b.declaringClass, failOnNull);
if (type == null) {
return type;
}
cachedMethod = getMethodForBinding(type, b);
if (cachedMethod != null) {
put(b, cachedMethod);
}
} else {
// Happens sometimes when looking up the type to resolve the binding
// causes us to also resolve the binding.
}
return cachedMethod;
} else if (binding instanceof FieldBinding) {
FieldBinding b = (FieldBinding) binding;
JField cachedField = (JField) crossRefMap.get(b);
if (cachedField == null) {
JDeclaredType type = (JDeclaredType) get(b.declaringClass, failOnNull);
if (type == null) {
return null;
}
cachedField = getFieldForBinding(type, b);
if (cachedField != null) {
put(b, cachedField);
}
} else {
// Happens sometimes when looking up the type to resolve the binding
// causes us to also resolve the binding.
}
return cachedField;
}
return null;
}
}