blob: 4d9e981363a25d332b3c30dc9710c538605beced [file] [log] [blame]
/**********************************************************************
* This file is part of "Object Teams Development Tooling"-Software
*
* Copyright 2003, 2015 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
* $Id: TransformStatementsVisitor.java 23416 2010-02-03 19:59:31Z stephan $
*
* 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.statemachine.transformer;
import java.util.Stack;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ArrayAllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall;
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.NullLiteral;
import org.eclipse.jdt.internal.compiler.ast.QualifiedThisReference;
import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.ThisReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions.WeavingScheme;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.objectteams.otdt.core.compiler.IOTConstants;
import org.eclipse.objectteams.otdt.core.exceptions.InternalCompilerError;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.BaseCallMessageSend;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.BaseReference;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.ITranslationStates;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.StateHelper;
import org.eclipse.objectteams.otdt.internal.core.compiler.mappings.CallinImplementorDyn;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.MethodModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.smap.SourcePosition;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstGenerator;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.TSuperHelper;
/**
* This visitor performs several replacements within method bodies etc.
* None of these replacements require resolved types or other non-local information.
*
* (1) Each tsuper(...) call -> this(..., markerArg)
* Works for constructor calls only.
* (2) Adjust according to signature enhancing of callin methods:
* - 'recursive' calls need more arguments
* - return expressions need to be generalized wrt their type.
*
* Linking local types to role models is now in RecordLocalTypesVisitor -
* WHY: recording local types MUST happen in any case, while TransformStatementsVisitor
* refuses to operate on types with ignoreFurtherInvestigation (see TypeDeclaration.traverse()).
*
* @author stephan
* @version $Id: TransformStatementsVisitor.java 23416 2010-02-03 19:59:31Z stephan $
*/
public class TransformStatementsVisitor
extends StackTransformStatementsVisitor
implements IOTConstants
{
// -- fields and methods for scope management ---
private Stack<MethodDeclaration> _methodDeclarationStack = new Stack<MethodDeclaration>();
private boolean isLocalTypeInCallin = false;
private WeavingScheme weavingScheme;
public TransformStatementsVisitor(WeavingScheme weavingScheme) {
this.weavingScheme = weavingScheme;
}
/**
* If methodDeclaration is a callin method remember it for translating base calls.
* Only remember methodDeclarations with ignoreFurtherInvestigation==false to
* avoid NPEs and spurious error messages.
*
* @param methodDeclaration
* @param localInCallin if the method is the enclosing (callin?) method of a local type
*/
public void checkPushCallinMethod(AbstractMethodDeclaration methodDeclaration, boolean localInCallin) {
if ( isGoodCallin(methodDeclaration))
{
this._methodDeclarationStack.push((MethodDeclaration)methodDeclaration);
this.isLocalTypeInCallin = localInCallin;
}
}
private boolean isGoodCallin(AbstractMethodDeclaration methodDeclaration) {
return methodDeclaration != null
&& !methodDeclaration.ignoreFurtherInvestigation
&& methodDeclaration.isCallin()
&& methodDeclaration.binding != null
&& methodDeclaration.binding.isCallin(); // binding's callin flag was reset if callin in illegal context
}
protected boolean checkPopCallinMethod(AbstractMethodDeclaration currentMethod) {
if ( !this._methodDeclarationStack.isEmpty()
&& this._methodDeclarationStack.peek() == currentMethod)
{
assert currentMethod.isCallin();
this._methodDeclarationStack.pop();
return true;
}
return false;
}
// visit member type
@Override
public boolean visit (TypeDeclaration type, ClassScope scope)
{
if (type.isTeam())
if (StateHelper.hasState(type.binding, ITranslationStates.STATE_STATEMENTS_TRANSFORMED))
return false; // don't descend again
return true;
}
/**
* Within a tsuper constructor call -> add marker argument
*/
@Override
public boolean visit (ExplicitConstructorCall call, BlockScope scope)
{
if (call.isTsuperAccess())
{
call.arguments = TSuperHelper.addMarkerArgument(
null/*qualification*/, call, call.arguments, scope);
}
return true;
}
// === Adjustments following enhancement of callin method signatures:
@Override
public boolean visit(BaseCallMessageSend messageSend, BlockScope scope) {
if (!this._methodDeclarationStack.isEmpty())
messageSend.prepareSuperAccess(this.weavingScheme, this._methodDeclarationStack.peek(), scope);
messageSend.bits |= ASTNode.HasBeenTransformed; // only the outer has been transformed so far.
return true;
}
/** May need to add arguments to a 'recursive' callin message send. */
@Override
public boolean visit(MessageSend messageSend, BlockScope scope) {
// scope is not reliable at this point due to unset scopes of block statements like "for"
if (this._methodDeclarationStack.isEmpty())
return true;
MethodDeclaration methodDecl = this._methodDeclarationStack.peek();
boolean isBaseCall = messageSend.receiver instanceof BaseReference;
if ( methodDecl.isCallin()
&& isRecursiveCall(methodDecl, messageSend, isBaseCall))
{
// argument enhancing within callin methods:
Expression[] args = messageSend.arguments;
if (isBaseCall) {
switch (this.weavingScheme) {
case OTDRE:
break;
case OTRE:
if (args != null) {
int len = args.length;
if (methodDecl.isStatic()) // chop of premature isSuperAccess flag:
System.arraycopy(args, 1, args=new Expression[len-1], 0, len-1);
}
}
}
messageSend.arguments = MethodSignatureEnhancer.enhanceArguments(args, messageSend.sourceEnd+1, this.weavingScheme);
messageSend.bits |= ASTNode.HasBeenTransformed; // mark only when args have really been enhanced
}
return true;
}
/* these are considered as recursive calls: base calls, this/super/tsuper calls. */
private boolean isRecursiveCall(MethodDeclaration callinMethod, MessageSend messageSend, boolean isBaseCall) {
if (!(messageSend.receiver instanceof ThisReference))
return false;
if (messageSend.receiver instanceof QualifiedThisReference)
return false;
if (!CharOperation.equals(callinMethod.selector, messageSend.selector) && !CharOperation.equals(CallinImplementorDyn.OT_CALL_NEXT, messageSend.selector))
return false;
if (callinMethod.arguments == null)
return false;
int sendArgs = messageSend.arguments == null ? 0 : messageSend.arguments.length;
if (this.weavingScheme == WeavingScheme.OTDRE) {
// is already packed in BCMS.prepareSuperAccess(), fetch number of arguments from the packed array in pos [0]
if (sendArgs > 0) {
Expression firstArg = messageSend.arguments[0];
if (firstArg instanceof NullLiteral)
sendArgs = 0;
else if (firstArg instanceof ArrayAllocationExpression)
sendArgs = ((ArrayAllocationExpression) firstArg).initializer.expressions.length;
}
}
sendArgs += MethodSignatureEnhancer.getEnhancingArgLen(this.weavingScheme);
if (isBaseCall && this.weavingScheme == WeavingScheme.OTRE)
sendArgs--; // don't count the isSuperAccess flag
return sendArgs == callinMethod.arguments.length;
}
/** May need to 'generalize' a return expression. */
@Override
public boolean visit(ReturnStatement returnStatement, BlockScope scope) {
if (this._methodDeclarationStack.isEmpty() || this.isLocalTypeInCallin)
return true;
MethodDeclaration methodDecl = this._methodDeclarationStack.peek();
if (!isGoodCallin(methodDecl))
return true;
if (scope != null && scope.methodScope() != methodDecl.scope)
return true; // method in a nested type, not the callin itself
TypeBinding returnType = MethodModel.getReturnType(methodDecl.binding);
if (!returnType.isBaseType())
return true;
AstGenerator gen = new AstGenerator(returnStatement.sourceStart, returnStatement.sourceEnd);
if (returnType == TypeBinding.VOID) {
if (returnStatement.expression != null) {
if (scope == null && !this._methodDeclarationStack.isEmpty())
scope = this._methodDeclarationStack.peek().scope;
if (scope != null)
scope.problemReporter().attemptToReturnNonVoidExpression(returnStatement, returnType);
else
throw new InternalCompilerError("Missing scope for error reporting"); //$NON-NLS-1$
} else {
// return stored value:
returnStatement.expression = gen.singleNameReference(OT_RESULT);
}
}
return true;
}
@Override
public boolean visit(MethodDeclaration methodDeclaration, ClassScope scope) {
methodDeclaration.bits |= ASTNode.HasBeenTransformed;
checkPushCallinMethod(methodDeclaration, false);
return true;
}
@Override
public void endVisit(MethodDeclaration methodDecl, ClassScope scope) {
if (checkPopCallinMethod(methodDecl)) {
// assume method had no errors (else it would not have been pushed)
TypeBinding returnType = MethodModel.getReturnType(methodDecl.binding);
// return type was already adjusted by SourceTypeBinding.resolveTypesFor()->MethodSignatureEnhance.generalizeReturnType()
if ( returnType == TypeBinding.VOID
&& !methodDecl.isGenerated
&& !methodDecl.isCopied
&& !methodDecl.isAbstract())
{
AstGenerator gen = new AstGenerator(methodDecl.bodyEnd, methodDecl.bodyEnd);
if (methodDecl.statements == null) {
methodDecl.setStatements(new Statement[] {
gen.returnStatement(gen.nullLiteral(), true/*synthetic*/)
});
} else {
// bracket body with:
// Object _OT$result = null;
// ... ( meanwhile a BaseCallMessageSend might assign to _OT$result )
// return _OT$result;
int len = methodDecl.statements.length;
Statement[] newStatements = new Statement[len+2];
System.arraycopy(methodDecl.statements, 0, newStatements, 1, len);
//save source positions from AstGenerator (ike)
SourcePosition savePos = gen.getSourcePosition();
try {
//set to first line of this method (if any)
if (len > 0)
gen.setSourcePosition((((long)methodDecl.statements[0].sourceStart)<<32) + methodDecl.statements[0].sourceEnd);
//generate local variable (ike)
newStatements[0] = gen.localVariable(OT_RESULT, scope.getJavaLangObject(), gen.nullLiteral());
} finally {
//restore source postions (ike)
gen.setSourcePosition(savePos);
}
newStatements[len+1] = gen.returnStatement(gen.singleNameReference(OT_RESULT), true/*synthetic*/);
methodDecl.setStatements(newStatements);
}
}
}
super.endVisit(methodDecl, scope);
}
/**
* Cut traversal for types without a scope.
*/
@Override
public boolean visit(TypeDeclaration td, BlockScope scope) {
if((td.bits & ASTNode.IsLocalType)!=0)
if (td.scope == null)
return false; // don't descend further, local type is not in a useful state yet.
return true;
}
// Late transformation for callins within types with errors,
// required for resilience regarding base calls.
public static void checkTransformStatements(AbstractMethodDeclaration method)
{
ClassScope scope = method.scope.classScope();
if ( scope.referenceContext.ignoreFurtherInvestigation
&& method instanceof MethodDeclaration
&& method.isCallin()
&& (method.bits & ASTNode.HasBeenTransformed) == 0)
{
// but if class has errors the visitor bailed out.
// need to transform before changing the selector
method.traverse(new TransformStatementsVisitor(scope.compilerOptions().weavingScheme), scope);
}
}
}