1 /******************************************************************************* 2 * Copyright (c) 2000, 2010 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 * Stephan Herrmann <stephan@cs.tu-berlin.de> - TypeConverters don't set enclosingType - https://bugs.eclipse.org/bugs/show_bug.cgi?id=320841 14 *******************************************************************************/ 15 package org.eclipse.jdt.internal.core; 16 17 import org.eclipse.jdt.core.Flags; 18 import org.eclipse.jdt.core.IField; 19 import org.eclipse.jdt.core.IMethod; 20 import org.eclipse.jdt.core.IType; 21 import org.eclipse.jdt.core.ITypeParameter; 22 import org.eclipse.jdt.core.JavaModelException; 23 import org.eclipse.jdt.core.Signature; 24 import org.eclipse.jdt.core.compiler.CharOperation; 25 import org.eclipse.jdt.internal.compiler.CompilationResult; 26 import org.eclipse.jdt.internal.compiler.ast.ASTNode; 27 import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; 28 import org.eclipse.jdt.internal.compiler.ast.AnnotationMethodDeclaration; 29 import org.eclipse.jdt.internal.compiler.ast.Argument; 30 import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; 31 import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; 32 import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; 33 import org.eclipse.jdt.internal.compiler.ast.ImportReference; 34 import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; 35 import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; 36 import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; 37 import org.eclipse.jdt.internal.compiler.ast.TypeReference; 38 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; 39 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; 40 import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; 41 import org.eclipse.jdt.internal.compiler.parser.TypeConverter; 42 import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; 43 import org.eclipse.jdt.internal.core.util.HashSetOfCharArrayArray; 44 import org.eclipse.jdt.internal.core.util.Util; 45 46 /** 47 * Converter from a binary type to an AST type declaration. 48 */ 49 public class BinaryTypeConverter extends TypeConverter { 50 51 private CompilationResult compilationResult; 52 private HashSetOfCharArrayArray typeNames; 53 BinaryTypeConverter(ProblemReporter problemReporter, CompilationResult compilationResult, HashSetOfCharArrayArray typeNames)54 public BinaryTypeConverter(ProblemReporter problemReporter, CompilationResult compilationResult, HashSetOfCharArrayArray typeNames) { 55 super(problemReporter, Signature.C_DOLLAR); 56 this.compilationResult = compilationResult; 57 this.typeNames = typeNames; 58 } 59 buildImports(ClassFileReader reader)60 public ImportReference[] buildImports(ClassFileReader reader) { 61 // add remaining references to the list of type names 62 // (code extracted from BinaryIndexer#extractReferenceFromConstantPool(...)) 63 int[] constantPoolOffsets = reader.getConstantPoolOffsets(); 64 int constantPoolCount = constantPoolOffsets.length; 65 for (int i = 0; i < constantPoolCount; i++) { 66 int tag = reader.u1At(constantPoolOffsets[i]); 67 char[] name = null; 68 switch (tag) { 69 case ClassFileConstants.MethodRefTag : 70 case ClassFileConstants.InterfaceMethodRefTag : 71 int constantPoolIndex = reader.u2At(constantPoolOffsets[i] + 3); 72 int utf8Offset = constantPoolOffsets[reader.u2At(constantPoolOffsets[constantPoolIndex] + 3)]; 73 name = reader.utf8At(utf8Offset + 3, reader.u2At(utf8Offset + 1)); 74 break; 75 case ClassFileConstants.ClassTag : 76 utf8Offset = constantPoolOffsets[reader.u2At(constantPoolOffsets[i] + 1)]; 77 name = reader.utf8At(utf8Offset + 3, reader.u2At(utf8Offset + 1)); 78 break; 79 } 80 if (name == null || (name.length > 0 && name[0] == '[')) 81 break; // skip over array references 82 this.typeNames.add(CharOperation.splitOn('/', name)); 83 } 84 85 // convert type names into import references 86 int typeNamesLength = this.typeNames.size(); 87 ImportReference[] imports = new ImportReference[typeNamesLength]; 88 char[][][] set = this.typeNames.set; 89 int index = 0; 90 for (int i = 0, length = set.length; i < length; i++) { 91 char[][] typeName = set[i]; 92 if (typeName != null) { 93 imports[index++] = new ImportReference(typeName, new long[typeName.length]/*dummy positions*/, false/*not on demand*/, 0); 94 } 95 } 96 return imports; 97 } 98 99 /** 100 * Convert a binary type into an AST type declaration and put it in the given compilation unit. 101 */ buildTypeDeclaration(IType type, CompilationUnitDeclaration compilationUnit)102 public TypeDeclaration buildTypeDeclaration(IType type, CompilationUnitDeclaration compilationUnit) throws JavaModelException { 103 PackageFragment pkg = (PackageFragment) type.getPackageFragment(); 104 char[][] packageName = Util.toCharArrays(pkg.names); 105 106 if (packageName.length > 0) { 107 compilationUnit.currentPackage = new ImportReference(packageName, new long[]{0}, false, ClassFileConstants.AccDefault); 108 } 109 110 /* convert type */ 111 TypeDeclaration typeDeclaration = convert(type, null, null); 112 113 IType alreadyComputedMember = type; 114 IType parent = type.getDeclaringType(); 115 TypeDeclaration previousDeclaration = typeDeclaration; 116 while(parent != null) { 117 TypeDeclaration declaration = convert(parent, alreadyComputedMember, previousDeclaration); 118 119 alreadyComputedMember = parent; 120 previousDeclaration = declaration; 121 parent = parent.getDeclaringType(); 122 } 123 124 compilationUnit.types = new TypeDeclaration[]{previousDeclaration}; 125 126 return typeDeclaration; 127 } 128 convert(IField field, IType type)129 private FieldDeclaration convert(IField field, IType type) throws JavaModelException { 130 TypeReference typeReference = createTypeReference(field.getTypeSignature()); 131 if (typeReference == null) return null; 132 FieldDeclaration fieldDeclaration = new FieldDeclaration(); 133 134 fieldDeclaration.name = field.getElementName().toCharArray(); 135 fieldDeclaration.type = typeReference; 136 fieldDeclaration.modifiers = field.getFlags(); 137 138 return fieldDeclaration; 139 } 140 convert(IMethod method, IType type)141 private AbstractMethodDeclaration convert(IMethod method, IType type) throws JavaModelException { 142 143 AbstractMethodDeclaration methodDeclaration; 144 145 org.eclipse.jdt.internal.compiler.ast.TypeParameter[] typeParams = null; 146 147 // convert 1.5 specific constructs only if compliance is 1.5 or above 148 if (this.has1_5Compliance) { 149 /* convert type parameters */ 150 ITypeParameter[] typeParameters = method.getTypeParameters(); 151 if (typeParameters != null && typeParameters.length > 0) { 152 int parameterCount = typeParameters.length; 153 typeParams = new org.eclipse.jdt.internal.compiler.ast.TypeParameter[parameterCount]; 154 for (int i = 0; i < parameterCount; i++) { 155 ITypeParameter typeParameter = typeParameters[i]; 156 typeParams[i] = 157 createTypeParameter( 158 typeParameter.getElementName().toCharArray(), 159 stringArrayToCharArray(typeParameter.getBounds()), 160 0, 161 0); 162 } 163 } 164 } 165 166 if (method.isConstructor()) { 167 ConstructorDeclaration decl = new ConstructorDeclaration(this.compilationResult); 168 decl.bits &= ~ASTNode.IsDefaultConstructor; 169 decl.typeParameters = typeParams; 170 methodDeclaration = decl; 171 } else { 172 MethodDeclaration decl = type.isAnnotation() ? new AnnotationMethodDeclaration(this.compilationResult) : new MethodDeclaration(this.compilationResult); 173 /* convert return type */ 174 TypeReference typeReference = createTypeReference(method.getReturnType()); 175 if (typeReference == null) return null; 176 decl.returnType = typeReference; 177 decl.typeParameters = typeParams; 178 methodDeclaration = decl; 179 } 180 methodDeclaration.selector = method.getElementName().toCharArray(); 181 int flags = method.getFlags(); 182 boolean isVarargs = Flags.isVarargs(flags); 183 methodDeclaration.modifiers = flags & ~Flags.AccVarargs; 184 185 /* convert arguments */ 186 String[] argumentTypeNames = method.getParameterTypes(); 187 String[] argumentNames = method.getParameterNames(); 188 int argumentCount = argumentTypeNames == null ? 0 : argumentTypeNames.length; 189 // Ignore synthetic arguments (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=212224) 190 int startIndex = (method.isConstructor() && type.isMember() && !Flags.isStatic(type.getFlags())) ? 1 : 0; 191 argumentCount -= startIndex; 192 methodDeclaration.arguments = new Argument[argumentCount]; 193 for (int i = 0; i < argumentCount; i++) { 194 String argumentTypeName = argumentTypeNames[startIndex+i]; 195 TypeReference typeReference = createTypeReference(argumentTypeName); 196 if (typeReference == null) return null; 197 if (isVarargs && i == argumentCount-1) { 198 typeReference.bits |= ASTNode.IsVarArgs; 199 } 200 methodDeclaration.arguments[i] = new Argument( 201 argumentNames[i].toCharArray(), 202 0, 203 typeReference, 204 ClassFileConstants.AccDefault); 205 // do not care whether was final or not 206 } 207 208 /* convert thrown exceptions */ 209 String[] exceptionTypeNames = method.getExceptionTypes(); 210 int exceptionCount = exceptionTypeNames == null ? 0 : exceptionTypeNames.length; 211 if(exceptionCount > 0) { 212 methodDeclaration.thrownExceptions = new TypeReference[exceptionCount]; 213 for (int i = 0; i < exceptionCount; i++) { 214 TypeReference typeReference = createTypeReference(exceptionTypeNames[i]); 215 if (typeReference == null) return null; 216 methodDeclaration.thrownExceptions[i] = typeReference; 217 } 218 } 219 return methodDeclaration; 220 } 221 convert(IType type, IType alreadyComputedMember,TypeDeclaration alreadyComputedMemberDeclaration)222 private TypeDeclaration convert(IType type, IType alreadyComputedMember,TypeDeclaration alreadyComputedMemberDeclaration) throws JavaModelException { 223 /* create type declaration - can be member type */ 224 TypeDeclaration typeDeclaration = new TypeDeclaration(this.compilationResult); 225 226 if (type.getDeclaringType() != null) { 227 typeDeclaration.bits |= ASTNode.IsMemberType; 228 } 229 typeDeclaration.name = type.getElementName().toCharArray(); 230 typeDeclaration.modifiers = type.getFlags(); 231 232 233 /* set superclass and superinterfaces */ 234 if (type.getSuperclassName() != null) { 235 TypeReference typeReference = createTypeReference(type.getSuperclassTypeSignature()); 236 if (typeReference != null) { 237 typeDeclaration.superclass = typeReference; 238 typeDeclaration.superclass.bits |= ASTNode.IsSuperType; 239 } 240 } 241 242 String[] interfaceTypes = type.getSuperInterfaceTypeSignatures(); 243 int interfaceCount = interfaceTypes == null ? 0 : interfaceTypes.length; 244 typeDeclaration.superInterfaces = new TypeReference[interfaceCount]; 245 int count = 0; 246 for (int i = 0; i < interfaceCount; i++) { 247 TypeReference typeReference = createTypeReference(interfaceTypes[i]); 248 if (typeReference != null) { 249 typeDeclaration.superInterfaces[count] = typeReference; 250 typeDeclaration.superInterfaces[count++].bits |= ASTNode.IsSuperType; 251 } 252 } 253 if (count != interfaceCount) { 254 System.arraycopy(typeDeclaration.fields, 0, typeDeclaration.superInterfaces = new TypeReference[interfaceCount], 0, interfaceCount); 255 } 256 257 // convert 1.5 specific constructs only if compliance is 1.5 or above 258 if (this.has1_5Compliance) { 259 260 /* convert type parameters */ 261 ITypeParameter[] typeParameters = type.getTypeParameters(); 262 if (typeParameters != null && typeParameters.length > 0) { 263 int parameterCount = typeParameters.length; 264 org.eclipse.jdt.internal.compiler.ast.TypeParameter[] typeParams = new org.eclipse.jdt.internal.compiler.ast.TypeParameter[parameterCount]; 265 for (int i = 0; i < parameterCount; i++) { 266 ITypeParameter typeParameter = typeParameters[i]; 267 typeParams[i] = 268 createTypeParameter( 269 typeParameter.getElementName().toCharArray(), 270 stringArrayToCharArray(typeParameter.getBounds()), 271 0, 272 0); 273 } 274 275 typeDeclaration.typeParameters = typeParams; 276 } 277 } 278 279 /* convert member types */ 280 IType[] memberTypes = type.getTypes(); 281 int memberTypeCount = memberTypes == null ? 0 : memberTypes.length; 282 typeDeclaration.memberTypes = new TypeDeclaration[memberTypeCount]; 283 for (int i = 0; i < memberTypeCount; i++) { 284 if(alreadyComputedMember != null && alreadyComputedMember.getFullyQualifiedName().equals(memberTypes[i].getFullyQualifiedName())) { 285 typeDeclaration.memberTypes[i] = alreadyComputedMemberDeclaration; 286 } else { 287 typeDeclaration.memberTypes[i] = convert(memberTypes[i], null, null); 288 } 289 typeDeclaration.memberTypes[i].enclosingType = typeDeclaration; 290 } 291 292 /* convert fields */ 293 IField[] fields = type.getFields(); 294 int fieldCount = fields == null ? 0 : fields.length; 295 typeDeclaration.fields = new FieldDeclaration[fieldCount]; 296 count = 0; 297 for (int i = 0; i < fieldCount; i++) { 298 FieldDeclaration fieldDeclaration = convert(fields[i], type); 299 if (fieldDeclaration != null) { 300 typeDeclaration.fields[count++] = fieldDeclaration; 301 } 302 } 303 if (count != fieldCount) { 304 System.arraycopy(typeDeclaration.fields, 0, typeDeclaration.fields = new FieldDeclaration[count], 0, count); 305 } 306 307 /* convert methods - need to add default constructor if necessary */ 308 IMethod[] methods = type.getMethods(); 309 int methodCount = methods == null ? 0 : methods.length; 310 311 /* source type has a constructor ? */ 312 /* by default, we assume that one is needed. */ 313 int neededCount = 1; 314 for (int i = 0; i < methodCount; i++) { 315 if (methods[i].isConstructor()) { 316 neededCount = 0; 317 // Does not need the extra constructor since one constructor already exists. 318 break; 319 } 320 } 321 boolean isInterface = type.isInterface(); 322 neededCount = isInterface ? 0 : neededCount; 323 typeDeclaration.methods = new AbstractMethodDeclaration[methodCount + neededCount]; 324 if (neededCount != 0) { // add default constructor in first position 325 typeDeclaration.methods[0] = typeDeclaration.createDefaultConstructor(false, false); 326 } 327 boolean hasAbstractMethods = false; 328 count = 0; 329 for (int i = 0; i < methodCount; i++) { 330 AbstractMethodDeclaration method = convert(methods[i], type); 331 if (method != null) { 332 boolean isAbstract; 333 if ((isAbstract = method.isAbstract()) || isInterface) { // fix-up flag 334 method.modifiers |= ExtraCompilerModifiers.AccSemicolonBody; 335 } 336 if (isAbstract) { 337 hasAbstractMethods = true; 338 } 339 typeDeclaration.methods[neededCount + (count++)] = method; 340 } 341 } 342 if (count != methodCount) { 343 System.arraycopy(typeDeclaration.methods, 0, typeDeclaration.methods = new AbstractMethodDeclaration[count + neededCount], 0, count + neededCount); 344 } 345 if (hasAbstractMethods) { 346 typeDeclaration.bits |= ASTNode.HasAbstractMethods; 347 } 348 return typeDeclaration; 349 } 350 stringArrayToCharArray(String[] strings)351 private static char[][] stringArrayToCharArray(String[] strings) { 352 if (strings == null) return null; 353 354 int length = strings.length; 355 if (length == 0) return CharOperation.NO_CHAR_CHAR; 356 357 char[][] result = new char [length][]; 358 for (int i = 0; i < length; i++) { 359 result[i] = strings[i].toCharArray(); 360 } 361 362 return result; 363 } 364 createTypeReference(String typeSignature)365 private TypeReference createTypeReference(String typeSignature) { 366 TypeReference result = createTypeReference(typeSignature, 0, 0); 367 if (this.typeNames != null && result instanceof QualifiedTypeReference) { 368 this.typeNames.add(((QualifiedTypeReference)result).tokens); 369 } 370 return result; 371 } 372 } 373