blob: 4e5687edf80d6266dd385cef98f191c5ab5f1917 [file] [log] [blame]
/**********************************************************************
* This file is part of "Object Teams Development Tooling"-Software
*
* Copyright 2009 Stephan Herrmann
*
* 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$
*
* Please visit http://www.eclipse.org/objectteams for updates and contact.
*
* Contributors:
* Stephan Herrmann
**********************************************************************/
package org.eclipse.objectteams.otdt.internal.core.compiler.lookup;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
import org.eclipse.jdt.internal.compiler.codegen.Opcodes;
import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
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.SyntheticMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
import org.eclipse.objectteams.otdt.core.compiler.IOTConstants;
import org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.WordValueAttribute;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.Config;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.Config.NotConfiguredException;
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.model.RoleModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.transformer.MethodSignatureEnhancer;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.RoleTypeCreator;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.TSuperHelper;
/**
* This class represents placeholder base-call surrogates which only throw
* an OTREInternalError -> no need to have these generated by the OTRE.
*
* @author stephan
* @since 1.3.0
*/
public class SyntheticBaseCallSurrogate extends SyntheticOTMethodBinding
{
// this corresponds to IOTConstants.OT_DOLLAR_NAME without the trailing '$':
private static final char[] _OT = "_OT".toCharArray(); //$NON-NLS-1$
private TypeBinding errorType;
private ReferenceBinding stringType;
/**
* Create a binding for a synthetic base-call surrogate.
*
* @param callinMethod the callin method potentially holding base-calls
* @param declaringClass the class that will hold the surrogate implementation: normally the role, for static callin methods: the team.
*/
public SyntheticBaseCallSurrogate(MethodBinding callinMethod, SourceTypeBinding declaringClass) {
super(declaringClass, ClassFileConstants.AccProtected|ClassFileConstants.AccSynthetic, callinMethod.selector, callinMethod.parameters, callinMethod.returnType);
this.selector = SyntheticBaseCallSurrogate.genSurrogateName(callinMethod.selector, callinMethod.declaringClass.sourceName(), callinMethod.isStatic());
if (!callinMethod.isStatic() && callinMethod.isCallin()) { // don't change paramaters if enhancement is absent
// additional arg "boolean isSuperAccess":
this.parameters = addIsSuperAccessArg(this.parameters);
}
this.purpose = MethodAccess;
this.targetMethod = callinMethod;
TypeBinding origReturnType = MethodModel.getReturnType(callinMethod);
MethodModel.saveReturnType(this, origReturnType);
// fetch type bindings while we have a scope:
Scope scope = declaringClass.scope;
this.errorType = scope.getType(IOTConstants.OTRE_INTERNAL_ERROR, 5);
this.stringType = scope.getJavaLangString();
// the synthetic methods of a class will be sorted according to a per-class index, find the index now:
SyntheticMethodBinding[] knownAccessMethods = declaringClass.syntheticMethods();
this.index = knownAccessMethods == null ? 0 : knownAccessMethods.length;
this.sourceStart = declaringClass.scope.referenceContext.sourceStart;
retrieveLineNumber(declaringClass);
}
/** Directly generate the instruction for this method's body (no AST available). */
public void generateInstructions(CodeStream codeStream) {
codeStream.new_(this.errorType);
codeStream.dup();
codeStream.ldc("Binding error: base-call impossible!"); //$NON-NLS-1$
MethodBinding ctorBinding = ((ReferenceBinding)this.errorType).getExactConstructor(new TypeBinding[]{this.stringType});
codeStream.invoke(Opcodes.OPC_invokespecial, ctorBinding, this.errorType);
codeStream.athrow();
codeStream.aconst_null(); // always generalized to Object
codeStream.areturn();
}
/** Is the callin method bound in the given role class? */
public static boolean isCallinMethodBoundIn(MethodBinding callinMethod, ReferenceBinding roleClass) {
if (TypeBinding.notEquals(callinMethod.declaringClass, roleClass)) {
callinMethod = roleClass.getExactMethod(callinMethod.selector, callinMethod.parameters, null);
if (callinMethod == null)
return false;
}
if (roleClass.callinCallouts != null)
for (CallinCalloutBinding mapping : roleClass.callinCallouts)
if (mapping._roleMethodBinding == callinMethod)
return true;
return false;
}
/** Does the current role (callinMethod.declaringClass) inherit a callin binding for this method? */
public static boolean isBindingForCallinMethodInherited(MethodBinding callinMethod) {
ReferenceBinding declaringClass = callinMethod.declaringClass;
if (isCallinMethodBoundIn(callinMethod, declaringClass))
return false;
while ((declaringClass = declaringClass.superclass()) != null) {
if (isCallinMethodBoundIn(callinMethod, declaringClass))
return true;
}
return false;
}
/**
* Add any required base-call surrogates to the given type.
* @param type must be a role class.
*/
public static void addFakedBaseCallSurrogates(SourceTypeBinding type) {
if (type.methods() == null)
return;
if (CallinImplementorDyn.DYNAMIC_WEAVING) // no surrogates for the dynamic weaver.
return;
for (MethodBinding method : type.methods())
if (method.isCallin() && (method.returnType != null))
SyntheticBaseCallSurrogate.getBaseCallSurrogate(method, type.roleModel, type.scope.environment());
}
/**
* Retrieve or create a method binding representing the base call surrogate for a callin method.
* The actual method will be created by the OTRE.
*
* PRE: callinMethod already has all signature enhancements
*
* @param callinMethod
* @param environment needed for lookup of java/lang/Object (needed for return type generalization).
* and for wrapping role types.
* @return a MethodBinding (if it already exists or if role is binary) or a new SyntheticBaseCallSurrogate or null (if none is required)
*/
public static MethodBinding getBaseCallSurrogate(MethodBinding callinMethod, RoleModel clazz, LookupEnvironment environment)
{
try {
if (TSuperHelper.isTSuper(callinMethod))
return null;
if (SyntheticBaseCallSurrogate.isBindingForCallinMethodInherited(callinMethod))
return null;
MethodBinding result = null;
ReferenceBinding roleType = callinMethod.declaringClass;
ReferenceBinding teamType = roleType.enclosingType();
ReferenceBinding declaringClass = callinMethod.isStatic() ? teamType : roleType;
char[] roleName = roleType.sourceName();
char[] selector =
genSurrogateName(callinMethod.selector, roleName, callinMethod.isStatic());
TypeBinding returnType = MethodSignatureEnhancer.getGeneralizedReturnType(callinMethod.returnType, environment);
TypeBinding[] baseCallParameters = callinMethod.parameters;
if (!callinMethod.isStatic())
baseCallParameters = addIsSuperAccessArg(baseCallParameters);
// search existing surrogate:
candidates:
for (MethodBinding candidate : declaringClass.getMethods(selector)) {
if (candidate.parameters.length != baseCallParameters.length)
continue;
for (int i=0; i<baseCallParameters.length; i++)
if (!areTypesEqual(baseCallParameters[i].erasure(), candidate.parameters[i]))
continue candidates;
result = candidate;
break;
}
if (result == null) {
if (declaringClass.isBinaryBinding())
result = new MethodBinding(
ClassFileConstants.AccPublic,
selector,
returnType,
baseCallParameters,
null /*exceptions*/,
declaringClass);
else
result = ((SourceTypeBinding)declaringClass).addSyntheticBaseCallSurrogate(callinMethod);
MethodModel.getModel(result)._fakeKind = MethodModel.FakeKind.BASECALL_SURROGATE;
RoleTypeCreator.wrapTypesInMethodBindingSignature(result, environment);
result.modifiers &= ~ExtraCompilerModifiers.AccUnresolved;
declaringClass.addMethod(result);
}
MethodModel.getModel(callinMethod).setBaseCallSurrogate(result);
return result;
} catch (RuntimeException ex) {
// check if failure is due to incompatible class file version
if (clazz != null && clazz.isIncompatibleCompilerVersion()) {
String version;
version = WordValueAttribute.getBytecodeVersionString(clazz._compilerVersion);
// note: we have no source to report this error against,
// so use whatever the current problem reporter is configured for.
String errorMessage = "Byte code for class "+String.valueOf(clazz.getBinding().readableName())
+" has incompatible version "+version;
try {
Config.getLookupEnvironment().problemReporter.abortDueToInternalError(
errorMessage);
} catch (NotConfiguredException e) {
throw new AbortCompilation(false, e);
}
return null;
}
throw ex; // want to see all other exceptions
}
}
/** For role types ignore weakening and anchors. */
static boolean areTypesEqual(TypeBinding one, TypeBinding two) {
if (TypeBinding.equalsEquals(one, two)) return true;
ReferenceBinding enclosingOne = one.enclosingType();
if (enclosingOne == null) return false;
ReferenceBinding enclosingTwo = two.enclosingType();
if (enclosingTwo == null) return false;
if (!areTypesEqual(enclosingOne, enclosingTwo)) return false;
return CharOperation.equals(one.sourceName(), two.sourceName());
}
static TypeBinding[] addIsSuperAccessArg(TypeBinding[] baseCallParameters)
{
int len = baseCallParameters.length;
TypeBinding[] newParams = new TypeBinding[len+1];
System.arraycopy(baseCallParameters, 0, newParams, 0, MethodSignatureEnhancer.ENHANCING_ARG_LEN);
newParams[MethodSignatureEnhancer.ENHANCING_ARG_LEN] = TypeBinding.BOOLEAN;
System.arraycopy(baseCallParameters, MethodSignatureEnhancer.ENHANCING_ARG_LEN,
newParams, MethodSignatureEnhancer.ENHANCING_ARG_LEN+1,
len-MethodSignatureEnhancer.ENHANCING_ARG_LEN);
return newParams;
}
public static char[] genSurrogateName(char[] methodName, char[] roleName, boolean isStatic)
{
if (isStatic)
return CharOperation.concatWith(
new char[][] {_OT, roleName, methodName, "base".toCharArray()}, //$NON-NLS-1$
'$');
return CharOperation.concatWith(
new char[][] {_OT, methodName, "base".toCharArray()}, //$NON-NLS-1$
'$');
}
public static boolean isBaseCallSurrogateName(char[] name) {
char[][] split = CharOperation.splitOn('$', name);
if (split.length < 3)
return false;
return CharOperation.equals(split[0], _OT)
&& CharOperation.equals(split[split.length-1], IOTConstants.BASE);
}
public static char[] stripBaseCallName(char[] selector) {
if (CharOperation.occurencesOf('$', selector) == 2) {
char[][] tokens = CharOperation.splitOn('$', selector);
selector = tokens[1]; // pick m from _OT$m$base
}
return selector;
}
}