blob: 6af37ffc5a6df3a71a46b1e9a1c96d34a123c6ca [file] [log] [blame]
/**********************************************************************
* This file is part of "Object Teams Development Tooling"-Software
*
* Copyright 2006 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: MethodSignatureEnhancer.java 23417 2010-02-03 20:13:55Z 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.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions.WeavingScheme;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.objectteams.otdt.core.compiler.IOTConstants;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.MethodModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstGenerator;
/**
* This class takes over the responsibility of the BaseCallRedirection Transformer of the OTRE
* wrt. signature enhancing.
* For this task it closely collaborates with BaseCallMessageSend, CallinImplementor and BaseReference.
* In conjunction BaseCallMessageSend and TransformStatementsVisitor take care of replacing base calls
* by calls to the base call surrogate.
* The changes mainly involve the following steps:
* <ul>
* <li> Parser initially adds enhanced args to callin methods
* <li> Resolving MethodSpecs is now aware of its position (expectingCallin)
* <li> MethodSpecs for callin methods maintain two signatures:
* getSourceParameters() and the enhanced resolvedMethod.parameters()
* <li> MethodModel stores the original return type of callin methods
* (before generalizing all base types (including void) to Object)
* <li> BaseCallMessageSend is now responsible for calls to the surrogate
* <li> BaseCallMessageSend can additionally handle base calls within
* local classes within a callin method.
* (The latter feature also concerns BaseReference)
* <li> generate MethodBindings representing future base call surrogates
* (both SourceTypeBinding and BinaryTypeBinding)
* <li> Link a callin MethodBinding and a base call surrogate via MethodModel
* and restore this structure also for copy inherited methods
* <li> CallinMethodMappings still uses source signatures and stores
* additional flag "staticRoleMethod"
* <li> CallinImplementor has to carefully choose the correct signatures/
* return types.
* <li> TransformStatementsVisitor has to enhance signatures of super and
* tsuper calls, as well as modify return statements in case the
* return type is adjusted (perhaps using boxing).
*</ul>
*
* @author stephan
*
*/
@SuppressWarnings("nls")
public class MethodSignatureEnhancer implements IOTConstants, TypeConstants, ClassFileConstants
{
private final static char[] OBJECT_SIGNATURE = "Ljava/lang/Object;".toCharArray();
/**
* Names for arguments in enhanced signature used for passing runtime meta-information to the wrapper/role method.
*/
private final char[][] ENHANCING_ARG_NAMES;
/** Length of the sublist of enhancing arguments. */
public final int ENHANCING_ARG_LEN;
public final char[] UNUSED_ARGS;
public static MethodSignatureEnhancer[] variants = new MethodSignatureEnhancer[WeavingScheme.values().length];
private WeavingScheme weavingScheme;
// {OT/JamVM support:
private static final boolean JAMVM_ASPECTBI = System.getProperty("ot.jamvm.aspectBI") != null;
// CH}
//{OTDyn: make constants configurable:
static {
for(WeavingScheme scheme : WeavingScheme.values())
variants[scheme.ordinal()] = new MethodSignatureEnhancer(scheme);
}
private MethodSignatureEnhancer(WeavingScheme weavingScheme) {
this.weavingScheme = weavingScheme;
if (weavingScheme == WeavingScheme.OTDRE) {
this.ENHANCING_ARG_NAMES = new char[][] {
"_OT$baseArg".toCharArray(),
"_OT$teams".toCharArray(),
"_OT$index".toCharArray(),
"_OT$callinIds".toCharArray(),
"_OT$boundMethodId".toCharArray(),
"_OT$args".toCharArray()};
// {OT/JamVM support:
} else if (JAMVM_ASPECTBI) {
this.ENHANCING_ARG_NAMES = new char[][] {
"_OT$teamIterator".toCharArray(),
"_OT$bindIdx".toCharArray(),
"_OT$baseMethodTag".toCharArray(),
"_OT$unusedArgs".toCharArray() };
// CH}
} else {
this.ENHANCING_ARG_NAMES = new char[][] {
"_OT$teams".toCharArray(),
"_OT$teamIDs".toCharArray(),
"_OT$idx".toCharArray(),
"_OT$bindIdx".toCharArray(),
"_OT$baseMethodTag".toCharArray(),
"_OT$unusedArgs".toCharArray() };
}
this.ENHANCING_ARG_LEN = this.ENHANCING_ARG_NAMES.length;
this.UNUSED_ARGS = this.ENHANCING_ARG_NAMES[this.ENHANCING_ARG_LEN-1];
}
// SH}
/** Get Typebindings for all enhancing arguments. */
private static TypeBinding[] getEnhancingArgTypes (Scope scope) {
//{OTDyn: configurable:
if (scope.compilerOptions().weavingScheme == WeavingScheme.OTDRE)
return new TypeBinding[] {
scope.getType(IOTConstants.ORG_OBJECTTEAMS_IBOUNDBASE2, 3), // _OT$baseArg
scope.createArrayType(scope.getOrgObjectteamsITeam(), 1), // _OT$teams
TypeBinding.INT, // _OT$index
scope.createArrayType(TypeBinding.INT, 1), // _OT$callinIds
TypeBinding.INT, // _OT$boundMethodId
scope.createArrayType(scope.getJavaLangObject(), 1), // _OT$args
};
// SH}
// {OT/JamVM support:
else if (JAMVM_ASPECTBI)
return new TypeBinding[] {
scope.getJavaLangObject(), // _OT$teamIterator
TypeBinding.INT, // _OT$bindIdx
TypeBinding.INT, // _OT$baseMethodTag
scope.createArrayType(scope.getJavaLangObject(), 1), // _OT$unusedArgs
};
// CH}
return new TypeBinding[] {
scope.createArrayType(scope.getOrgObjectteamsITeam(), 1),// _OT$teams
scope.createArrayType(TypeBinding.INT, 1), // _OT$teamIDs
TypeBinding.INT, // _OT$idx
TypeBinding.INT, // _OT$bindIdx
TypeBinding.INT, // _OT$baseMethodTag
scope.createArrayType(scope.getJavaLangObject(), 1), // _OT$unusedArgs
};
}
// ===== AST: =====
/**
* Enhance the arguments of a callin method or its wrapper.
*
* @param arguments source arguments
* @param namePrefix prepend this to all names of enhancement arguments (needed for callin-to-callin)
* @param isWrapper are we enhancing a callin wrapper (vs. the actual callin method)?
* @param gen an AstGenerator positioned for generating new arguments (method.sourceEnd+1).
* @return a new array of args
*/
public static Argument[] enhanceArguments(
Argument[] arguments, char[] namePrefix, boolean isWrapper, AstGenerator gen, WeavingScheme weavingScheme)
{
return variants[weavingScheme.ordinal()].internalEnhanceArguments(arguments, namePrefix, isWrapper, gen);
}
private Argument[] internalEnhanceArguments(
Argument[] arguments, char[] namePrefix, boolean isWrapper, AstGenerator gen)
{
int fullLen = this.ENHANCING_ARG_LEN;
if (arguments != null) fullLen += arguments.length; // source arguments?
Argument[] newArgs = new Argument[fullLen];
TypeReference[] enhanceTypes =
//{OTDyn: configurable:
this.weavingScheme == WeavingScheme.OTDRE
? new TypeReference[] {
gen.qualifiedTypeReference(ORG_OBJECTTEAMS_IBOUNDBASE2), // _OT$baseArg
gen.qualifiedArrayTypeReference(ORG_OBJECTTEAMS_ITEAM, 1), // _OT$teams
gen.singleTypeReference(TypeConstants.INT), // _OT$index
new ArrayTypeReference(TypeConstants.INT, 1, gen.pos), // _OT$callinIds
gen.singleTypeReference(TypeConstants.INT), // _OT$boundMethodId
gen.qualifiedArrayTypeReference(JAVA_LANG_OBJECT, 1), // _OT$args
}
// SH}
// {OT/JamVM support:
: JAMVM_ASPECTBI
? new TypeReference[] {
gen.qualifiedTypeReference(JAVA_LANG_OBJECT),
gen.singleTypeReference(TypeConstants.INT),
gen.singleTypeReference(TypeConstants.INT),
gen.qualifiedArrayTypeReference(JAVA_LANG_OBJECT, 1),
}
// CH}
: new TypeReference[] {
gen.qualifiedArrayTypeReference(ORG_OBJECTTEAMS_ITEAM, 1),
new ArrayTypeReference(TypeConstants.INT, 1, gen.pos),
gen.singleTypeReference(TypeConstants.INT),
gen.singleTypeReference(TypeConstants.INT),
gen.singleTypeReference(TypeConstants.INT),
gen.qualifiedArrayTypeReference(JAVA_LANG_OBJECT, 1),
};
int prefixLen = 0;
// base arg?
if (isWrapper && arguments != null)
newArgs[prefixLen++] = arguments[0];
// enhancing args:
for (int i=0; i<this.ENHANCING_ARG_LEN; i++)
newArgs[i+prefixLen] = gen.argument(
CharOperation.concat(namePrefix, this.ENHANCING_ARG_NAMES[i]),
enhanceTypes[i], AccFinal);
// source args:
if (arguments != null)
System.arraycopy(arguments, prefixLen,
newArgs, this.ENHANCING_ARG_LEN+prefixLen, arguments.length-prefixLen);
return newArgs;
}
/** Enhance the arguments of a base call or self-call within callin. */
public static Expression[] enhanceArguments(Expression[] arguments, int pos, WeavingScheme weavingScheme)
{
return variants[weavingScheme.ordinal()].internalEnhanceArguments(arguments, pos);
}
private Expression[] internalEnhanceArguments(Expression[] arguments, int pos)
{
AstGenerator gen = new AstGenerator(pos, pos);
int fullLen = this.ENHANCING_ARG_LEN; // enhancing arguments
if (arguments != null) fullLen += arguments.length; // source arguments?
Expression[] enhancedArgs = new Expression[fullLen];
// enhancing arguments:
for (int i = 0; i < this.ENHANCING_ARG_LEN; i++)
enhancedArgs[i] = gen.singleNameReference(this.ENHANCING_ARG_NAMES[i]);
// source arguments?
if (arguments != null )
System.arraycopy(arguments, 0, enhancedArgs, this.ENHANCING_ARG_LEN, arguments.length);
return enhancedArgs;
}
// ===== BINDINGS: =====
/** Enhance a type list with the enhancing argument's types.
*
* @param scope needed for lookup of types
* @param parameters source parameters
* @return new array
*/
public static TypeBinding[] enhanceParameters(Scope scope, TypeBinding[] parameters)
{
return variants[scope.compilerOptions().weavingScheme.ordinal()].internalEnhanceParameters(scope, parameters);
}
private TypeBinding[] internalEnhanceParameters(Scope scope, TypeBinding[] parameters)
{
int fullLen = parameters.length + this.ENHANCING_ARG_LEN;
TypeBinding[] newParameters = new TypeBinding[fullLen];
// enhancing parameters:
System.arraycopy(
getEnhancingArgTypes(scope), 0,
newParameters, 0, this.ENHANCING_ARG_LEN);
// source parameters:
System.arraycopy(
parameters, 0,
newParameters, this.ENHANCING_ARG_LEN, parameters.length);
return newParameters;
}
/**
* Given that methodDecl is a callin method with base type return, adjust the return type
* to j.l.Object, while storing the original returnType via MethodModel.
*
* @param methodDecl
* @param returnType
* @return the generalized type (Object)
*/
public static TypeBinding generalizeReturnType(MethodDeclaration methodDecl, TypeBinding returnType)
{
MethodModel.saveReturnType(methodDecl, returnType);
returnType= methodDecl.scope.getJavaLangObject();
methodDecl.returnType.resolvedType= returnType;
return returnType;
}
/**
* Prepare for generic handling of return values: all base types are converted to java.lang.Object.
* @param orig
* @param environment for lookup of java.lang.Object
* @return either a binding for java.lang.Object or the original type 'orig'
*/
public static TypeBinding getGeneralizedReturnType (TypeBinding orig, LookupEnvironment environment) {
if (orig.isBaseType())
return environment.getType(TypeConstants.JAVA_LANG_OBJECT);
return orig;
}
// ===== SIGNATURES (String) =====
//
// private static char[] enhanceArgsSignature = "[Lorg/objectteams/Team;[IIII[Ljava/lang/Object;".toCharArray();
//
// public static char[] enhanceSignature(char[] orig)
// {
// // disassemble:
// int rparenPos = CharOperation.indexOf(')', orig);
// char[] sourceArgs = CharOperation.subarray(orig, 1, rparenPos); // assumes orig[0] == '('
// // generalize return?
// char[] returnType;
// if (rparenPos == orig.length-2) // only one char after ')' means: return is a base type.
// returnType = OBJECT_SIGNATURE;
// else
// returnType = CharOperation.subarray(orig, rparenPos+1, -1);
//
// // enhance arguments:
// char[] args = CharOperation.concat(
// enhanceArgsSignature,
// sourceArgs);
//
// // re-assemble
// return assembleSignature(args, returnType);
// }
//
// private static char[] assembleSignature(char[] args, char[] returnType) {
// char[] result = new char[args.length+returnType.length+2];
// result[0] = '(';
// System.arraycopy(args, 0, result, 1, args.length);
// result[args.length+1] = ')';
// System.arraycopy(returnType, 0, result, args.length+2, returnType.length);
// return result;
// }
public static char[] generalizeReturnInSignature(char[] signature) {
int rparenPos = CharOperation.indexOf(')', signature);
if (rparenPos == signature.length-2) // only one char after ')' means: return is a base type.
return CharOperation.concat(
CharOperation.subarray(signature, 0, rparenPos+1),
OBJECT_SIGNATURE);
return signature;
}
/** If methodDecl is a callin method return just its source-level arguments. */
public static Argument[] maybeRetrenchArguments(MethodDeclaration methodDecl, WeavingScheme weavingScheme) {
return variants[weavingScheme.ordinal()].internalMaybeRetrenchArguments(methodDecl);
}
private Argument[] internalMaybeRetrenchArguments(MethodDeclaration methodDecl) {
if (!methodDecl.isCallin() || !internalIsEnhanced(methodDecl))
return methodDecl.arguments;
int len = methodDecl.arguments.length;
Argument[] result = new Argument[len-this.ENHANCING_ARG_LEN];
System.arraycopy(methodDecl.arguments, this.ENHANCING_ARG_LEN, result, 0, result.length);
return result;
}
public static Expression[] retrenchBasecallArguments(Expression[] arguments, boolean isEnhanced, WeavingScheme weavingScheme) {
return variants[weavingScheme.ordinal()].internalRetrenchBasecallArguments(arguments, isEnhanced);
}
private Expression[] internalRetrenchBasecallArguments(Expression[] arguments, boolean isEnhanced) {
if (arguments == null) return null;
int len = arguments.length;
int discard = this.weavingScheme == WeavingScheme.OTDRE ? 0 : 1; // isSuperAccess (unconditionally) // FIXME(OTDYN) must handle super flag?
if (isEnhanced) // if TransformStatementsVisitor has modified this node
discard += this.ENHANCING_ARG_LEN;
Expression[] result = new Expression[len-discard];
System.arraycopy(arguments, discard, result, 0, result.length);
return result;
}
public TypeBinding[] retrenchParameterTypes(TypeBinding[] parameters) {
if (parameters == null) return null;
int len = parameters.length;
if (len >= this.ENHANCING_ARG_LEN) {
TypeBinding[] result = new TypeBinding[len-this.ENHANCING_ARG_LEN];
System.arraycopy(parameters, this.ENHANCING_ARG_LEN, result, 0, result.length);
return result;
}
return parameters;
}
public static void beautifyTypesString(StringBuffer types, boolean makeShort, WeavingScheme weavingScheme) {
String typeString = types.toString();
String prefix =
//{OTDyn: configurable:
weavingScheme == WeavingScheme.OTDRE
? (makeShort
? "IBoundBase2, ITeam[], int, int[], int, Object[]"
: "org.objectteams.IBoundBase2, org.objectteams.ITeam[], int, int[], int, java.lang.Object[]")
// SH}
// {OT/JamVM support:
: JAMVM_ASPECTBI
? (makeShort
? "Object, int, int, Object[]"
: "java.lang.Object, int, int, java.lang.Object[]")
// CH}
: (makeShort
? "ITeam[], int[], int, int, int, Object[]"
: "org.objectteams.ITeam[], int[], int, int, int, java.lang.Object[]");
if (typeString.startsWith(prefix)) {
types.delete(0, prefix.length());
if (types.length()> 0 && types.charAt(0) == ',')
types.delete(0, 2); // remove leading ", "
}
}
public static Argument[] getSourceArguments(AbstractMethodDeclaration methodDeclaration, WeavingScheme weavingScheme) {
return variants[weavingScheme.ordinal()].internalGetSourceArguments(methodDeclaration);
}
public static Argument[] getSourceArguments(AbstractMethodDeclaration methodDeclaration) {
WeavingScheme weavingScheme = WeavingScheme.OTRE; // "default" just to avoid null
if (methodDeclaration.scope != null) {
weavingScheme = methodDeclaration.scope.compilerOptions().weavingScheme;
}
return variants[weavingScheme.ordinal()].internalGetSourceArguments(methodDeclaration);
}
private Argument[] internalGetSourceArguments(AbstractMethodDeclaration methodDeclaration) {
Argument[] arguments = methodDeclaration.arguments;
if (methodDeclaration.isCallin() && internalIsEnhanced(methodDeclaration)) {
assert arguments != null;
int len = arguments.length - this.ENHANCING_ARG_LEN;
assert len >= 0;
if (len == 0)
arguments = null;
else
System.arraycopy(
arguments, this.ENHANCING_ARG_LEN,
arguments = new Argument[len], 0, len);
} else if (CharOperation.prefixEquals(IOTConstants.BASE_PREDICATE_PREFIX, methodDeclaration.selector)) {
// hide base arg of base predicate (has dummy type after parsing):
return null;
}
return arguments;
}
public static boolean isEnhanced(AbstractMethodDeclaration methodDeclaration, WeavingScheme weavingScheme) {
return variants[weavingScheme.ordinal()].internalIsEnhanced(methodDeclaration);
}
private boolean internalIsEnhanced(AbstractMethodDeclaration methodDeclaration) {
Argument[] arguments = methodDeclaration.arguments;
if (arguments == null || arguments.length < this.ENHANCING_ARG_LEN)
return false;
for (int i = 0; i < this.ENHANCING_ARG_LEN; i++) {
if (!CharOperation.endsWith(arguments[i].name, this.ENHANCING_ARG_NAMES[i]))
return false;
}
return true;
}
public static int getEnhancingArgLen(WeavingScheme weavingScheme) {
return variants[weavingScheme.ordinal()].ENHANCING_ARG_LEN;
}
}