blob: 25e515547bb48eda3f3f63eb05bde0ff889f380c [file] [log] [blame]
/**********************************************************************
* This file is part of "Object Teams Development Tooling"-Software
*
* Copyright 2004, 2015 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
*
* 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.ClassFile;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileStruct;
import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
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.ReferenceBinding;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.ITeamAnchor;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.FieldModel;
/**
* Bytecode attributes with exactly one (fixed-length) value (string).
* Currently handled:
* <ul>
* <li>PlayedBy
* Represents the "PlayedBy" attribute
*
* Location:
* A role class which, or which super role class, is bound to a base class.
*
* Content:
* The name of the bound base class.
*
* Purpose:
* The OTRE uses this attribute to create BoundBaseClass instances.
* The OTDT uses this attribute to read the playedBy declaration from binary classes (needs to restore anchored types here)
*
* <li>FieldTypeAnchor
* Represents the "FieldTypeAnchor" attribute.
*
* Location:
* A field whose type is an externalized role.
*
* Content:
* A flat encoding of the anchor path, i.e., a '.'-separated list of fields,
* possibly prefixed with a type (for a static field as first segment of the path).
* The type uses '.' to separate packages, '$' to separate inner types.
*
* Purpose:
* For type checking the compiler uses this attribute to restore the RoleTypeBinding
* for a field read from a class file.
*
* </ul>
* @author stephan
*/
public class SingleValueAttribute
extends AbstractAttribute
{
// ============== STATIC API ===================
public static final char[] ANCHOR_DELIM = "<@".toCharArray(); //$NON-NLS-1$
/**
* Create a new "PlayedBy" attribute.
* FIXME(SH): need to include anchor information into this attribute.
* see: 2.1.5-otjld_bound-to-role-11
* cf. also: CodeStream.recordTypeBinding(), ConstantPoolObjectReader.getType()
*/
public static SingleValueAttribute playedByAttribute(char[] baseClass)
{
return new SingleValueAttribute(PLAYEDBY_NAME, baseClass);
}
/**
* Read a PlayedBy from byte code.
*/
public static SingleValueAttribute readPlayedBy(
ClassFileStruct reader,
int readOffset,
int[] constantPoolOffsets)
{
int utf8Offset = constantPoolOffsets[reader.u2At(readOffset)];
char[] value = reader.utf8At(utf8Offset + 3, reader.u2At(utf8Offset + 1));
SingleValueAttribute result = playedByAttribute(value);
result._reader = reader;
return result;
}
/**
* Create a "FieldTypeAnchor" attribute.
*/
public static SingleValueAttribute fieldTypeAnchorAttribute(char[] typeAnchor) {
return new SingleValueAttribute(FIELD_TYPE_ANCHOR, typeAnchor);
}
/**
* Read a "FieldTypeAnchor" attribute from byte code.
*/
public static SingleValueAttribute readFieldTypeAnchor(
ClassFileStruct reader,
int readOffset,
int structOffset,
int[] constantPoolOffsets)
{
int idx = reader.u2At(readOffset);
// attention: reader will add structOffset, but we appearently need to read at
// a location without structOffset! =:-0
int utf8Offset = constantPoolOffsets[idx] - structOffset;
int num = reader.u2At(utf8Offset + 1);
char[] value = reader.utf8At(utf8Offset + 3, num);
return fieldTypeAnchorAttribute(value);
}
// the attribute value
private char[] _value;
/**
* INTERNAL USE ONLY. Use static factory methods instead.
* @param name
* @param value
*/
protected SingleValueAttribute(char[] name, char[] value) {
super(name);
this._value = value;
}
public char[] getValue() {
return this._value;
}
@Override
int size() {
return 8;
}
/* (non-Javadoc)
* @see org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.AbstractAttribute#write(org.eclipse.jdt.internal.compiler.ClassFile)
*/
@Override
public void write(ClassFile classFile)
{
super.write(classFile);
if (this._contentsOffset + 8 > this._contents.length) {
this._contents = classFile.getResizedContents(8);
}
// write the name
int attributeNameIndex = this._constantPool.literalIndex(this._name);
this._contents[this._contentsOffset++] = (byte) (attributeNameIndex >> 8);
this._contents[this._contentsOffset++] = (byte) attributeNameIndex;
// The length of a singele value attribute is 2 (fixed-length).
this._contents[this._contentsOffset++] = 0;
this._contents[this._contentsOffset++] = 0;
this._contents[this._contentsOffset++] = 0;
this._contents[this._contentsOffset++] = 2;
// write the value
int valueIndex = this._constantPool.literalIndex(this._value);
this._contents[this._contentsOffset++] = (byte) (valueIndex >> 8);
this._contents[this._contentsOffset++] = (byte) valueIndex;
writeBack(classFile);
}
/**
* Evaluate class level attribute(s).
*/
@Override
public void evaluate(Binding binding, LookupEnvironment environment, char[][][] missingTypeNames) {
if (CharOperation.equals(this._name, PLAYEDBY_NAME))
{
checkBindingMismatch(binding, ExtraCompilerModifiers.AccRole);
ReferenceBinding roleType = (ReferenceBinding)binding;
if (CharOperation.indexOf(ANCHOR_DELIM, this._value, true) > -1) {
// decode anchored type:
char[] typeName = singleRoleName(this._value);
char[][] anchorPath = anchorPath(this._value);
ReferenceBinding staticPart = null;
int i;
for (i=0; i<anchorPath.length-1; i++) {
ReferenceBinding envType = environment.askForType(CharOperation.subarray(anchorPath, 0, i+1), roleType.fPackage.enclosingModule);
if (envType != null)
staticPart = envType;
else if (staticPart != null)
break; // seen enough
}
if (staticPart == null) return; // no success!
ReferenceBinding currentType = staticPart;
ITeamAnchor anchor = null; // accumulate anchor path here
while (i<anchorPath.length) {
if (currentType instanceof BinaryTypeBinding && ((BinaryTypeBinding) currentType).version == 0) {
// necessary type on the path is not fully initialized (version is about the last field to be assigned in cachePartsFrom()).
roleType.roleModel.addAttribute(this);
return; // defer evaluation
}
FieldBinding f = currentType.getField(anchorPath[i], true);
if (f == null || !(f.type instanceof ReferenceBinding))
return; // not a valid anchor path.
currentType = (ReferenceBinding) f.type;
if (anchor == null)
anchor = f;
else
anchor = f.setPathPrefix(anchor);
i++;
}
if (anchor == null) return; // no success!
ReferenceBinding baseType = anchor.getMemberTypeOfType(typeName);
if (baseType == null) return; // no success!
roleType.baseclass = (ReferenceBinding) anchor.getRoleTypeBinding(baseType, null/*typeArguments*/, 0); // FIXME(SH) retrieve type arguments from attribute (need to store first ;-)
} else {
roleType.baseclass = getResolvedType(environment, toConstantPoolName(this._value), missingTypeNames);
}
roleType.baseclass.setIsBoundBase(roleType);
}
}
private char[] singleRoleName(char[] anchoredName) {
int pos = CharOperation.indexOf('<', this._value);
char[] typeName = CharOperation.subarray(this._value, 0, pos);
int dollar = CharOperation.indexOf('$', typeName);
if (dollar > -1)
typeName = CharOperation.subarray(typeName, dollar+1, -1);
return typeName;
}
private char[][] anchorPath(char[] anchoredName) {
int pos = CharOperation.indexOf('@', this._value);
char[] anchorName = CharOperation.subarray(this._value, pos+1, this._value.length-1);
return CharOperation.splitOn('.', anchorName);
}
/**
* Evaluate field level attribute.
*/
@Override
public boolean evaluate (FieldBinding binding) {
if (CharOperation.equals(this._name, FIELD_TYPE_ANCHOR)) {
FieldModel model = FieldModel.getModel(binding);
// store for further processing from BinaryTypeBinding.resolveTypesFor(FieldBinding)
model.typeAnchor = this._value;
return true;
}
return false;
}
@Override
public String toString()
{
return "OT-Attribute "+ //$NON-NLS-1$
new String(this._name)+": "+ //$NON-NLS-1$
new String(this._value);
}
}