blob: 85cdfe3955e20b315741c8ee730dce8fab4f99f6 [file] [log] [blame]
/**********************************************************************
* This file is part of "Object Teams Development Tooling"-Software
*
* Copyright 2005, 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: CalloutMappingsAttribute.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.bytecode;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileStruct;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
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.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.parser.TerminalTokens;
import org.eclipse.objectteams.otdt.core.compiler.IOTConstants;
import org.eclipse.objectteams.otdt.core.exceptions.InternalCompilerError;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.ITranslationStates;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.CallinCalloutBinding;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.RoleModel;
/**
* Represents the "CalloutMappings" attribute
*
* Location:
* A bound role class with a callout binding.
*
* Content:
* A list of method bindings consisting of:
* role method name
* role method signature
* .. more elements may need to be added ..
*
* Purpose:
* Only used in the compiler:
* + create CallinCalloutBindings from this information,
* - used specifically for improved error reporting.
* - could also be usefull for search etc??
*
* @author stephan
* @version $Id: CalloutMappingsAttribute.java 23417 2010-02-03 20:13:55Z stephan $
*/
public class CalloutMappingsAttribute extends ListValueAttribute {
RoleModel _role;
private CallinCalloutBinding[] _mappings; // array of CallinCalloutBinding
private char[][] _methodNames= null;
private char[][] _methodSignatures = null;
private char[][] _baseMethodNames = null;
private char[][] _baseMethodSignatures = null;
private byte[] _flags = null;
public static final byte CALLOUT_GET = 1<<0;
public static final byte CALLOUT_SET = 1<<1;
private static final byte CALLOUT_PUBLIC = 1<<2;
private static final byte CALLOUT_PROTECTED = 1<<3;
private static final byte CALLOUT_PRIVATE = 1<<4;
private static final int ELEMENT_LENGTH = 11;
// 5 name references and 1 flag-byte:
// role: method-name, method-signature
// base: declaring-class, method-name, method-signature
/**
* Create an attribute from source
* @param role fetch all further info from this role
*/
public CalloutMappingsAttribute(RoleModel role) {
super(IOTConstants.CALLOUT_MAPPINGS,
0, // count is not yet known, add in write(..)
ELEMENT_LENGTH);
this._role = role;
}
/**
* Create an attribute from .class file.
*
* @param reader
* @param readOffset
* @param constantPoolOffsets
*/
public CalloutMappingsAttribute(
ClassFileStruct reader,
int readOffset,
int[] constantPoolOffsets)
{
super(IOTConstants.CALLOUT_MAPPINGS, 0, ELEMENT_LENGTH); // count still unknown?
readList(reader, readOffset, 0 /* no structOffset */, constantPoolOffsets);
}
public int getNumMappings() {
return this._methodNames.length;
}
public String getRoleMethodNameAt(int i) {
return String.valueOf(this._methodNames[i]);
}
public String getRoleMethodSignatureAt(int i) {
return String.valueOf(this._methodSignatures[i]);
}
public String getBaseMethodNameAt(int i) {
return String.valueOf(this._baseMethodNames[i]);
}
public String getBaseMethodSignatureAt(int i) {
return String.valueOf(this._baseMethodSignatures[i]);
}
public int getCalloutFlagsAt(int i) {
return this._flags[i] & (CALLOUT_GET|CALLOUT_SET);
}
public int getDeclaredModifiersAt(int i) {
switch (this._flags[i] & (CALLOUT_PUBLIC|CALLOUT_PROTECTED|CALLOUT_PRIVATE)) {
case CALLOUT_PUBLIC : return ClassFileConstants.AccPublic;
case CALLOUT_PROTECTED : return ClassFileConstants.AccProtected;
case CALLOUT_PRIVATE : return ClassFileConstants.AccPrivate;
default : return 0;
}
}
@Override
void read(int i)
{
if (this._methodNames== null) {
this._methodNames = new char[this._count][];
this._methodSignatures = new char[this._count][];
this._flags = new byte[this._count];
this._baseMethodNames = new char[this._count][];
this._baseMethodSignatures = new char[this._count][];
}
this._methodNames[i] = consumeName();
this._methodSignatures[i] = consumeName();
this._flags[i] = (byte)consumeByte();
// ignored:
consumeName(); // base declaring class
this._baseMethodNames[i] = consumeName();
this._baseMethodSignatures[i] = consumeName();
}
@Override
public boolean setupForWriting() {
if (this._role.isIgnoreFurtherInvestigation())
return false; // don't write this attribute, resolved information might be missing.
// before starting to write out the attribute prepare the data:
this._mappings = this._role.getBinding().callinCallouts;
this._count = filterGoodBindings();
return this._count > 0;
}
private int filterGoodBindings () {
int i=0;
for (int j = 0; j < this._mappings.length; j++) {
this._mappings[i] = this._mappings[j];
CallinCalloutBinding currenMapping = this._mappings[j];
if ( currenMapping.isCallout()
&& ((currenMapping.tagBits & TagBits.HasMappingIncompatibility) == 0)
&& currenMapping.hasValidRoleMethod()
&& currenMapping.hasValidBaseMethods())
i++;
}
return i;
}
/* (non-Javadoc)
* @see org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.ListValueAttribute#writeElementValue(int)
*/
@Override
void writeElementValue(int i) {
CallinCalloutBinding mapping = this._mappings[i];
writeName(mapping._roleMethodBinding.constantPoolName());
writeName(mapping._roleMethodBinding.signature());
byte flags = 0;
switch (mapping.calloutModifier) {
case TerminalTokens.TokenNameget: flags = CALLOUT_GET; break;
case TerminalTokens.TokenNameset: flags = CALLOUT_SET; break;
}
switch (mapping.declaredModifiers) {
case ClassFileConstants.AccPublic: flags |= CALLOUT_PUBLIC; break;
case ClassFileConstants.AccProtected: flags |= CALLOUT_PROTECTED; break;
case ClassFileConstants.AccPrivate: flags |= CALLOUT_PRIVATE; break;
}
writeByte(flags);
MethodBinding baseMethod = mapping._baseMethods[0];
writeName(baseMethod.declaringClass.attributeName());
writeName(baseMethod.constantPoolName());
writeName(baseMethod.signature());
}
/* (non-Javadoc)
* @see org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.AbstractAttribute#evaluate(org.eclipse.jdt.internal.compiler.lookup.Binding, org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment)
*/
@Override
public void evaluate(Binding binding, LookupEnvironment environment, char[][][] missingTypeNames) {
checkBindingMismatch(binding, ExtraCompilerModifiers.AccRole);
((ReferenceBinding)binding).roleModel.addAttribute(this);
}
// Evaluate CalloutMethodMappingAttribute late, because we need our methods to be in place.
@Override
public void evaluateLateAttribute(ReferenceBinding type, int state)
{
if (state != ITranslationStates.STATE_FAULT_IN_TYPES)
return;
this._mappings = new CallinCalloutBinding[this._count];
if (this._count == 0)
return;
nextMethod:
for (int i = 0; i < this._methodNames.length; i++) {
char[] methodName = this._methodNames[i];
MethodBinding[] methods = null;
ReferenceBinding site = type;
while (site != null) {
methods = site.getMethods(methodName);
for (int j = 0; j < methods.length; j++) {
if (methodMatchesSignature(methods[j], this._methodSignatures[i]))
{
int calloutModifiers =0;
if ((this._flags[i] & CALLOUT_GET) != 0)
calloutModifiers = TerminalTokens.TokenNameget;
else if ((this._flags[i] & CALLOUT_SET) != 0)
calloutModifiers = TerminalTokens.TokenNameset;
int declaredModifiers = getDeclaredModifiersAt(i);
this._mappings[i] = new CallinCalloutBinding(
false, // override: neither known nor relevant, currently
methods[j],
type,
calloutModifiers,
declaredModifiers);
continue nextMethod;
}
}
site = site.superclass();
}
// TODO(SH): could we have generated such an attribute??
// See TPX-326(1).
throw new InternalCompilerError("callout binding refers to inexistent role method: "+ //$NON-NLS-1$
new String(this._methodNames[i])+new String(this._methodSignatures[i]));
}
type.addCallinCallouts(this._mappings);
}
private boolean methodMatchesSignature(MethodBinding methodBinding, char[] signature) {
if (CharOperation.equals(methodBinding.signature(), signature))
return true;
char[][] signatureTypes = scanSignature(signature);
TypeBinding type = methodBinding.returnType;
if (!typeMatchesSignature(type, signatureTypes[0]))
return false;
char[][] types = CharOperation.subarray(signatureTypes, 1, -1);
if (types.length != methodBinding.parameters.length)
return false;
for (int i=0; i<types.length; i++)
if (!typeMatchesSignature(methodBinding.parameters[i], types[i]))
return false;
return true;
}
private boolean typeMatchesSignature(TypeBinding type, char[] signature) {
char[] bindingType;
char[] signatureType = signature;
if (!type.isValidBinding()) {
signatureType = getSignatureSimpleName(signatureType);
bindingType = type.internalName();
} else {
bindingType = type.signature();
}
return CharOperation.equals(bindingType, signatureType);
}
private char[] getSignatureSimpleName(char[] signatureType) {
if (signatureType[signatureType.length-1]!=';')
return signatureType;
int start = CharOperation.lastIndexOf('/', signatureType);
if (start == -1)
start = 0;
return CharOperation.subarray(signatureType, start+1, signatureType.length-1);
}
public static char[][] scanSignature(char[] methodDescriptor) throws IllegalArgumentException {
// modelled after BinaryTypeBinding.createMethod (simplified)
int numOfParams = 0;
char nextChar;
int index = 0; // first character is always '(' so skip it
while ((nextChar = methodDescriptor[++index]) != ')') {
if (nextChar != '[') {
numOfParams++;
if (nextChar == 'L')
while ((nextChar = methodDescriptor[++index]) != ';'){/*empty*/}
}
}
// Ignore synthetic argument for member types or enum types.
int startIndex = 0;
// NOTE: no callout to ctor, thus no check 'isConstructor()'
int size = numOfParams - startIndex;
char[][] result= new char[size+1][]; // [0] is returnType
if (size > 0) {
index = 1;
int end = 0; // first character is always '(' so skip it
for (int i = 0; i < numOfParams; i++) {
while ((nextChar = methodDescriptor[++end]) == '['){/*empty*/}
if (nextChar == 'L')
while ((nextChar = methodDescriptor[++end]) != ';'){/*empty*/}
if (i >= startIndex) { // skip the synthetic arg if necessary
result[i - startIndex + 1] = CharOperation.subarray(methodDescriptor, index, end+1);
}
index = end + 1;
}
}
result[0] = CharOperation.subarray(methodDescriptor, index + 1, -1);
return result;
}
/* (non-Javadoc)
* @see org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.ListValueAttribute#toString(int)
*/
@Override
String toString(int i) {
if (this._mappings != null)
return this._mappings[i].toString();
StringBuffer buf = new StringBuffer();
buf.append(this._methodNames[i]);
buf.append(this._methodSignatures[i]);
buf.append(" -> "); //$NON-NLS-1$
if ((this._flags[i] & CALLOUT_GET) != 0)
buf.append("get "); //$NON-NLS-1$
if ((this._flags[i] & CALLOUT_SET) != 0)
buf.append("set "); //$NON-NLS-1$
buf.append(this._baseMethodNames[i]);
buf.append(this._baseMethodSignatures[i]);
return buf.toString();
}
}