| /******************************************************************************* |
| * Copyright (c) 2013, 2015 GK Software AG. |
| * |
| * 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: |
| * Stephan Herrmann - initial API and implementation |
| * Lars Vogel <Lars.Vogel@vogella.com> - Contributions for |
| * Bug 473178 |
| *******************************************************************************/ |
| package org.aspectj.org.eclipse.jdt.internal.compiler.lookup; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.aspectj.org.eclipse.jdt.internal.compiler.ast.FunctionalExpression; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.ast.LambdaExpression; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.ast.ReferenceExpression; |
| |
| /** |
| * Constraint formula expressing that a given expression must have an exception type. |
| * <ul> |
| * <li>Expression contains<sub>throws</sub> T</li> |
| * </ul> |
| */ |
| public class ConstraintExceptionFormula extends ConstraintFormula { |
| |
| FunctionalExpression left; |
| |
| public ConstraintExceptionFormula(FunctionalExpression left, TypeBinding type) { |
| this.left = left; |
| this.right = type; |
| this.relation = EXCEPTIONS_CONTAINED; |
| } |
| |
| @Override |
| public Object reduce(InferenceContext18 inferenceContext) { |
| // JLS 18.2.5 |
| Scope scope = inferenceContext.scope; |
| if (!this.right.isFunctionalInterface(scope)) |
| return FALSE; |
| MethodBinding sam = this.right.getSingleAbstractMethod(scope, true); |
| if (sam == null) |
| return FALSE; |
| if (this.left instanceof LambdaExpression) { |
| if (((LambdaExpression)this.left).argumentsTypeElided()) { |
| int nParam = sam.parameters.length; |
| for (int i = 0; i < nParam; i++) |
| if (!sam.parameters[i].isProperType(true)) |
| return FALSE; |
| } |
| if (sam.returnType != TypeBinding.VOID && !sam.returnType.isProperType(true)) |
| return FALSE; |
| } else { // reference expression |
| if (!((ReferenceExpression)this.left).isExactMethodReference()) { |
| int nParam = sam.parameters.length; |
| for (int i = 0; i < nParam; i++) |
| if (!sam.parameters[i].isProperType(true)) |
| return FALSE; |
| if (sam.returnType != TypeBinding.VOID && !sam.returnType.isProperType(true)) |
| return FALSE; |
| } |
| } |
| TypeBinding[] thrown = sam.thrownExceptions; |
| InferenceVariable[] e = new InferenceVariable[thrown.length]; |
| int n = 0; |
| for (int i = 0; i < thrown.length; i++) |
| if (!thrown[i].isProperType(true)) |
| e[n++] = (InferenceVariable) thrown[i]; // thrown[i] is not a proper type, since it's an exception it must be an inferenceVariable, right? |
| |
| /* If throw specification does not encode any type parameters, there are no constraints to be gleaned/gathered from the throw sites. |
| See also that thrown exceptions are not allowed to influence compatibility and overload resolution. |
| */ |
| if (n == 0) |
| return TRUE; |
| |
| TypeBinding[] ePrime = null; |
| if (this.left instanceof LambdaExpression) { |
| LambdaExpression lambda = ((LambdaExpression) this.left).resolveExpressionExpecting(this.right, inferenceContext.scope, inferenceContext); |
| if (lambda == null) |
| return TRUE; // cannot make use of this buggy constraint |
| Set<TypeBinding> ePrimeSet = lambda.getThrownExceptions(); |
| ePrime = ePrimeSet.toArray(new TypeBinding[ePrimeSet.size()]); |
| } else { |
| ReferenceExpression referenceExpression = ((ReferenceExpression) this.left).resolveExpressionExpecting(this.right, scope, inferenceContext); |
| MethodBinding method = referenceExpression != null ? referenceExpression.binding : null; |
| if (method != null) |
| ePrime = method.thrownExceptions; |
| } |
| if (ePrime == null) |
| return TRUE; |
| int m = ePrime.length; |
| List<ConstraintFormula> result = new ArrayList<>(); |
| actual: for (int i = 0; i < m; i++) { |
| if (ePrime[i].isUncheckedException(false)) |
| continue; |
| for (int j = 0; j < thrown.length; j++) |
| if (thrown[j].isProperType(true) && ePrime[i].isCompatibleWith(thrown[j])) |
| continue actual; |
| for (int j = 0; j < n; j++) |
| result.add(ConstraintTypeFormula.create(ePrime[i], e[j], SUBTYPE)); |
| } |
| for (int j = 0; j < n; j++) |
| inferenceContext.currentBounds.inThrows.add(e[j].prototype()); |
| return result.toArray(new ConstraintFormula[result.size()]); |
| } |
| |
| @Override |
| Collection<InferenceVariable> inputVariables(final InferenceContext18 context) { |
| // from 18.5.2. |
| if (this.left instanceof LambdaExpression) { |
| if (this.right instanceof InferenceVariable) { |
| return Collections.singletonList((InferenceVariable)this.right); |
| } |
| if (this.right.isFunctionalInterface(context.scope)) { |
| LambdaExpression lambda = (LambdaExpression) this.left; |
| MethodBinding sam = this.right.getSingleAbstractMethod(context.scope, true); // TODO derive with target type? |
| final Set<InferenceVariable> variables = new HashSet<>(); |
| if (lambda.argumentsTypeElided()) { |
| // i) |
| int len = sam.parameters.length; |
| for (int i = 0; i < len; i++) { |
| sam.parameters[i].collectInferenceVariables(variables); |
| } |
| } |
| if (sam.returnType != TypeBinding.VOID) { |
| // ii) |
| sam.returnType.collectInferenceVariables(variables); |
| } |
| return variables; |
| } |
| } else if (this.left instanceof ReferenceExpression) { |
| if (this.right instanceof InferenceVariable) { |
| return Collections.singletonList((InferenceVariable)this.right); |
| } |
| if (this.right.isFunctionalInterface(context.scope)) { // TODO: && this.left is inexact |
| MethodBinding sam = this.right.getSingleAbstractMethod(context.scope, true); // TODO derive with target type? |
| final Set<InferenceVariable> variables = new HashSet<>(); |
| int len = sam.parameters.length; |
| for (int i = 0; i < len; i++) { |
| sam.parameters[i].collectInferenceVariables(variables); |
| } |
| sam.returnType.collectInferenceVariables(variables); |
| return variables; |
| } |
| } |
| return EMPTY_VARIABLE_LIST; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuffer buf = new StringBuffer().append(LEFT_ANGLE_BRACKET); |
| this.left.printExpression(4, buf); |
| buf.append(" \u2286throws "); //$NON-NLS-1$ |
| appendTypeName(buf, this.right); |
| buf.append(RIGHT_ANGLE_BRACKET); |
| return buf.toString(); |
| } |
| } |