Skip to main content
summaryrefslogtreecommitdiffstats
blob: 514c3e69edab3fe72a848e637239e0eb21812534 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
/*******************************************************************************
 * Copyright (c) 2000, 2014 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Technical University Berlin - extended API and implementation
 *     Stephan Herrmann - Contributions for
 *								bug 186342 - [compiler][null] Using annotations for null checking
 *								bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
 *								bug 392384 - [1.8][compiler][null] Restore nullness info from type annotations in class files
 *								Bug 415043 - [1.8][null] Follow-up re null type annotations after bug 392099
 *								Bug 417295 - [1.8[[null] Massage type annotated null analysis to gel well with deep encoded type bindings.
 *								Bug 425152 - [1.8] [compiler] Lambda Expression not resolved but flow analyzed leading to NPE.
 *								Bug 429958 - [1.8][null] evaluate new DefaultLocation attribute of @NonNullByDefault
 *******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;

import org.eclipse.jdt.internal.compiler.ast.NullAnnotationMatching;
import org.eclipse.jdt.internal.compiler.ast.Wildcard;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.ITeamAnchor;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.RoleModel;

/**
 * Binding denoting a method after type parameter substitutions got performed.
 * On parameterized type bindings, all methods got substituted, regardless whether
 * their signature did involve generics or not, so as to get the proper declaringClass for
 * these methods.
 */
public class ParameterizedMethodBinding extends MethodBinding {

	protected MethodBinding originalMethod;

	/**
	 * Create method of parameterized type, substituting original parameters/exception/return type with type arguments.
	 */
	public ParameterizedMethodBinding(final ParameterizedTypeBinding parameterizedDeclaringClass, MethodBinding originalMethod) {
		super(
				originalMethod.modifiers,
				originalMethod.selector,
				originalMethod.returnType,
				originalMethod.parameters,
				originalMethod.thrownExceptions,
				parameterizedDeclaringClass);
		this.originalMethod = originalMethod;
		/* missing type bit cannot be copied as is it might come from the return type or a parameter type that
		 * is substituted by a raw type.
		 */
		this.tagBits = originalMethod.tagBits & ~TagBits.HasMissingType;
		this.parameterNonNullness = originalMethod.parameterNonNullness;
		this.defaultNullness = originalMethod.defaultNullness;

		final TypeVariableBinding[] originalVariables = originalMethod.typeVariables;
		Substitution substitution = null;
		final int length = originalVariables.length;
//{ObjectTeams: along the tsuper-link staticness is ignored as methods can still be overridden:
/* orig:
		final boolean isStatic = originalMethod.isStatic(); 
  :giro */
		final boolean isStatic = originalMethod.isStatic() && !RoleModel.hasTagBit(parameterizedDeclaringClass, RoleModel.IsViewedAsTSuper);
// SH}
		if (length == 0) {
			this.typeVariables = Binding.NO_TYPE_VARIABLES;
			if (!isStatic) substitution = parameterizedDeclaringClass;
		} else {
			// at least fix up the declaringElement binding + bound substitution if non static
			final TypeVariableBinding[] substitutedVariables = new TypeVariableBinding[length];
			for (int i = 0; i < length; i++) { // copy original type variable to relocate
				TypeVariableBinding originalVariable = originalVariables[i];
				substitutedVariables[i] = new TypeVariableBinding(originalVariable.sourceName, this, originalVariable.rank, parameterizedDeclaringClass.environment);
				substitutedVariables[i].tagBits |= (originalVariable.tagBits & (TagBits.AnnotationNullMASK|TagBits.HasNullTypeAnnotation));
			}
			this.typeVariables = substitutedVariables;

			// need to substitute old var refs with new ones (double substitution: declaringClass + new type variables)
			substitution = new Substitution() {
				public LookupEnvironment environment() {
					return parameterizedDeclaringClass.environment;
				}
				public boolean isRawSubstitution() {
					return !isStatic && parameterizedDeclaringClass.isRawSubstitution();
				}
				public TypeBinding substitute(TypeVariableBinding typeVariable) {
					// check this variable can be substituted given copied variables
					if (typeVariable.rank < length && TypeBinding.equalsEquals(originalVariables[typeVariable.rank], typeVariable)) {
						TypeBinding substitute = substitutedVariables[typeVariable.rank];
						return typeVariable.hasTypeAnnotations() ? environment().createAnnotatedType(substitute, typeVariable.getTypeAnnotations()) : substitute;
					}
					if (!isStatic)
						return parameterizedDeclaringClass.substitute(typeVariable);
					return typeVariable;
				}
//{ObjectTeams: implement new method from Substitution
				public ITeamAnchor substituteAnchor(ITeamAnchor anchor, int rank) {
					return null; // FIXME(SH): implement
				}
// SH}
			};

			// initialize new variable bounds
			for (int i = 0; i < length; i++) {
				TypeVariableBinding originalVariable = originalVariables[i];
				TypeVariableBinding substitutedVariable = substitutedVariables[i];
				TypeBinding substitutedSuperclass = Scope.substitute(substitution, originalVariable.superclass);
				ReferenceBinding[] substitutedInterfaces = Scope.substitute(substitution, originalVariable.superInterfaces);
				if (originalVariable.firstBound != null) {
					TypeBinding firstBound;
					firstBound = TypeBinding.equalsEquals(originalVariable.firstBound, originalVariable.superclass)
						? substitutedSuperclass // could be array type or interface
						: substitutedInterfaces[0];
					substitutedVariable.setFirstBound(firstBound);
				}
				switch (substitutedSuperclass.kind()) {
					case Binding.ARRAY_TYPE :
						substitutedVariable.setSuperClass(parameterizedDeclaringClass.environment.getResolvedType(TypeConstants.JAVA_LANG_OBJECT, null));
						substitutedVariable.setSuperInterfaces(substitutedInterfaces);
						break;
					default:
						if (substitutedSuperclass.isInterface()) {
							substitutedVariable.setSuperClass(parameterizedDeclaringClass.environment.getResolvedType(TypeConstants.JAVA_LANG_OBJECT, null));
							int interfaceCount = substitutedInterfaces.length;
							System.arraycopy(substitutedInterfaces, 0, substitutedInterfaces = new ReferenceBinding[interfaceCount+1], 1, interfaceCount);
							substitutedInterfaces[0] = (ReferenceBinding) substitutedSuperclass;
							substitutedVariable.setSuperInterfaces(substitutedInterfaces);
						} else {
							substitutedVariable.setSuperClass((ReferenceBinding) substitutedSuperclass); // typeVar was extending other typeVar which got substituted with interface
							substitutedVariable.setSuperInterfaces(substitutedInterfaces);
						}
				}
			}
		}
		if (substitution != null) {
			this.returnType = Scope.substitute(substitution, this.returnType);
			this.parameters = Scope.substitute(substitution, this.parameters);
			this.thrownExceptions = Scope.substitute(substitution, this.thrownExceptions);
			// error case where exception type variable would have been substituted by a non-reference type (207573)
			if (this.thrownExceptions == null) this.thrownExceptions = Binding.NO_EXCEPTIONS;

			// after substitution transfer nullness information from type annotations:
			if (parameterizedDeclaringClass.environment.globalOptions.isAnnotationBasedNullAnalysisEnabled) {
				long returnNullBits = NullAnnotationMatching.validNullTagBits(this.returnType.tagBits);
				if (returnNullBits != 0L) {
					this.tagBits &= ~TagBits.AnnotationNullMASK;
					this.tagBits |= returnNullBits;
				}
				int parametersLen = this.parameters.length;
				for (int i=0; i<parametersLen; i++) {
					long paramTagBits = NullAnnotationMatching.validNullTagBits(this.parameters[i].tagBits);
					if (paramTagBits != 0) {
						if (this.parameterNonNullness == null)
							this.parameterNonNullness = new Boolean[parametersLen];
						this.parameterNonNullness[i] = Boolean.valueOf(paramTagBits == TagBits.AnnotationNonNull);
					}
				}
			}
		}
		checkMissingType: {
			if ((this.tagBits & TagBits.HasMissingType) != 0)
				break checkMissingType;
			if ((this.returnType.tagBits & TagBits.HasMissingType) != 0) {
				this.tagBits |=  TagBits.HasMissingType;
				break checkMissingType;
			}
			for (int i = 0, max = this.parameters.length; i < max; i++) {
				if ((this.parameters[i].tagBits & TagBits.HasMissingType) != 0) {
					this.tagBits |=  TagBits.HasMissingType;
					break checkMissingType;
				}
			}
			for (int i = 0, max = this.thrownExceptions.length; i < max; i++) {
				if ((this.thrownExceptions[i].tagBits & TagBits.HasMissingType) != 0) {
					this.tagBits |=  TagBits.HasMissingType;
					break checkMissingType;
				}
			}
		}
	}

	/**
	 * Create method of parameterized type, substituting original parameters/exception/return type with type arguments.
	 * This is a CODE ASSIST method ONLY.
	 */
	public ParameterizedMethodBinding(final ReferenceBinding declaringClass, MethodBinding originalMethod, char[][] alternateParamaterNames, final LookupEnvironment environment) {
		super(
				originalMethod.modifiers,
				originalMethod.selector,
				 originalMethod.returnType,
				originalMethod.parameters,
				originalMethod.thrownExceptions,
				declaringClass);
		this.originalMethod = originalMethod;
		/* missing type bit cannot be copied as is it might come from the return type or a parameter type that
		 * is substituted by a raw type.
		 */
		this.tagBits = originalMethod.tagBits & ~TagBits.HasMissingType;
		this.parameterNonNullness = originalMethod.parameterNonNullness;
		this.defaultNullness = originalMethod.defaultNullness;

		final TypeVariableBinding[] originalVariables = originalMethod.typeVariables;
		Substitution substitution = null;
		final int length = originalVariables.length;
		if (length == 0) {
			this.typeVariables = Binding.NO_TYPE_VARIABLES;
		} else {
			// at least fix up the declaringElement binding + bound substitution if non static
			final TypeVariableBinding[] substitutedVariables = new TypeVariableBinding[length];
			for (int i = 0; i < length; i++) { // copy original type variable to relocate
				TypeVariableBinding originalVariable = originalVariables[i];
				substitutedVariables[i] = new TypeVariableBinding(
						alternateParamaterNames == null ?
								originalVariable.sourceName :
								alternateParamaterNames[i],
							this,
							originalVariable.rank,
							environment);
				substitutedVariables[i].tagBits |= (originalVariable.tagBits & (TagBits.AnnotationNullMASK|TagBits.HasNullTypeAnnotation));
			}
			this.typeVariables = substitutedVariables;

			// need to substitute old var refs with new ones (double substitution: declaringClass + new type variables)
			substitution = new Substitution() {
				public LookupEnvironment environment() {
					return environment;
				}
				public boolean isRawSubstitution() {
					return false;
				}
				public TypeBinding substitute(TypeVariableBinding typeVariable) {
			        // check this variable can be substituted given copied variables
			        if (typeVariable.rank < length && TypeBinding.equalsEquals(originalVariables[typeVariable.rank], typeVariable)) {
			        	TypeBinding substitute = substitutedVariables[typeVariable.rank];
						return typeVariable.hasTypeAnnotations() ? environment().createAnnotatedType(substitute, typeVariable.getTypeAnnotations()) : substitute;
			        }
			        return typeVariable;
				}
//{ObjectTeams: implement new method from Substitution
				public ITeamAnchor substituteAnchor(ITeamAnchor anchor, int rank) {
					return null; // FIXME(SH): implement
				}
// SH}
			};

			// initialize new variable bounds
			for (int i = 0; i < length; i++) {
				TypeVariableBinding originalVariable = originalVariables[i];
				TypeVariableBinding substitutedVariable = substitutedVariables[i];
				TypeBinding substitutedSuperclass = Scope.substitute(substitution, originalVariable.superclass);
				ReferenceBinding[] substitutedInterfaces = Scope.substitute(substitution, originalVariable.superInterfaces);
				if (originalVariable.firstBound != null) {
					TypeBinding firstBound;
					firstBound = TypeBinding.equalsEquals(originalVariable.firstBound, originalVariable.superclass)
						? substitutedSuperclass // could be array type or interface
						: substitutedInterfaces[0];
					substitutedVariable.setFirstBound(firstBound);
				}
				switch (substitutedSuperclass.kind()) {
					case Binding.ARRAY_TYPE :
						substitutedVariable.setSuperClass(environment.getResolvedType(TypeConstants.JAVA_LANG_OBJECT, null));
						substitutedVariable.setSuperInterfaces(substitutedInterfaces);
						break;
					default:
						if (substitutedSuperclass.isInterface()) {
							substitutedVariable.setSuperClass(environment.getResolvedType(TypeConstants.JAVA_LANG_OBJECT, null));
							int interfaceCount = substitutedInterfaces.length;
							System.arraycopy(substitutedInterfaces, 0, substitutedInterfaces = new ReferenceBinding[interfaceCount+1], 1, interfaceCount);
							substitutedInterfaces[0] = (ReferenceBinding) substitutedSuperclass;
							substitutedVariable.setSuperInterfaces(substitutedInterfaces);
						} else {
							substitutedVariable.setSuperClass((ReferenceBinding) substitutedSuperclass); // typeVar was extending other typeVar which got substituted with interface
							substitutedVariable.setSuperInterfaces(substitutedInterfaces);
						}
				}
			}
		}
		if (substitution != null) {
			this.returnType = Scope.substitute(substitution, this.returnType);
			this.parameters = Scope.substitute(substitution, this.parameters);
			this.thrownExceptions = Scope.substitute(substitution, this.thrownExceptions);
		    // error case where exception type variable would have been substituted by a non-reference type (207573)
		    if (this.thrownExceptions == null) this.thrownExceptions = Binding.NO_EXCEPTIONS;
		}
		checkMissingType: {
			if ((this.tagBits & TagBits.HasMissingType) != 0)
				break checkMissingType;
			if ((this.returnType.tagBits & TagBits.HasMissingType) != 0) {
				this.tagBits |=  TagBits.HasMissingType;
				break checkMissingType;
			}
			for (int i = 0, max = this.parameters.length; i < max; i++) {
				if ((this.parameters[i].tagBits & TagBits.HasMissingType) != 0) {
					this.tagBits |=  TagBits.HasMissingType;
					break checkMissingType;
				}
			}
			for (int i = 0, max = this.thrownExceptions.length; i < max; i++) {
				if ((this.thrownExceptions[i].tagBits & TagBits.HasMissingType) != 0) {
					this.tagBits |=  TagBits.HasMissingType;
					break checkMissingType;
				}
			}
		}
	}

	public ParameterizedMethodBinding() {
		// no init
	}

	/**
	 * The type of x.getClass() is substituted from 'Class<? extends Object>' into: 'Class<? extends raw(X)>
	 */
	public static ParameterizedMethodBinding instantiateGetClass(TypeBinding receiverType, MethodBinding originalMethod, Scope scope) {
		ParameterizedMethodBinding method = new ParameterizedMethodBinding();
		method.modifiers = originalMethod.modifiers;
		method.selector = originalMethod.selector;
		method.declaringClass = originalMethod.declaringClass;
		method.typeVariables = Binding.NO_TYPE_VARIABLES;
		method.originalMethod = originalMethod;
		method.parameters = originalMethod.parameters;
		method.thrownExceptions = originalMethod.thrownExceptions;
		method.tagBits = originalMethod.tagBits;
		ReferenceBinding genericClassType = scope.getJavaLangClass();
		LookupEnvironment environment = scope.environment();
		TypeBinding rawType = environment.convertToRawType(receiverType.erasure(), false /*do not force conversion of enclosing types*/);
		method.returnType = environment.createParameterizedType(
			genericClassType,
			new TypeBinding[] {  environment.createWildcard(genericClassType, 0, rawType, null /*no extra bound*/, Wildcard.EXTENDS) },
			null);
		if ((method.returnType.tagBits & TagBits.HasMissingType) != 0) {
			method.tagBits |=  TagBits.HasMissingType;
		}
		return method;
	}

	/**
	 * Returns true if some parameters got substituted.
	 */
	public boolean hasSubstitutedParameters() {
		return this.parameters != this.originalMethod.parameters;
	}

	/**
	 * Returns true if the return type got substituted.
	 */
	public boolean hasSubstitutedReturnType() {
		return this.returnType != this.originalMethod.returnType; //$IDENTITY-COMPARISON$
	}

	/**
	 * Returns the original method (as opposed to parameterized instances)
	 */
	public MethodBinding original() {
		return this.originalMethod.original();
	}
	
	
	public MethodBinding shallowOriginal() {
		return this.originalMethod;
	}
}

Back to the top