Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 625cb97625a6ebf8548ef10b487584343b84d7ee (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
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
/*******************************************************************************
 * Copyright (c) 2013, 2017 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Jesper S Moller - Contributions for
 *							bug 382701 - [1.8][compiler] Implement semantic analysis of Lambda expressions & Reference expression
 *							Bug 405066 - [1.8][compiler][codegen] Implement code generation infrastructure for JSR335
 *     Stephan Herrmann - Contribution for
 *							Bug 400874 - [1.8][compiler] Inference infrastructure should evolve to meet JLS8 18.x (Part G of JSR335 spec)
 *							Bug 423504 - [1.8] Implement "18.5.3 Functional Interface Parameterization Inference"
 *							Bug 425142 - [1.8][compiler] NPE in ConstraintTypeFormula.reduceSubType
 *							Bug 425153 - [1.8] Having wildcard allows incompatible types in a lambda expression
 *							Bug 425156 - [1.8] Lambda as an argument is flagged with incompatible error
 *							Bug 424403 - [1.8][compiler] Generic method call with method reference argument fails to resolve properly.
 *							Bug 427438 - [1.8][compiler] NPE at org.eclipse.jdt.internal.compiler.ast.ConditionalExpression.generateCode(ConditionalExpression.java:280)
 *							Bug 428352 - [1.8][compiler] Resolution errors don't always surface
 *							Bug 446442 - [1.8] merge null annotations from super methods
 *     Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for
 *                          Bug 405104 - [1.8][compiler][codegen] Implement support for serializeable lambdas
 *******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;

import static org.eclipse.jdt.internal.compiler.ast.ExpressionContext.VANILLA_CONTEXT;

import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy;
import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
import org.eclipse.jdt.internal.compiler.lookup.IntersectionTypeBinding18;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.MethodVerifier;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
import org.eclipse.jdt.internal.compiler.lookup.RawTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBindingVisitor;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;

public abstract class FunctionalExpression extends Expression {
	
	protected TypeBinding expectedType;
	public MethodBinding descriptor;
	public MethodBinding binding;                 // Code generation binding. May include synthetics. See getMethodBinding()
	protected MethodBinding actualMethodBinding;  // void of synthetics.
	boolean ignoreFurtherInvestigation;
	protected ExpressionContext expressionContext = VANILLA_CONTEXT;
	public CompilationResult compilationResult;
	public BlockScope enclosingScope;
	public int bootstrapMethodNumber = -1;
	public boolean shouldCaptureInstance = false; // Whether the expression needs access to instance data of enclosing type
	protected static IErrorHandlingPolicy silentErrorHandlingPolicy = DefaultErrorHandlingPolicies.ignoreAllProblems();
	private boolean hasReportedSamProblem = false;
	public boolean isSerializable;
	public int ordinal;

	public FunctionalExpression(CompilationResult compilationResult) {
		this.compilationResult = compilationResult;
	}
	
	public FunctionalExpression() {
		super();
	}
	
	@Override
	public boolean isBoxingCompatibleWith(TypeBinding targetType, Scope scope) {
		return false;
	}
	
	public void setCompilationResult(CompilationResult compilationResult) {
		this.compilationResult = compilationResult;
	}
	
	// Return the actual (non-code generation) method binding that is void of synthetics.
	public MethodBinding getMethodBinding() {
		return null;
	}

	@Override
	public void setExpectedType(TypeBinding expectedType) {
		this.expectedType = expectedType;
	}
	
	@Override
	public void setExpressionContext(ExpressionContext context) {
		this.expressionContext = context;
	}

	@Override
	public ExpressionContext getExpressionContext() {
		return this.expressionContext;
	}

	@Override
	public boolean isPolyExpression(MethodBinding candidate) {
		return true;
	}
	@Override
	public boolean isPolyExpression() {
		return true; // always as per introduction of part D, JSR 335
	}

	@Override
	public boolean isFunctionalType() {
		return true;
	}
	
	@Override
	public boolean isPertinentToApplicability(TypeBinding targetType, MethodBinding method) {
		if (targetType instanceof TypeVariableBinding) {
			TypeVariableBinding typeVariable = (TypeVariableBinding) targetType;
			if (method != null) { // when called from type inference
				if (typeVariable.declaringElement == method)
					return false;
				if (method.isConstructor() && typeVariable.declaringElement == method.declaringClass)
					return false;
			} else { // for internal calls
				if (typeVariable.declaringElement instanceof MethodBinding)
					return false;
			}
		}
		return true;
	}

	@Override
	public TypeBinding invocationTargetType() {
		if (this.expectedType == null) return null;
		// when during inference this expression mimics as an invocationSite,
		// we simulate an *invocation* of this functional expression,
		// where the expected type of the expression is the return type of the sam:
		MethodBinding sam = this.expectedType.getSingleAbstractMethod(this.enclosingScope, true);
		if (sam != null && sam.problemId() != ProblemReasons.NoSuchSingleAbstractMethod) {
			if (sam.isConstructor())
				return sam.declaringClass;
			else
				return sam.returnType;
		}
		return null;
	}

	@Override
	public TypeBinding expectedType() {
		return this.expectedType;
	}
	
	public boolean argumentsTypeElided() { return true; /* only exception: lambda with explicit argument types. */ }

	// Notify the compilation unit that it contains some functional types, taking care not to add any transient copies. this is assumed not to be a copy
	public int recordFunctionalType(Scope scope) {
		while (scope != null) {
			switch (scope.kind) {
				case Scope.METHOD_SCOPE :
					ReferenceContext context = ((MethodScope) scope).referenceContext;
					if (context instanceof LambdaExpression) {
						LambdaExpression expression = (LambdaExpression) context;
						if (expression != expression.original) // fake universe.
							return 0;
					}
					break; 
				case Scope.COMPILATION_UNIT_SCOPE :
					CompilationUnitDeclaration unit = ((CompilationUnitScope) scope).referenceContext;
					return unit.record(this);
			}
			scope = scope.parent;
		}
		return 0; // not reached.
	}

	@Override
	public TypeBinding resolveType(BlockScope blockScope) {
		return resolveType(blockScope, false);
	}

	public TypeBinding resolveType(BlockScope blockScope, boolean skipKosherCheck) {
		this.constant = Constant.NotAConstant;
		this.enclosingScope = blockScope;
		MethodBinding sam = this.expectedType == null ? null : this.expectedType.getSingleAbstractMethod(blockScope, argumentsTypeElided());
		if (sam == null) {
			blockScope.problemReporter().targetTypeIsNotAFunctionalInterface(this);
			return null;
		}
		if (!sam.isValidBinding() && sam.problemId() != ProblemReasons.ContradictoryNullAnnotations) {
			return reportSamProblem(blockScope, sam);
		}
		
		this.descriptor = sam;
		if (skipKosherCheck || kosherDescriptor(blockScope, sam, true)) {
			if (this.expectedType instanceof IntersectionTypeBinding18) {
				ReferenceBinding[] intersectingTypes =  ((IntersectionTypeBinding18)this.expectedType).intersectingTypes;
				for (int t = 0, max = intersectingTypes.length; t < max; t++) {
					if (intersectingTypes[t].findSuperTypeOriginatingFrom(TypeIds.T_JavaIoSerializable, false /*Serializable is not a class*/) != null) {
						this.isSerializable = true;
						break;
					}
				}
			} else if (this.expectedType.findSuperTypeOriginatingFrom(TypeIds.T_JavaIoSerializable, false /*Serializable is not a class*/) != null) {
				this.isSerializable = true;
			}
			LookupEnvironment environment = blockScope.environment();
			if (environment.globalOptions.isAnnotationBasedNullAnalysisEnabled) {
				NullAnnotationMatching.checkForContradictions(sam, this, blockScope);
			}
			return this.resolvedType = this.expectedType;		
		}
		
		return this.resolvedType = null;
	}

	protected TypeBinding reportSamProblem(BlockScope blockScope, MethodBinding sam) {
		if (this.hasReportedSamProblem)
			return null;
		switch (sam.problemId()) {
			case ProblemReasons.NoSuchSingleAbstractMethod:
				blockScope.problemReporter().targetTypeIsNotAFunctionalInterface(this);
				this.hasReportedSamProblem = true;
				break;
			case ProblemReasons.NotAWellFormedParameterizedType:
				blockScope.problemReporter().illFormedParameterizationOfFunctionalInterface(this);
				this.hasReportedSamProblem = true;
				break;
		}
		return null;
	}

	class VisibilityInspector extends TypeBindingVisitor {

		private Scope scope;
		private boolean shouldChatter;
        private boolean visible = true;
		private FunctionalExpression expression;
        
		public VisibilityInspector(FunctionalExpression expression, Scope scope, boolean shouldChatter) {
			this.scope = scope;
			this.shouldChatter = shouldChatter;
			this.expression = expression;
		}

		private void checkVisibility(ReferenceBinding referenceBinding) {
			if (!referenceBinding.canBeSeenBy(this.scope)) {
				this.visible = false;
				if (this.shouldChatter)
					this.scope.problemReporter().descriptorHasInvisibleType(this.expression, referenceBinding);
			}
		}
		
		@Override
		public boolean visit(ReferenceBinding referenceBinding) {
			checkVisibility(referenceBinding);
			return true;
		}

		
		@Override
		public boolean visit(ParameterizedTypeBinding parameterizedTypeBinding) {
			checkVisibility(parameterizedTypeBinding);
			return true;
		}
		
		@Override
		public boolean visit(RawTypeBinding rawTypeBinding) {
			checkVisibility(rawTypeBinding);
			return true;
		}

		public boolean visible(TypeBinding type) {
			TypeBindingVisitor.visit(this, type);
			return this.visible;
		}

		public boolean visible(TypeBinding[] types) {
			TypeBindingVisitor.visit(this, types);
			return this.visible;
		}
		
	}

	public boolean kosherDescriptor(Scope scope, MethodBinding sam, boolean shouldChatter) {
	
		VisibilityInspector inspector = new VisibilityInspector(this, scope, shouldChatter);
		
		boolean status = true;
		if (!inspector.visible(sam.returnType))
			status = false;
		if (!inspector.visible(sam.parameters))
			status = false;
		if (!inspector.visible(sam.thrownExceptions))
			status = false;
		if (!inspector.visible(this.expectedType))
			status = false;
		return status;
	}

	public int nullStatus(FlowInfo flowInfo) {
		return FlowInfo.NON_NULL;
	}

	public int diagnosticsSourceEnd() {
		return this.sourceEnd;
	}

	public MethodBinding[] getRequiredBridges() {

		class BridgeCollector {
			
			MethodBinding [] bridges;
			MethodBinding method;
			char [] selector;
			LookupEnvironment environment;
			Scope scope;

			BridgeCollector(ReferenceBinding functionalType, MethodBinding method) {
				this.method = method;
				this.selector = method.selector;
				this.environment = FunctionalExpression.this.enclosingScope.environment();
				this.scope = FunctionalExpression.this.enclosingScope;
				collectBridges(new ReferenceBinding[]{functionalType});
			}
			
			void collectBridges(ReferenceBinding[] interfaces) {
				int length = interfaces == null ? 0 : interfaces.length;
				for (int i = 0; i < length; i++) {
					ReferenceBinding superInterface = interfaces[i];
					if (superInterface == null) 
						continue;
					MethodBinding [] methods = superInterface.getMethods(this.selector);
					for (int j = 0, count = methods == null ? 0 : methods.length; j < count; j++) {
						MethodBinding inheritedMethod = methods[j];
						if (inheritedMethod == null || this.method == inheritedMethod)  // descriptor declaring class may not be same functional interface target type.
							continue;
						if (inheritedMethod.isStatic() || inheritedMethod.redeclaresPublicObjectMethod(this.scope)) 
							continue;
						inheritedMethod = MethodVerifier.computeSubstituteMethod(inheritedMethod, this.method, this.environment);
						if (inheritedMethod == null || !MethodVerifier.isSubstituteParameterSubsignature(this.method, inheritedMethod, this.environment) ||
								   !MethodVerifier.areReturnTypesCompatible(this.method, inheritedMethod, this.environment))
							continue;
						final MethodBinding originalInherited = inheritedMethod.original();
						final MethodBinding originalOverride = this.method.original();
						if (!originalOverride.areParameterErasuresEqual(originalInherited) || TypeBinding.notEquals(originalOverride.returnType.erasure(), originalInherited.returnType.erasure()))
							add(originalInherited);
					}
					collectBridges(superInterface.superInterfaces());
				}
			}
			void add(MethodBinding inheritedMethod) {
				if (this.bridges == null) {
					this.bridges = new MethodBinding[] { inheritedMethod };
					return;
				}
				int length = this.bridges.length;
				for (int i = 0; i < length; i++) {
					if (this.bridges[i].areParameterErasuresEqual(inheritedMethod) && TypeBinding.equalsEquals(this.bridges[i].returnType.erasure(), inheritedMethod.returnType.erasure()))
						return;
				}
				System.arraycopy(this.bridges, 0, this.bridges = new MethodBinding[length + 1], 0, length);
				this.bridges[length] = inheritedMethod;
			}
			MethodBinding [] getBridges () {
				return this.bridges;
			}
		}
		
		ReferenceBinding functionalType;
		if (this.expectedType instanceof IntersectionTypeBinding18) {
			functionalType = (ReferenceBinding) ((IntersectionTypeBinding18)this.expectedType).getSAMType(this.enclosingScope);
		} else {
			functionalType = (ReferenceBinding) this.expectedType;
		}
		return new BridgeCollector(functionalType, this.descriptor).getBridges();
	}
	boolean requiresBridges() {
		return getRequiredBridges() != null; 
	}
	public void cleanUp() {
		// to be overridden by sub-classes
	}
}

Back to the top