blob: ebc0f8ad81abd40aa76afcf55d2532c9ff896343 [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.
*
* 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
* $Id: StandardElementGenerator.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 org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.*;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
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.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.MissingTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
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.bytecode.SingleValueAttribute;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.ITranslationStates;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.StateMemento;
import org.eclipse.objectteams.otdt.internal.core.compiler.lifting.TreeNode;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.DependentTypeBinding;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.RoleTypeBinding;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.TThisBinding;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.FieldModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.RoleModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.TeamModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.copyinheritance.CopyInheritance;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstEdit;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstGenerator;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.TypeAnalyzer;
import static org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants.*;
import static org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers.*;
import static org.eclipse.objectteams.otdt.core.compiler.IOTConstants.*;
/**
* This class inserts standard elements into role and team classes.
*
* For bound and unbound roles:
* Team _OT$getTeam();
* protected <role> _OT$castTo$<role> (Object _OT$arg1)
* protected <role>[].. _OT$castTo$<role>$<dims> (Object[].. _OT$arg1) // OPTIONALLY
*
* For bound roles only:
* <baseclass> _OT$base; // field
* <baseclass> _OT$getBase(); // method (abstract or implemented)
* "PlayedBy" Attribute (byte code)
*
* Some more elements have been (partly) handled here in earlier versions:
* role caches -> LiftingEnvironment
* role class literal -> RoleClassLiteralAccess
*
* Special treatment of _OT$base when refining inherited playedBy:
* (see 1.1.26-otjld-nesting-and-layering-2):
* + do NOT create a new field declaration to avoid duplicates
* + BUT create a new field binding mark with TagBits.IsFakedField
* This field binding allows to correctly type-check <@base> types but it ...
* - is filtered out in ReferenceBinding.fieldCount()
* - does not report uninitializedBlankFinalField
* - is not copied in CopyInheritance.copyField
* - generates getfield of real inherited field plus a checkcast to compensate for weakening
*
* @author stephan
* @version $Id: StandardElementGenerator.java 23416 2010-02-03 19:59:31Z stephan $
*/
public class StandardElementGenerator {
// // need a fully-split (no $compound) name:
// private static final char[][] ORG_OBJECTTEAMS_TEAM_DOT_CONFINED = new char[][]{
// "org".toCharArray(), //$NON-NLS-1$
// "objectteams".toCharArray(), //$NON-NLS-1$
// "Team".toCharArray(), //$NON-NLS-1$
// "Confined".toCharArray() //$NON-NLS-1$
// };
private static final String OUTER_THIS_PREFIX = "this$"; //$NON-NLS-1$
private static final char[] LENGTH = "length".toCharArray(); //$NON-NLS-1$
/**
* For each role (bound or unbound) generate
* public org.objectteams.Team _OT$getTeam()
* If roleType is an interface generate the abstract interface method.
*
* @param roleType
* @return
*/
public static MethodBinding createGetTeamMethod(TypeDeclaration roleType)
{
MethodBinding existingMethod = TypeAnalyzer.findMethod(
roleType.initializerScope,
roleType.binding,
_OT_GETTEAM,
Binding.NO_PARAMETERS);
if (existingMethod != null && existingMethod.isValidBinding()) {
// is already covered by inheritance (copy or extends)
if (roleType.isInterface() == existingMethod.isAbstract())
return null;
}
AstGenerator gen = roleType.baseclass != null ?
new AstGenerator(roleType.baseclass.sourceStart, roleType.baseclass.sourceEnd) :
new AstGenerator(roleType.sourceStart, roleType.sourceEnd);
ReferenceBinding teamType = null;
LookupEnvironment environment = roleType.scope.environment();
try {
environment.missingClassFileLocation = roleType;
teamType = roleType.scope.getOrgObjectteamsITeam();
} finally {
environment.missingClassFileLocation = null;
}
int flags = AccPublic;
if (roleType.isInterface())
flags |= AccAbstract|AccSemicolonBody;
MethodDeclaration getTeam = gen.method(roleType.compilationResult, flags, teamType, _OT_GETTEAM, null);
AstEdit.addMethod(roleType, getTeam);
if ((flags & AccSemicolonBody) == 0)
{
int depth = roleType.binding.depth() - 1;
getTeam.setStatements(new Statement[]{
gen.returnStatement(
gen.fieldReference(
gen.thisReference(),
(OUTER_THIS_PREFIX+depth).toCharArray()
))
});
if (StateMemento.hasMethodResolveStarted(roleType.binding))
getTeam.resolveStatements();
}
return getTeam.binding;
}
/**
* API for Dependencies:
* Create a role cast method.
*/
public static void createCastMethod(TeamModel teamModel, TypeDeclaration roleType, int dimensions) {
getCastMethod(teamModel, roleType.binding, roleType.scope, dimensions, false, roleType.sourceStart, roleType.sourceEnd);
}
/**
* Retreive (or create) a team level method used for casting an expression to a role.
* After casting also check the containingInstance against the current team.
* @param teamClass
* @param roleType
* @param scope (used only for lookup of j.l.Object)
* @param searchSuper should super classes of teamClass be search, too?
* @param sourceStart
* @param sourceEnd
* @return the method
*/
public static MethodBinding getCastMethod(
TeamModel teamModel,
ReferenceBinding roleType,
Scope scope,
int dimensions,
boolean searchSuper,
int sourceStart,
int sourceEnd)
{
/*
* protected <role> _OT$castTo$<role> (Object _OT$arg1) {
* if (_OT$arg1 == null) return null;
* __OT__<role> role = (__OT__<role>) _OT$arg1;
* if (role._OT$getTeam() != this)
* throw new RuntimeException();
* return role;
* }
*OR FOR ARRAY:
* protected <role>[].. _OT$castTo$<role>$<dims> (Object[].. _OT$arg1) {
* if (_OT$arg1 == null) return null;
* <role>[].. role = (<role>[]..) _OT$arg1;
* if (role.length > 0 && ((__OT__<role>)role[0])._OT$getTeam() != this) // TODO(SH): extract through several dims
* throw new RuntimeException();
* return role;
* }
* NOTE(SH): it suffices to check team equivalence for one element, since at this point it
* must already be a role-array, which cannot mix roles from different teams ;-)
*/
boolean shouldWeaken = (teamModel.getState() >= ITranslationStates.STATE_TYPES_ADJUSTED); // weakening for other methods already done?
MethodBinding superMethod = null;
roleType = roleType.getRealType();
char[] methodName = CharOperation.concat(CAST_PREFIX, roleType.sourceName());
if (dimensions > 0)
methodName = CharOperation.concat(methodName, String.valueOf(dimensions).toCharArray(), '$');
ReferenceBinding teamBinding = teamModel.getBinding();
while (teamBinding != null) {
MethodBinding[] methods = teamBinding.getMethods(methodName);
if (methods != null && methods.length == 1) {
if (methods[0].declaringClass == teamModel.getBinding() || searchSuper)
return methods[0];
// go ahead and generate a new method, but use superMethod for weakening after generating:
superMethod = methods[0];
break;
}
if (!searchSuper && !shouldWeaken)
break;
teamBinding = teamBinding.superclass();
}
TypeDeclaration teamClass = teamModel.getAst();
if (teamClass == null) {
if (true) {// FIXME(SH): team has error?
MethodBinding castMethod = new MethodBinding(AccPublic,
methodName,
roleType,
new TypeBinding[]{scope.getJavaLangObject()},
null,
teamModel.getBinding());
teamModel.getBinding().addMethod(castMethod);
return castMethod;
}
throw new InternalCompilerError("Required cast method not found."); //$NON-NLS-1$
}
AstGenerator gen = new AstGenerator(sourceStart, sourceEnd);
// --- method header ---
int modifiers = 0;
boolean clearPrivateModifier = false;
if (roleType.isPublic()) {
modifiers = AccPublic;
} else {
// this weird combination allows to return a non-public role and will
// grant access across packages in the byte code.
modifiers = AccProtected;
clearPrivateModifier = true;
// See also BinaryTypeBinding.resolveTypesFor(MethodBinding) where the Protected flag is restored.
}
// args
char[] argName = OT_DOLLAR_ARG.toCharArray();
// find the appropriate top-level-super-type:
ReferenceBinding exprType = teamClass.scope.getJavaLangObject();
// if (!roleType.isStrictlyCompatibleWith(exprType)) {
// exprType = (ReferenceBinding)teamClass.scope.getType(ORG_OBJECTTEAMS_ICONFINED, 3);
// if (!roleType.isCompatibleWith(exprType))
// exprType = (ReferenceBinding)teamClass.scope.getType(
// ORG_OBJECTTEAMS_TEAM_DOT_CONFINED,
// 4);
// }
TypeReference exprTypeRef = gen.typeReference(exprType);
MethodDeclaration castMethod = gen.method(teamClass.compilationResult(),
modifiers,
gen.createArrayTypeReference(roleType, dimensions),
methodName,
new Argument[]{
gen.argument(argName, exprTypeRef)
});
// <role> role = (<role>)_OT$arg;
TypeReference arrayCastType = gen.createArrayTypeReference(roleType, dimensions);
LocalDeclaration castedLocalVar = gen.localVariable(
ROLE,
arrayCastType,
gen.castExpression(
gen.singleNameReference(argName),
arrayCastType,
CastExpression.RAW));
//STATEMENTS:
// if (_OT$arg1 == null) return null;
//AND
// if (role._OT$getTeam() != this)
// throw new RuntimeException();
// OR
// if (role.length > 0 && ((<roleClass>)role[0])._OT$getTeam() != this)
// throw new RuntimeException();
Statement nullCheck = gen.ifStatement(
gen.nullCheck(gen.singleNameReference(argName)),
gen.returnStatement(gen.nullLiteral()));
Expression teamCheckCondition;
teamCheckCondition = genTeamCheck(gen,
OperatorIds.NOT_EQUAL,
gen.singleNameReference(ROLE),
gen.thisReference(),
dimensions);
if (dimensions > 0)
teamCheckCondition = gen.setPos(new AND_AND_Expression(
gen.equalExpression(
gen.qualifiedNameReference(
new char[][]{ROLE, LENGTH}),
gen.intLiteral(0),
OperatorIds.GREATER),
teamCheckCondition,
OperatorIds.AND_AND));
// here we go:
castMethod.setStatements(new Statement[] {
nullCheck,
castedLocalVar,
gen.ifStatement(
teamCheckCondition,
gen.throwStatement(
gen.allocation(
gen.qualifiedTypeReference(ROLE_CAST_EXCEPTION),
new Expression[0]))),
// return role;
gen.returnStatement(
gen.singleNameReference(ROLE))
});
castMethod.isGenerated = true;
AstEdit.addGeneratedMethod(teamClass, castMethod);
if (clearPrivateModifier)
castMethod.binding.tagBits = TagBits.ClearPrivateModifier;
if (superMethod != null)
CopyInheritance.weakenSignature(castMethod, superMethod);
return castMethod.binding;
}
public static boolean isCastToMethod(char[] selector) {
return CharOperation.prefixEquals(CAST_PREFIX, selector);
}
/**
* Create the code that combines team anchor comparison and a regulare instanceof.
* @param exprType
* @param castType
*/
public static Expression createRoleInstanceOfCheck(
BlockScope scope, InstanceOfExpression expr, ReferenceBinding exprType, DependentTypeBinding castType) {
AstGenerator gen = new AstGenerator(expr.sourceStart, expr.sourceEnd);
Expression teamInstanceComparison;
if (RoleTypeBinding.isRoleWithExplicitAnchor(exprType)) {
teamInstanceComparison = createAnchorEqualCheck
(scope, (RoleTypeBinding)exprType, castType, expr.sourceStart, expr.sourceEnd);
} else {
BinaryExpression teamCheck = genTeamCheck(gen,
OperatorIds.EQUAL_EQUAL,
gen.resolvedCastExpression(
expr.expression, // FIXME(SH): avoid double evaluation of expression!
// but how can we store a value without a statement?
// use a byte-code hack (cf. Lowering.{Pushing,Pop}Expression!)
castType.getRealType(),
CastExpression.RAW),
createTeamAnchorReference(
scope,
castType,
gen),
expr.type.dimensions());
// manually resolve:
MessageSend msg = (MessageSend)teamCheck.left;
msg.binding = castType.getMethod(scope, IOTConstants._OT_GETTEAM);
if (msg.binding == null) {
// add a fake method, assuming it was not created due to errors:
msg.binding = new MethodBinding(AccPublic,
IOTConstants._OT_GETTEAM,
scope.getOrgObjectteamsITeam(),
TypeBinding.NO_PARAMETERS,
TypeBinding.NO_EXCEPTIONS,
castType);
assert castType.roleModel.isIgnoreFurtherInvestigation();
}
msg.actualReceiverType = castType.getRealType();
msg.constant = Constant.NotAConstant;
teamCheck.right.constant = Constant.NotAConstant;
teamCheck.constant = Constant.NotAConstant;
teamCheck.resolvedType = TypeBinding.BOOLEAN;
teamInstanceComparison = teamCheck;
}
TypeReference castTypeRef = gen.typeReference(castType.getRealType());
castTypeRef.resolvedType = castType.getRealType();
castTypeRef.constant = Constant.NotAConstant;
expr.expression.resolvedType = exprType.getRealClass();
expr.expression.constant = Constant.NotAConstant;
InstanceOfExpression origCheckClone = gen.setPos(new InstanceOfExpression(expr.expression, castTypeRef));
origCheckClone.bits = expr.bits; // includes operator
origCheckClone.resolvedType = TypeBinding.BOOLEAN;
origCheckClone.constant = Constant.NotAConstant;
AND_AND_Expression andAnd = gen.setPos(new AND_AND_Expression(origCheckClone, teamInstanceComparison, OperatorExpression.AND_AND));
andAnd.resolvedType = TypeBinding.BOOLEAN;
andAnd.constant = Constant.NotAConstant;
return andAnd;
}
/**
* Generates the comparison of team anchors as needed for cast and instanceof.
*
* @param gen
* @param operator either "==" or "!="
* @param providedExpression
* @param expectedTeamExpr
* @param dimensions
* @return a boolean expression
*/
private static BinaryExpression genTeamCheck(AstGenerator gen,
int operator,
Expression providedExpression,
Expression expectedTeamExpr, int dimensions)
{
BinaryExpression teamCheckCondition;
Expression teamProvidedRef;
Expression providedLeafExpression;
if (dimensions == 0)
providedLeafExpression = providedExpression;
else if (dimensions == 1)
providedLeafExpression = gen.arrayReference(providedExpression, 0);
else
throw new InternalCompilerError("Multidimensional array of roles not supported here"); // FIXME: implement this case, too //$NON-NLS-1$
teamProvidedRef = gen.messageSend(providedLeafExpression, _OT_GETTEAM, null);
teamCheckCondition = gen.equalExpression(
teamProvidedRef,
expectedTeamExpr,
operator);
return teamCheckCondition;
}
/**
* Create the code needed for checking at runtime the identity of type anchors of two role types.
* The result already has all information relating to the resolve phase.
* @param left
* @param right
* @param start start of source code position (fake).
* @param end end of source code position (fake).
* @return a new EqualExpression
*/
private static EqualExpression createAnchorEqualCheck(BlockScope scope, DependentTypeBinding left, DependentTypeBinding right, int start, int end)
{
AstGenerator gen = new AstGenerator(start,end);
Expression exprTeam = createTeamAnchorReference(scope, left, gen);
Expression castTeam = createTeamAnchorReference(scope, right, gen);
EqualExpression teamInstanceComparison = gen.equalExpression(exprTeam, castTeam, OperatorIds.EQUAL_EQUAL);
teamInstanceComparison.constant = Constant.NotAConstant;
return teamInstanceComparison;
}
/**
* @param roleType retrieve the anchor from this type.
* @param pos faked source code position (encoded start and end).
* @return a new reference
*/
private static Expression createTeamAnchorReference(BlockScope scope, DependentTypeBinding roleType, AstGenerator gen) {
Expression teamExpr = createTeamExpression(roleType, gen);
teamExpr.resolveType(scope);
return teamExpr;
}
/** Create an expression evaluating to the team instance inherent to the given role type. */
public static Expression createTeamExpression(DependentTypeBinding roleType, AstGenerator gen)
{
return (roleType._teamAnchor instanceof TThisBinding) ?
gen.qualifiedThisReference((ReferenceBinding)roleType._teamAnchor.getResolvedType())
: gen.singleNameReference(roleType._teamAnchor.internalName());
}
/**
* Create the binding between a role and its base:
* For lowering:
* + create the _OT$base field.
* and/or
* + create the _OT$getBase method (abstract or implemented)
*
* Bytecode:
* + Add the "PlayedBy" Attribute.
*/
public static void generatePlayedByElements(
RoleModel role,
TreeNode boundParentRole)
{
TypeDeclaration roleType = role.getAst();
ReferenceBinding baseclass = roleType.binding.baseclass();
if (baseclass != null)
{
if (boundParentRole == null)
{
// this is a topmost bound role, create the base link method
// synthetic interfaces don't reach here.
if (roleType.isInterface()) {
createGetBaseMethod(roleType, baseclass, _OT_GETBASE, AccPublic|AccAbstract|AccSemicolonBody);
} else {
checkCreateBaseField(roleType, baseclass, true);
// all actual base fields are accessible by a corresponding method:
// for role classes create two versions (ifc/class)
createGetBaseMethod(role.getInterfaceAst(), baseclass, _OT_GETBASE, AccPublic|AccAbstract|AccSemicolonBody);
createGetBaseMethod(roleType, baseclass, _OT_GETBASE, AccPublic);
}
} else {
// potentially create covariant _OT$getBase() method:
ReferenceBinding superBase= boundParentRole.getTreeObject().getBaseTypeBinding();
if (!superBase.isCompatibleWith(baseclass)) {
// optimization: only need the covariant method, if a superinterface declared
// the more specific return type:
ReferenceBinding[] superInterfaces= roleType.binding.superInterfaces();
if (superInterfaces != null)
for (ReferenceBinding superIfc : superInterfaces)
if (superIfc.baseclass() != null && !superBase.isCompatibleWith(superIfc.baseclass())) {
createGetBaseMethod(roleType, baseclass, _OT_GETBASE, AccPublic);
break;
}
}
}
ReferenceBinding interfacePartBinding = role.getInterfacePartBinding();
char[] baseclassName = role.getBaseclassAttributename(true); // with anchor
role.addAttribute(SingleValueAttribute.playedByAttribute(baseclassName));
if (!role.getBinding().isInterface() && interfacePartBinding != null)
interfacePartBinding.roleModel.addAttribute(
SingleValueAttribute.playedByAttribute(baseclassName));
}
}
private static void checkCreateFakedStrongBaseField(
ReferenceBinding superRole, ReferenceBinding roleClass,
ReferenceBinding baseTypeBinding)
{
if (superRole.baseclass == baseTypeBinding)
return; // not strengthening
ReferenceBinding nextSuper= superRole.superclass();
while (nextSuper.isRole() && nextSuper.roleModel.isBound()) {
superRole= nextSuper;
nextSuper= nextSuper.superclass();
}
// create faked base field with exact type information:
FieldBinding fakeStrongBaseField = new FieldBinding(_OT_BASE,
baseTypeBinding,
AccPublic|AccFinal|AccSynthetic,
roleClass,
Constant.NotAConstant);
fakeStrongBaseField.tagBits |= TagBits.IsFakedField;
SourceTypeBinding roleSourceClass = (SourceTypeBinding) roleClass;
FieldModel model = FieldModel.getModel(fakeStrongBaseField);
model.actualDeclaringClass = superRole;
FieldBinding superBaseField= superRole.getField(IOTConstants._OT_BASE, false);
if (superBaseField != null)
fakeStrongBaseField.shareBestName(superBaseField);
roleSourceClass.addField(fakeStrongBaseField);
}
/**
* If roleType is a class-part that does not inherit a base field,
* create one here.
*
* @param roleType the type to hold the new _OT$base field
* @param baseclass the type of the field
* @param createBinding should a FieldBinding be created now?
* @return
*/
public static void checkCreateBaseField(TypeDeclaration roleType,
ReferenceBinding baseclass,
boolean createBinding)
{
if (roleType.isInterface())
return;
if (roleType.fields != null)
for (FieldDeclaration field : roleType.fields)
if (CharOperation.equals(field.name, _OT_BASE))
return;
AstGenerator gen= roleType.baseclass != null
? new AstGenerator(roleType.baseclass)
: new AstGenerator(roleType);
ReferenceBinding superRole = roleType.binding.superclass;
if ( superRole != null // has parent
&& superRole.isRole() // parent is role
&& superRole.roleModel.isBound()) // parent is bound
{
checkCreateFakedStrongBaseField(superRole, roleType.binding, baseclass);
return; // don't try to actually override a field
}
int modifiers = AccSynthetic | AccPublic | AccFinal;
if (roleType.binding.isCompatibleWith(roleType.scope.getOrgObjectteamsIBaseMigratable())) {
modifiers &= ~ClassFileConstants.AccFinal;
// migrate method is added from LiftingEnvironment.createOneRoleBaseLinkage()
}
FieldDeclaration baseField = gen.field(modifiers,
gen.baseclassReference(baseclass),
_OT_BASE,
null);
boolean hasTypeProblem = baseclass instanceof MissingTypeBinding;
AstEdit.addField(roleType, baseField, createBinding, hasTypeProblem, false);
if (hasTypeProblem) {
// faked resolving for missing type
baseField.binding.type= baseclass;
baseField.binding.modifiers &= ~ExtraCompilerModifiers.AccUnresolved;
}
}
/**
* Answer the _OT$getBase() method, create it if needed.
* We return the get base method of the direct type of roleModel.
* If a second AST exists (class/ifc) that one will get a get base method, too.
*
* @param roleModel
* @param baseType
* @return method binding for _OT$getBase().
*/
public static MethodBinding getGetBaseMethod(
BlockScope scope,
RoleModel roleModel,
ReferenceBinding baseType)
{
MethodBinding binding = TypeAnalyzer.findMethod(
scope, roleModel.getBinding(),
_OT_GETBASE, Binding.NO_PARAMETERS);
if (binding.isValidBinding())
return binding;
// CHECK(SH): the code below should be obsolete:
// if no suitable getBase method is found, we need to create it
if (roleModel.getBinding().isBinaryBinding()) {
scope.problemReporter().abortDueToInternalError(
"missing internal _OT$getBase() method"); //$NON-NLS-1$
return binding; // the problem binding.
}
// create both version (ifc/class):
MethodBinding ifcMethod = createGetBaseMethod(
roleModel.getInterfaceAst(), baseType, _OT_GETBASE, AccPublic |AccAbstract|AccSemicolonBody);
if (roleModel.getClassPartAst() != null) {
MethodBinding classMethod = createGetBaseMethod(
roleModel.getClassPartAst(), baseType, _OT_GETBASE, AccPublic);
if (!roleModel.getAst().isInterface())
return classMethod;
}
return ifcMethod;
}
/**
* @return either a newly created method or a valid, existing one.
*/
private static MethodBinding createGetBaseMethod(
TypeDeclaration roleType,
ReferenceBinding baseType,
char[] methodName,
int flags)
{
if (roleType == null)
return null; // sanity check, null can be caused by binary ifc-part in a source role.
MethodBinding existingMethod = null;
AbstractMethodDeclaration decl = TypeAnalyzer.findMethodDecl(roleType, methodName, 0);
if (decl != null)
existingMethod = decl.binding;
if (existingMethod == null)
existingMethod = TypeAnalyzer.findMethod(
roleType.initializerScope,
roleType.binding.superclass(),
methodName,
Binding.NO_PARAMETERS);
if (existingMethod != null && existingMethod.isValidBinding()) { // valid method exists
if (existingMethod.isAbstract() == roleType.isInterface()) // abstractness is correct
if ( existingMethod.declaringClass == roleType.binding // declared here
|| existingMethod.returnType.isCompatibleWith(baseType)) // inherited but compatible
return existingMethod;
}
AstGenerator gen = roleType.baseclass != null ?
new AstGenerator(roleType.baseclass.sourceStart, roleType.baseclass.sourceEnd) :
new AstGenerator(roleType.sourceStart, roleType.sourceEnd);
TypeReference baseclassReference; // must set in if or else
TypeParameter methodParam = null;
Statement bodyStatement; // must set in if or else
if (baseType != null) {
baseclassReference = gen.baseclassReference(baseType);
bodyStatement = gen.returnStatement(
gen.castExpression(
gen.singleNameReference(_OT_BASE),
baseclassReference,
CastExpression.DO_WRAP
));
} else {
// this role is not bound but create a generic getBase method for use via a <B base R> type:
final char[] paramName = "_OT$AnyBase".toCharArray(); //$NON-NLS-1$ used only for this one declaration.
methodParam = gen.baseBoundedTypeParameter(paramName, roleType.binding);
baseclassReference = gen.singleTypeReference(paramName);
final char[][] ABSTRACT_METHOD = new char[][] {"java".toCharArray(), //$NON-NLS-1$
"lang".toCharArray(), //$NON-NLS-1$
"AbstractMethodError".toCharArray()}; //$NON-NLS-1$
bodyStatement = gen.throwStatement(
gen.allocation(
gen.qualifiedTypeReference(ABSTRACT_METHOD),
null));
}
MethodDeclaration getBase = gen.method(roleType.compilationResult, flags,
baseclassReference, methodName, null);
if (methodParam != null)
getBase.typeParameters = new TypeParameter[]{methodParam};
AstEdit.addMethod(roleType, getBase);
if (methodParam != null)
roleType.getRoleModel().unimplementedGetBase = getBase.binding;
if ((flags & AccSemicolonBody) == 0)
{
getBase.setStatements(new Statement[]{bodyStatement});
if (StateMemento.hasMethodResolveStarted(roleType.binding))
getBase.resolveStatements();
}
return getBase.binding;
}
/** If an unbound role declares to implement ILowerable an abstract getBase method must be added. */
public static void createGetBaseForUnboundLowerable(RoleModel model) {
TypeDeclaration interfaceAst = model.getInterfaceAst();
if (interfaceAst == null || interfaceAst.scope == null)
return;
ClassScope scope = interfaceAst.scope;
TypeBinding iLowerable = scope.getType(
new char[][] {IOTConstants.ORG, IOTConstants.OBJECTTEAMS, IOTConstants.ITEAM, IOTConstants.ILOWERABLE},
4);
if (!model.isBound() && model.getBinding().isCompatibleWith(iLowerable))
createGetBaseMethod(interfaceAst, scope.getJavaLangObject(), _OT_GETBASE, AccPublic|AccAbstract|AccSemicolonBody);
}
}