| /* |
| * 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; |
| } |
| } |