blob: a7b0045b3b8d863ce9c2ee1ef3392de41447b69e [file] [log] [blame]
/**********************************************************************
* This file is part of "Object Teams Development Tooling"-Software
*
* Copyright 2004, 2010 Fraunhofer Gesellschaft, Munich, Germany,
* for its Fraunhofer Institute for Computer Architecture and Software
* Technology (FIRST), Berlin, Germany and Technical University Berlin,
* Germany.
*
* 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
*
* Please visit http://www.eclipse.org/objectteams for updates and contact.
*
* Contributors:
* Fraunhofer FIRST - Initial API and implementation
* Technical University Berlin - Initial API and implementation
**********************************************************************/
package org.eclipse.objectteams.otdt.internal.core.compiler.ast;
import static org.eclipse.objectteams.otdt.core.compiler.IOTConstants.CALLIN_FLAG_DEFINITELY_MISSING_BASECALL;
import static org.eclipse.objectteams.otdt.core.compiler.IOTConstants.CALLIN_FLAG_POTENTIALLY_MISSING_BASECALL;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.SuperReference;
import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
import org.eclipse.jdt.internal.compiler.flow.FlowContext;
import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
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.objectteams.otdt.core.exceptions.InternalCompilerError;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.MethodModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstGenerator;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.TSuperHelper;
/**
* NEW for OTDT.
*
* A messages send using tsuper has to perform additional checks,
* because it is legal only if enclosing method and invoked method have
* the same signature (except for the tsuper-mark argument).
*
* What: add appropriate marker argument
* How: before actual resolve, resolve the (possibly qualified) tsuper reference
* and determine from it the appropriate marker interface.
*
* @author stephan
* @version $Id: TSuperMessageSend.java 23401 2010-02-02 23:56:05Z stephan $
*/
public class TSuperMessageSend extends MessageSend {
public TsuperReference tsuperReference;
/** Check whether this message send contributes to base call analysis */
@Override
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
flowInfo = super.analyseCode(currentScope, flowContext, flowInfo);
if (this.binding.isCallin())
{
MethodBinding tsuperMethod = this.binding;
if (tsuperMethod.copyInheritanceSrc != null)
tsuperMethod = tsuperMethod.copyInheritanceSrc;
if (!MethodModel.hasCallinFlag(tsuperMethod, CALLIN_FLAG_DEFINITELY_MISSING_BASECALL)) { // no contribution if no base call.
MethodDeclaration callinMethod = (MethodDeclaration)currentScope.methodScope().referenceContext;
LocalVariableBinding trackingVariable = callinMethod.baseCallTrackingVariable.binding;
if (MethodModel.hasCallinFlag(tsuperMethod, CALLIN_FLAG_POTENTIALLY_MISSING_BASECALL)) {
if ( flowInfo.isDefinitelyAssigned(trackingVariable)
|| flowInfo.isPotentiallyAssigned(trackingVariable))
{
currentScope.problemReporter().potentiallyDuplicateBasecall(this);
} else {
FlowInfo potential = flowInfo.copy();
potential.markAsDefinitelyAssigned(trackingVariable);
flowInfo = FlowInfo.conditional(flowInfo.initsWhenTrue(), potential.initsWhenTrue());
}
} else { // tsuper definitely has a base call:
if (flowInfo.isDefinitelyAssigned(trackingVariable))
currentScope.problemReporter().definitelyDuplicateBasecall(this);
else if (flowInfo.isPotentiallyAssigned(trackingVariable))
currentScope.problemReporter().potentiallyDuplicateBasecall(this);
if (!flowInfo.isDefinitelyAssigned(trackingVariable))
flowInfo.markAsDefinitelyAssigned(trackingVariable);
}
}
}
return flowInfo;
}
@Override
protected TypeBinding findMethodBinding(BlockScope scope) {
// check: is a tsuper call legal in the current context?
AbstractMethodDeclaration context = scope.methodScope().referenceMethod();
if (context == null
|| !CharOperation.equals(this.selector, context.selector)
|| context.binding.parameters.length != this.argumentTypes.length)
{
scope.problemReporter().tsuperCallsWrongMethod(this);
return null;
}
ReferenceBinding receiverRole;
if (!(this.actualReceiverType instanceof ReferenceBinding)
|| !(receiverRole = (ReferenceBinding)this.actualReceiverType).isSourceRole())
{
scope.problemReporter().tsuperOutsideRole(context, this, this.actualReceiverType);
return null;
}
ReferenceBinding[] tsuperRoleBindings = receiverRole.roleModel.getTSuperRoleBindings();
if (tsuperRoleBindings.length == 0) {
scope.problemReporter().tsuperCallWithoutTsuperRole(receiverRole, this);
return null;
}
// context is OK, start searching:
this.tsuperReference.resolveType(scope);
// qualified tsuper? => directly search within the designated tsuper role:
if (this.tsuperReference.qualification != null) {
TypeBinding tsuperRole = this.tsuperReference.resolvedType;
if (tsuperRole == null || !tsuperRole.isRole())
return null;
this.binding = scope.getMethod(tsuperRole, this.selector, this.argumentTypes, this);
if (!this.binding.isValidBinding() && ((ProblemMethodBinding)this.binding).declaringClass == null)
this.binding.declaringClass = (ReferenceBinding) tsuperRole;
resolvePolyExpressionArguments(this, this.binding, this.argumentTypes, scope);
return this.binding.returnType;
}
// no qualification => search all tsupers by priority:
MethodBinding bestMatch = null;
for (int i=tsuperRoleBindings.length-1; i>=0; i--) {
ReferenceBinding tsuperRole = tsuperRoleBindings[i];
MethodBinding candidate = scope.getMethod(tsuperRole, this.selector, this.argumentTypes, this);
if (candidate.isValidBinding()) {
if (scope.parameterCompatibilityLevel(candidate, this.argumentTypes) != Scope.COMPATIBLE) {
scope.problemReporter().tsuperCallsWrongMethod(this);
return candidate.returnType;
}
this.binding = candidate;
resolvePolyExpressionArguments(this, this.binding, this.argumentTypes, scope);
return this.binding.returnType;
}
if (bestMatch == null ||
(bestMatch.problemId() == ProblemReasons.NotFound && candidate.problemId() != ProblemReasons.NotFound))
bestMatch = candidate;
}
if (bestMatch == null)
bestMatch = new ProblemMethodBinding(this.selector, this.argumentTypes, ProblemReasons.NotFound);
if (bestMatch.declaringClass == null)
bestMatch.declaringClass = (ReferenceBinding) this.tsuperReference.resolvedType;
this.binding = bestMatch;
return this.binding.returnType;
}
@Override
public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired)
{
// for code gen we need to add the marker arg...
int len = this.binding.parameters.length;
TypeBinding[] extendedParameters = new TypeBinding[len+1];
System.arraycopy(this.binding.parameters, 0, extendedParameters, 0, len);
char[] tSuperMarkName = TSuperHelper.getTSuperMarkName(this.tsuperReference.resolvedType.enclosingType());
extendedParameters[len] = currentScope.getType(tSuperMarkName);
// ... and find the copied method binding
MethodBinding codegenBinding = currentScope.getMethod(this.actualReceiverType, this.selector, extendedParameters, this);
if (codegenBinding.problemId() == ProblemReasons.NotFound) {
// tsuper.m() may in fact refer to tsuper.super.m().
// try to find the method as super.tsuper() instead:
ReferenceBinding superRole = ((ReferenceBinding)this.receiver.resolvedType).superclass();
codegenBinding = getAlternateMethod(currentScope, superRole, extendedParameters);
if (codegenBinding == null)
codegenBinding = getAlternateMethod(currentScope, superRole, this.binding.parameters);
if (codegenBinding == null)
throw new InternalCompilerError("cannot find real method binding for tsuper call!"); //$NON-NLS-1$
this.receiver = new SuperReference(this.receiver.sourceStart, this.receiver.sourceEnd);
this.receiver.resolvedType = superRole;
this.receiver.constant = Constant.NotAConstant;
this.actualReceiverType = superRole;
}
MethodBinding tsuperMethod = this.binding;
this.binding = codegenBinding;
try {
super.generateCode(currentScope, codeStream, valueRequired);
} finally {
this.binding = tsuperMethod;
}
if (valueRequired && this.binding.isCallin()) {
if (this.resolvedType != null && this.resolvedType.isValidBinding()) {
if (this.resolvedType.isBaseType()) {
// something like: ((Integer)result).intValue()
char[][] boxtypeName= AstGenerator.boxTypeName((BaseTypeBinding)this.resolvedType);
codeStream.checkcast(currentScope.getType(boxtypeName, 3));
codeStream.generateUnboxingConversion(this.resolvedType.id);
} else {
// (RefType)result
codeStream.checkcast(this.resolvedType);
}
}
}
}
@SuppressWarnings("hiding")
@Override
public void generateArguments(MethodBinding binding, Expression[] arguments, BlockScope currentScope, CodeStream codeStream)
{
super.generateArguments(binding, arguments, currentScope, codeStream);
// check if we need to pass the marker arg, too:
TypeBinding[] parameters = this.binding.parameters;
TypeBinding tsuperMarkerBinding = parameters.length == 0 ? null : parameters[parameters.length-1];
if (tsuperMarkerBinding == null || !TSuperHelper.isMarkerInterface(tsuperMarkerBinding))
return;
codeStream.aconst_null();
codeStream.checkcast(tsuperMarkerBinding);
}
private MethodBinding getAlternateMethod(Scope scope, ReferenceBinding superRole, TypeBinding[] extendedArgumentTypes)
{
MethodBinding alternateMethod = scope.getMethod(superRole, this.selector, extendedArgumentTypes, this);
if (alternateMethod.problemId() == ProblemReasons.NotVisible) {
return alternateMethod; // want to see this error as IProblem.IndirectTSuperInvisible, cf. ProblemReporter.invalidMethod
}
MethodBinding alternateSrc = alternateMethod.copyInheritanceSrc;
// TODO(SH): binary verbatim copies (no marker arg) are not recognized as copies!
if ( alternateSrc != null
&& isRoleOfSuperTeam(alternateSrc.declaringClass, scope))
return alternateMethod;
return null;
}
@Override
protected boolean isAnySuperAccess() {
return true;
}
private boolean isRoleOfSuperTeam(ReferenceBinding roleClass, Scope scope) {
ReferenceBinding site = scope.enclosingSourceType();
if (!site.isRole())
return false;
return site.enclosingType().superclass().isCompatibleWith(roleClass.enclosingType());
}
@Override
public TypeBinding resolveType(BlockScope scope) {
TypeBinding answer = super.resolveType(scope);
if (this.binding != null && this.binding.isCallin())
// restore return type which has been generalized to 'Object':
return this.resolvedType= MethodModel.getReturnType(this.binding);
return answer;
}
@Override
public StringBuffer printExpression(int indent, StringBuffer output){
if (this.tsuperReference != null && this.tsuperReference.qualification != null) {
this.tsuperReference.qualification.printExpression(indent, output);
output.append("."); //$NON-NLS-1$
}
output.append("tsuper."); //$NON-NLS-1$
return super.printExpression(indent, output);
}
}