1 /*******************************************************************************
2  * Copyright (c) 2000, 2017 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 - Contribution for
14  *								Bug 429958 - [1.8][null] evaluate new DefaultLocation attribute of @NonNullByDefault
15  *								Bug 435570 - [1.8][null] @NonNullByDefault illegally tries to affect "throws E"
16  *								Bug 466713 - Null Annotations: NullPointerException using <int @Nullable []> as Type Param
17  *******************************************************************************/
18 package org.eclipse.jdt.internal.compiler.ast;
19 
20 import java.util.function.Consumer;
21 
22 import org.eclipse.jdt.core.compiler.CharOperation;
23 import org.eclipse.jdt.internal.compiler.ASTVisitor;
24 import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
25 import org.eclipse.jdt.internal.compiler.lookup.Binding;
26 import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
27 import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
28 import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
29 import org.eclipse.jdt.internal.compiler.lookup.Scope;
30 import org.eclipse.jdt.internal.compiler.lookup.TagBits;
31 import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
32 import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
33 
34 public class ArrayTypeReference extends SingleTypeReference {
35 	public int dimensions;
36 	private Annotation[][] annotationsOnDimensions; // jsr308 style type annotations on dimensions.
37 	public int originalSourceEnd;
38 	public int extendedDimensions;
39 	public TypeBinding leafComponentTypeWithoutDefaultNullness;
40 
41 	/**
42 	 * ArrayTypeReference constructor comment.
43 	 * @param source char[]
44 	 * @param dimensions int
45 	 * @param pos int
46 	 */
ArrayTypeReference(char[] source, int dimensions, long pos)47 	public ArrayTypeReference(char[] source, int dimensions, long pos) {
48 
49 		super(source, pos);
50 		this.originalSourceEnd = this.sourceEnd;
51 		this.dimensions = dimensions ;
52 		this.annotationsOnDimensions = null;
53 	}
54 
ArrayTypeReference(char[] source, int dimensions, Annotation[][] annotationsOnDimensions, long pos)55 	public ArrayTypeReference(char[] source, int dimensions, Annotation[][] annotationsOnDimensions, long pos) {
56 		this(source, dimensions, pos);
57 		if (annotationsOnDimensions != null) {
58 			this.bits |= ASTNode.HasTypeAnnotations;
59 		}
60 		this.annotationsOnDimensions = annotationsOnDimensions;
61 	}
62 
63 	@Override
dimensions()64 	public int dimensions() {
65 
66 		return this.dimensions;
67 	}
68 
69 	@Override
extraDimensions()70 	public int extraDimensions() {
71 		return this.extendedDimensions;
72 	}
73 
74 	/**
75 	 @see org.eclipse.jdt.internal.compiler.ast.TypeReference#getAnnotationsOnDimensions(boolean)
76 	*/
77 	@Override
getAnnotationsOnDimensions(boolean useSourceOrder)78 	public Annotation[][] getAnnotationsOnDimensions(boolean useSourceOrder) {
79 		if (useSourceOrder || this.annotationsOnDimensions == null || this.annotationsOnDimensions.length == 0 || this.extendedDimensions == 0 || this.extendedDimensions == this.dimensions)
80 			return this.annotationsOnDimensions;
81 		Annotation [][] externalAnnotations = new Annotation[this.dimensions][];
82 		final int baseDimensions = this.dimensions - this.extendedDimensions;
83 		System.arraycopy(this.annotationsOnDimensions, baseDimensions, externalAnnotations, 0, this.extendedDimensions);
84 		System.arraycopy(this.annotationsOnDimensions, 0, externalAnnotations, this.extendedDimensions, baseDimensions);
85 		return externalAnnotations;
86 	}
87 
88 	@Override
setAnnotationsOnDimensions(Annotation [][] annotationsOnDimensions)89 	public void setAnnotationsOnDimensions(Annotation [][] annotationsOnDimensions) {
90 		this.annotationsOnDimensions = annotationsOnDimensions;
91 	}
92 	/**
93 	 * @return char[][]
94 	 */
95 	@Override
getParameterizedTypeName()96 	public char [][] getParameterizedTypeName(){
97 		int dim = this.dimensions;
98 		char[] dimChars = new char[dim*2];
99 		for (int i = 0; i < dim; i++) {
100 			int index = i*2;
101 			dimChars[index] = '[';
102 			dimChars[index+1] = ']';
103 		}
104 		return new char[][]{ CharOperation.concat(this.token, dimChars) };
105 	}
106 	@Override
getTypeBinding(Scope scope)107 	protected TypeBinding getTypeBinding(Scope scope) {
108 
109 		if (this.resolvedType != null) {
110 			return this.resolvedType;
111 		}
112 		if (this.dimensions > 255) {
113 			scope.problemReporter().tooManyDimensions(this);
114 		}
115 		TypeBinding leafComponentType = scope.getType(this.token);
116 		return scope.createArrayType(leafComponentType, this.dimensions);
117 
118 	}
119 
120 	@Override
printExpression(int indent, StringBuffer output)121 	public StringBuffer printExpression(int indent, StringBuffer output){
122 
123 		super.printExpression(indent, output);
124 		if ((this.bits & IsVarArgs) != 0) {
125 			for (int i= 0 ; i < this.dimensions - 1; i++) {
126 				if (this.annotationsOnDimensions != null && this.annotationsOnDimensions[i] != null) {
127 					output.append(' ');
128 					printAnnotations(this.annotationsOnDimensions[i], output);
129 					output.append(' ');
130 				}
131 				output.append("[]"); //$NON-NLS-1$
132 			}
133 			if (this.annotationsOnDimensions != null && this.annotationsOnDimensions[this.dimensions - 1] != null) {
134 				output.append(' ');
135 				printAnnotations(this.annotationsOnDimensions[this.dimensions - 1], output);
136 				output.append(' ');
137 			}
138 			output.append("..."); //$NON-NLS-1$
139 		} else {
140 			for (int i= 0 ; i < this.dimensions; i++) {
141 				if (this.annotationsOnDimensions != null && this.annotationsOnDimensions[i] != null) {
142 					output.append(" "); //$NON-NLS-1$
143 					printAnnotations(this.annotationsOnDimensions[i], output);
144 					output.append(" "); //$NON-NLS-1$
145 				}
146 				output.append("[]"); //$NON-NLS-1$
147 			}
148 		}
149 		return output;
150 	}
151 
152 	@Override
traverse(ASTVisitor visitor, BlockScope scope)153 	public void traverse(ASTVisitor visitor, BlockScope scope) {
154 		if (visitor.visit(this, scope)) {
155 			if (this.annotations != null) {
156 				Annotation [] typeAnnotations = this.annotations[0];
157 				for (int i = 0, length = typeAnnotations == null ? 0 : typeAnnotations.length; i < length; i++) {
158 					typeAnnotations[i].traverse(visitor, scope);
159 				}
160 			}
161 			if (this.annotationsOnDimensions != null) {
162 				for (int i = 0, max = this.annotationsOnDimensions.length; i < max; i++) {
163 					Annotation[] annotations2 = this.annotationsOnDimensions[i];
164 					if (annotations2 != null) {
165 						for (int j = 0, max2 = annotations2.length; j < max2; j++) {
166 							Annotation annotation = annotations2[j];
167 							annotation.traverse(visitor, scope);
168 						}
169 					}
170 				}
171 			}
172 		}
173 		visitor.endVisit(this, scope);
174 	}
175 
176 	@Override
traverse(ASTVisitor visitor, ClassScope scope)177 	public void traverse(ASTVisitor visitor, ClassScope scope) {
178 		if (visitor.visit(this, scope)) {
179 			if (this.annotations != null) {
180 				Annotation [] typeAnnotations = this.annotations[0];
181 				for (int i = 0, length = typeAnnotations == null ? 0 : typeAnnotations.length; i < length; i++) {
182 					typeAnnotations[i].traverse(visitor, scope);
183 				}
184 			}
185 			if (this.annotationsOnDimensions != null) {
186 				for (int i = 0, max = this.annotationsOnDimensions.length; i < max; i++) {
187 					Annotation[] annotations2 = this.annotationsOnDimensions[i];
188 					if (annotations2 != null) {
189 						for (int j = 0, max2 = annotations2.length; j < max2; j++) {
190 							Annotation annotation = annotations2[j];
191 							annotation.traverse(visitor, scope);
192 						}
193 					}
194 				}
195 			}
196 		}
197 		visitor.endVisit(this, scope);
198 	}
199 
200 	@Override
internalResolveType(Scope scope, int location)201 	protected TypeBinding internalResolveType(Scope scope, int location) {
202 		TypeBinding internalResolveType = super.internalResolveType(scope, location);
203 		internalResolveType = maybeMarkArrayContentsNonNull(scope, internalResolveType, this.sourceStart, this.dimensions,
204 									leafType -> this.leafComponentTypeWithoutDefaultNullness = leafType);
205 
206 		return internalResolveType;
207 	}
208 
maybeMarkArrayContentsNonNull(Scope scope, TypeBinding typeBinding, int sourceStart, int dimensions, Consumer<TypeBinding> leafConsumer)209 	static TypeBinding maybeMarkArrayContentsNonNull(Scope scope, TypeBinding typeBinding, int sourceStart, int dimensions, Consumer<TypeBinding> leafConsumer) {
210 		LookupEnvironment environment = scope.environment();
211 		if (environment.usesNullTypeAnnotations()
212 				&& scope.hasDefaultNullnessFor(Binding.DefaultLocationArrayContents, sourceStart)) {
213 			AnnotationBinding nonNullAnnotation = environment.getNonNullAnnotation();
214 			typeBinding = addNonNullToDimensions(scope, typeBinding, nonNullAnnotation, dimensions);
215 
216 			TypeBinding leafComponentType = typeBinding.leafComponentType();
217 			if ((leafComponentType.tagBits & TagBits.AnnotationNullMASK) == 0 && leafComponentType.acceptsNonNullDefault()) {
218 				if (leafConsumer != null)
219 					leafConsumer.accept(leafComponentType);
220 				TypeBinding nonNullLeafComponentType = scope.environment().createAnnotatedType(leafComponentType,
221 						new AnnotationBinding[] { nonNullAnnotation });
222 				typeBinding = scope.createArrayType(nonNullLeafComponentType, typeBinding.dimensions(),
223 						typeBinding.getTypeAnnotations());
224 			}
225 		}
226 		return typeBinding;
227 	}
228 
addNonNullToDimensions(Scope scope, TypeBinding typeBinding, AnnotationBinding nonNullAnnotation, int dimensions2)229 	static TypeBinding addNonNullToDimensions(Scope scope, TypeBinding typeBinding,
230 			AnnotationBinding nonNullAnnotation, int dimensions2) {
231 		AnnotationBinding[][] newAnnots = new AnnotationBinding[dimensions2][];
232 		AnnotationBinding[] oldAnnots = typeBinding.getTypeAnnotations();
233 		if (oldAnnots == null) {
234 			for (int i = 1; i < dimensions2; i++) {
235 				newAnnots[i] = new AnnotationBinding[] { nonNullAnnotation };
236 			}
237 		} else {
238 			int j = 0;
239 			for (int i = 0; i < dimensions2; i++) {
240 				if (j >= oldAnnots.length || oldAnnots[j] == null) {
241 					if (i != 0) {
242 						newAnnots[i] = new AnnotationBinding[] { nonNullAnnotation };
243 					}
244 					j++;
245 				} else {
246 					int k = j;
247 					boolean seen = false;
248 					while (oldAnnots[k] != null) {
249 						seen |= oldAnnots[k].getAnnotationType()
250 								.hasNullBit(TypeIds.BitNonNullAnnotation | TypeIds.BitNullableAnnotation);
251 						k++;
252 					}
253 					if (seen || i == 0) {
254 						if (k > j) {
255 							AnnotationBinding[] annotationsForDimension = new AnnotationBinding[k - j];
256 							System.arraycopy(oldAnnots, j, annotationsForDimension, 0, k - j);
257 							newAnnots[i] = annotationsForDimension;
258 						}
259 					} else {
260 						AnnotationBinding[] annotationsForDimension = new AnnotationBinding[k - j + 1];
261 						annotationsForDimension[0] = nonNullAnnotation;
262 						System.arraycopy(oldAnnots, j, annotationsForDimension, 1, k - j);
263 						newAnnots[i] = annotationsForDimension;
264 					}
265 					j = k + 1;
266 				}
267 			}
268 		}
269 		return scope.environment().createAnnotatedType(typeBinding, newAnnots);
270 	}
271 
272 	@Override
hasNullTypeAnnotation(AnnotationPosition position)273 	public boolean hasNullTypeAnnotation(AnnotationPosition position) {
274 		switch (position) {
275 			case LEAF_TYPE:
276 				// ignore annotationsOnDimensions:
277 				return super.hasNullTypeAnnotation(position);
278 			case MAIN_TYPE:
279 				// outermost dimension only:
280 				if (this.annotationsOnDimensions != null && this.annotationsOnDimensions.length > 0) {
281 					Annotation[] innerAnnotations = this.annotationsOnDimensions[0];
282 					return containsNullAnnotation(innerAnnotations);
283 				}
284 				// e.g. subclass ParameterizedSingleTypeReference is not only used for arrays
285 				return super.hasNullTypeAnnotation(position);
286 			case ANY:
287 				if (super.hasNullTypeAnnotation(position))
288 					return true;
289 				if (this.resolvedType != null && !this.resolvedType.hasNullTypeAnnotations())
290 					return false; // shortcut
291 				if (this.annotationsOnDimensions != null) {
292 					for (int i = 0; i < this.annotationsOnDimensions.length; i++) {
293 						Annotation[] innerAnnotations = this.annotationsOnDimensions[i];
294 						if (containsNullAnnotation(innerAnnotations))
295 							return true;
296 					}
297 				}
298 		}
299     	return false;
300 	}
301 }
302