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