blob: 28763b4a7e9fc2bdcaf23395273602193493add0 [file] [log] [blame]
/**********************************************************************
* This file is part of "Object Teams Development Tooling"-Software
*
* Copyright 2004, 2014 Fraunhofer Gesellschaft, Munich, Germany,
* for its Fraunhofer Institute for Computer Architecture and Software
* Technology (FIRST), Berlin, Germany and Technical University Berlin,
* Germany.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* 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 org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.CompilationResult.CheckPoint;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.CastExpression;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.NameReference;
import org.eclipse.jdt.internal.compiler.ast.QualifiedAllocationExpression.AbstractQualifiedAllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
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.Binding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.PolyTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
import org.eclipse.objectteams.otdt.core.exceptions.InternalCompilerError;
import org.eclipse.objectteams.otdt.internal.core.compiler.lifting.Lifting;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.RoleTypeBinding;
import org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.copyinheritance.CopyInheritance;
/**
* This class wraps a qualified allocation in order to defer the decision
* whether or not to translate it into a role creator call.
* This decision can only be made after the receiver (lhs of dot) is resolved.
*
* @author stephan
*/
public abstract class OTQualifiedAllocationExpression extends AbstractQualifiedAllocationExpression {
/** if this is set, we are using the translated version. */
private MessageSend creatorCall = null;
private Runnable preGenerateTask = null; // an optional task to be performed at the start of generateCode.
/**
* Wrap a given allocation expression.
*/
public OTQualifiedAllocationExpression(TypeDeclaration anonymousType) {
super(anonymousType);
}
public OTQualifiedAllocationExpression() {
super();
}
/** Simply dispatch. */
public FlowInfo analyseCode(
BlockScope currentScope,
FlowContext flowContext,
FlowInfo flowInfo)
{
if (this.creatorCall == null)
return super.analyseCode(currentScope, flowContext, flowInfo);
else
return this.creatorCall.analyseCode(currentScope, flowContext, flowInfo);
}
/** simply forward. */
public Expression enclosingInstance() {
return super.enclosingInstance();
}
/** Simply dispatch. */
public void generateCode(
BlockScope currentScope,
CodeStream codeStream,
boolean valueRequired)
{
if (this.preGenerateTask != null)
this.preGenerateTask.run(); // transfer a local variable's resolvedPosition just before it is used for generating code
if (this.creatorCall == null) {
super.generateCode(currentScope, codeStream, valueRequired);
} else {
this.creatorCall.generateCode(currentScope, codeStream, valueRequired);
}
}
/**
* During resolve we make the decision which variant to use.
*/
public TypeBinding resolveType(BlockScope scope)
{
if (this.anonymousType == null && this.creatorCall == null && this.enclosingInstance == null) // special case during code assist
return super.resolveType(scope);
CompilationResult compilationResult = scope.referenceContext().compilationResult();
CheckPoint cp = compilationResult.getCheckPoint(scope.referenceContext());
this.hasEnclosingInstanceProblem = false;
if (this.anonymousType == null && this.creatorCall == null) { // no double processing
if (this.enclosingInstance instanceof CastExpression)
this.enclosingInstance.bits |= DisableUnnecessaryCastCheck; // will check later on (within super.resolveType())
TypeBinding enclosingInstanceType = this.enclosingInstance.resolveType(scope);
this.hasEnclosingInstanceProblem = enclosingInstanceType == null;
if ( !scope.isGeneratedScope()
&& enclosingInstanceType != null
&& enclosingInstanceType.isTeam()) // non reference types will trigger error reporting via super.resolveType()
{
if (this.enclosingInstance instanceof NameReference) {
final NameReference anchorRef = (NameReference)this.enclosingInstance;
if (!((VariableBinding)anchorRef.binding).isFinal()) {
// replace non-final anchor with fake-binding,
// so that this type is not compatibly to anything else:
char[] variableName = ((VariableBinding)anchorRef.binding).name;
switch (anchorRef.bits & ASTNode.RestrictiveFlagMASK) {
case Binding.LOCAL:
final LocalVariableBinding localOrig = (LocalVariableBinding)anchorRef.binding;
// mark the original as used before we procede with a fake copy:
localOrig.useFlag = LocalVariableBinding.USED;
anchorRef.binding = new LocalVariableBinding(variableName, enclosingInstanceType, ClassFileConstants.AccFinal, false)
{
@Override public int problemId() { return IProblem.AnchorNotFinal; }
};
this.preGenerateTask = new Runnable() { public void run() {
// need to transfer this info from the real local to the fake one (don't have that info yet):
((LocalVariableBinding)anchorRef.binding).resolvedPosition = localOrig.resolvedPosition;
}};
break;
case Binding.FIELD:
anchorRef.binding = new FieldBinding(variableName, enclosingInstanceType, ClassFileConstants.AccFinal, scope.referenceType().binding, Constant.NotAConstant)
{
@Override public int problemId() { return IProblem.AnchorNotFinal; }
};
break;
default:
throw new InternalCompilerError("Unexpected bits, neither local nor field "+anchorRef.bits+": "+anchorRef); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
if (this.type.getTypeName().length > 1) {
scope.problemReporter().roleCreationNotRelativeToEnclosingTeam(this);
return null;
}
// now it's finally time to create the alternate version:
this.creatorCall = CopyInheritance.createConstructorMethodInvocationExpression(scope, this);
if (this.creatorCall == null)
return null;
}
}
if (this.creatorCall == null) {
TypeBinding typ = super.resolveType(scope);
if (typ == null || typ instanceof PolyTypeBinding)
return typ;
if (!this.hasEnclosingInstanceProblem) { // more checks only if no error already
// if enclosing is a role request a cast to the class part as required by the inner constructor
if (this.enclosingInstance != null) {
TypeBinding enclosingType = this.enclosingInstance.resolvedType;
if (enclosingType instanceof ReferenceBinding && ((ReferenceBinding)enclosingType).isDirectRole())
this.enclosingInstanceCast = ((ReferenceBinding)enclosingType).getRealClass();
}
ReferenceBinding superType = null;
if (this.resolvedType instanceof ReferenceBinding)
superType= ((ReferenceBinding)this.resolvedType).superclass();
if ( superType != null
&& (superType instanceof RoleTypeBinding))
{
RoleTypeBinding superRole = (RoleTypeBinding)superType;
if (superRole.hasExplicitAnchor())
scope.problemReporter().extendingExternalizedRole(superRole, this.type);
}
}
} else { // === with creatorCall ===
this.constant = Constant.NotAConstant;
this.resolvedType = this.creatorCall.resolveType(scope);
// when creating role nested instance, no cast of enclosing role needed in this branch,
// because creator call is routed via the interface of the enclosing role.
if (this.resolvedType != null) {
if (((ReferenceBinding)this.resolvedType).isAbstract())
{
if (!((ReferenceBinding)enclosingInstance().resolvedType).isAbstract())
scope.problemReporter().abstractRoleIsRelevant(this, (ReferenceBinding) this.creatorCall.resolvedType);
}
if (this.resolvedType.isValidBinding()) {
// FIXME(SH): remove cast unwrapping
Expression createExpr = this.creatorCall;
while (createExpr instanceof CastExpression) // may have been wrapped using CollectedReplacementsTransformer
createExpr = ((CastExpression)createExpr).expression;
this.binding = ((MessageSend)createExpr).binding; // store the method binding
// using lift-ctor in a qualified way? (OTJDL 2.4.1(a))
ReferenceBinding role = (ReferenceBinding)this.resolvedType;
MethodBinding creator = this.binding;
if (creator != null) {
MethodBinding ctor = role.getExactConstructor(creator.parameters);
if (Lifting.isLiftToConstructor(ctor, role))
scope.problemReporter().qualifiedUseOfLiftingConstructor(ctor, this.creatorCall);
}
}
}
}
return this.resolvedType;
}
/** Simply dispatch. */
public StringBuffer printExpression(int indent, StringBuffer output) {
if (this.creatorCall == null)
return super.printExpression(indent,output);
else
return this.creatorCall.printExpression(indent, output);
}
/** Simply dispatch. */
public void traverse(ASTVisitor visitor, BlockScope scope)
{
if (this.creatorCall == null)
super.traverse(visitor, scope);
else
this.creatorCall.traverse(visitor, scope);
}
public MethodBinding getMethodBinding() {
if (this.creatorCall != null)
return this.creatorCall.binding;
return this.binding;
}
}