blob: 9394e22f634d475e1a580c02097d1386c9bef01d [file] [log] [blame]
/**********************************************************************
* This file is part of "Object Teams Development Tooling"-Software
*
* Copyright 2004, 2016 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.model;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions.WeavingScheme;
import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
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.UnresolvedReferenceBinding;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
import org.eclipse.jdt.internal.compiler.util.Util;
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.CPTypeAnchorAttribute;
import org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.OTSpecialAccessAttribute;
import org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.ReferencedTeamsAttribute;
import org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.RoleFilesAttribute;
import org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.WordValueAttribute;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.Dependencies;
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.lookup.ITeamAnchor;
import org.eclipse.objectteams.otdt.internal.core.compiler.smap.LineNumberProvider;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.TypeAnalyzer;
/**
* Generalizes TeamModel and RoleModel
* @author stephan
*/
public class TypeModel extends ModelElement {
/* Constructor for regular instances. */
public TypeModel (TypeDeclaration ast) {
this._ast = ast;
int[] lineSeparatorPositions = ast.compilationResult.lineSeparatorPositions;
if (lineSeparatorPositions != null) {
this._maxLineNumber = Util.getLineNumber(ast.declarationSourceEnd, lineSeparatorPositions, 0, lineSeparatorPositions.length-1);
} else if ( !ast.isConverted
&& !ast.isGenerated
&& Dependencies.needMethodBodies(ast))
{
throw new InternalCompilerError("Unexpectedly missing line number information"); //$NON-NLS-1$
}
this._state = new StateMemento();
}
/* Constructor for regular instances. */
public TypeModel (ReferenceBinding bind) {
this._binding = bind;
this._state = new StateMemento();
}
/** clone constructor: */
public TypeModel (TypeModel prototype, ReferenceBinding protoBinding) {
this._binding = protoBinding;
this._state = prototype._state;
this._compilerVersion = prototype._compilerVersion;
this.weavingScheme = prototype.weavingScheme;
this._ast = prototype._ast;
}
/** Store the byte code attribute which maps constant pool entries (Class_info) to type anchors. */
private CPTypeAnchorAttribute _typeAnchors;
/** Mediate RoleFilesAttribute to RoleFileCache. */
public RoleFilesAttribute _roleFilesAttribute = null;
/** Backward reference for the Ast being wrapped. */
protected TypeDeclaration _ast;
protected ReferenceBinding _binding;
private HashSet<ReferenceBinding> _referencedTeams = null;
/** translation state of this model element */
final public StateMemento _state;
/** Record here all types which are defined local to a method (TypeDeclaration). */
private LinkedList<TypeDeclaration> _localPlainTypes = new LinkedList<TypeDeclaration>();
public int _maxLineNumber = 0;
/**
* Needed for JSR-045 support.
* Can be asked for new line numbers(above linenumberrange) and stores mapping.
*/
protected LineNumberProvider _lineNumberProvider;
/**
* Version number of the compiler that created a (binary) type;
*/
public int _compilerVersion;
WeavingScheme weavingScheme;
public void setCompilerVersion(int version, WeavingScheme weavingScheme) {
this._compilerVersion = version;
this.weavingScheme = weavingScheme;
}
public void setBinding (ReferenceBinding binding) {
this._binding = binding;
binding.model = this;
}
/**
* @return The class-part of the AST being wrapped. May be null!!
*/
public TypeDeclaration getAst() {
return this._ast;
}
public ReferenceBinding getBinding() {
if (this._binding == null && this._ast != null && this._ast.binding != null)
setBinding(this._ast.binding);
return this._binding;
}
/**
* Get all members which are not roles represented by their TypeModel.
* Considers Ast or Bindings, whatever is more appropriate.
* Note, that members could again be teams.
*
* @returns all members for this Team.
*/
public TypeModel[] getMembers()
{
List<TypeModel> list = new LinkedList<TypeModel>();
if(this._ast == null)
{
if ( this._binding.kind() == Binding.TYPE_PARAMETER
|| this._binding.kind() == Binding.WILDCARD_TYPE
|| this._binding.kind() == Binding.INTERSECTION_TYPE
|| this._binding.kind() == Binding.INTERSECTION_TYPE18)
return new TypeModel[0]; // has no members
if (this._binding.isUnresolvedType())
return new TypeModel[0]; // members unknown and cannot resolve here :(
//binary type
assert this._binding.isBinaryBinding();
// don't used memberTypes() as not to trigger resolving unneeded members.
// see tests.compiler.regegression.LookupTest.test044
ReferenceBinding[] memberBindings = ((BinaryTypeBinding)this._binding).unresolvedMemberTypes();
for (int i = 0; i < memberBindings.length; i++)
{
ReferenceBinding binding = memberBindings[i];
if (binding instanceof UnresolvedReferenceBinding)
continue; // has no model yet cannot handle yet.
if (binding.isTeam())
list.add(binding.getTeamModel());
else
list.add(binding.model);
}
}
else
{
TypeDeclaration[] members = this._ast.memberTypes;
if (members != null)
{
for (int idx = 0; idx<members.length; idx++)
{
TypeDeclaration decl = members[idx];
if (decl.isTeam())
list.add(decl.getTeamModel());
else if (decl.isRole())
list.add(decl.getRoleModel());
else
list.add(decl.getModel());
}
}
AbstractMethodDeclaration[] methods = this._ast.methods;
if (methods != null) {
for (int i=0; i<methods.length; i++) {
if (methods[i] != null && methods[i].scope != null) {
ClassScope[] scopes = methods[i].scope.getAllLocalTypes();
for (int j=0; j<scopes.length; j++) {
TypeDeclaration type = scopes[j].referenceContext;
if (type.isTeam()) // very unlikely ;-)
list.add(type.getTeamModel());
else if (type.isRole())
list.add(type.getRoleModel());
else
list.add(type.getModel());
}
}
// no scope could mean:
// 1. it's a <clinit>
// 2. we are before scope creation
// (which happens in completeTypeBindings(), last step of beginToCompile).
// This is ok, since then we only miss step RoleSplitter, but local types
// cannot be teams with roles anyway.
}
}
}
return list.toArray(new TypeModel[list.size()]);
}
public ReferenceBinding findType(LookupEnvironment environment, char[][] compoundName) {
char[][] myName = CharOperation.splitOn('/', this._binding.constantPoolName());
if ( this._binding.isLocalType()
&& CharOperation.equals(compoundName, myName))
return this._binding;
if ( this._binding.enclosingType() != null
&& this._binding.enclosingType().isRole())
return this._binding.enclosingType().roleModel.findType(environment, compoundName);
return environment.getType(compoundName);
}
public char[] getInternalName() {
if (this._ast != null)
return this._ast.name;
else
return this._binding.internalName();
}
public void cleanup() {
this._ast = null;
this._lineNumberProvider = null;
}
public void addLocalType(TypeDeclaration localType) {
this._localPlainTypes.add(localType);
}
// === state handling: ===
public int getState() {
return this._state.getState();
}
/**
* Include local types in set state.
*/
public int setState(int state) {
int oldState= this._state.setState(state);
for (TypeDeclaration localType : this._localPlainTypes)
if (localType.getModel().getState() >= ITranslationStates.STATE_RESOLVED)
localType.getModel().setState(state);
this._state.runPendingJobs(state);
return oldState;
}
public void setMemberState(int state) {
TypeModel[] members = getMembers();
if (members != null)
{
for (int i=0; i<members.length; i++)
{
TypeModel member = members[i];
member.setMemberState(state);
member.setState(state);
}
}
}
public boolean isReadyToProcess(int state) {
return this._state.isReadyToProcess(state);
}
public boolean isTeam() {
return false;
}
public boolean isRole() {
if (this._binding != null)
return this._binding.isRole();
return this._ast.isRole();
}
protected String getKindString() {
return "Class "; //$NON-NLS-1$
}
@Override
@SuppressWarnings("nls")
public String toString() {
String name = (this._ast != null) ?
new String(this._ast.name) :
new String(this._binding.sourceName());
return getKindString()+" "+name
+ " (state: " + this._state +")";
}
/**
* Check whether a binding found by getTypeOrPackage is a team,
* and if so, record it as a referenced team of this type.
*
* @param binding
* @param scope site where reference was found
*/
public static void checkReferencedTeam(Binding binding, Scope scope) {
if (!(binding instanceof ReferenceBinding))
return;
ReferenceBinding type = (ReferenceBinding)binding;
if (!type.isTeam())
return;
// is binding an enclosing type of scope?
ReferenceBinding currentType = scope.enclosingSourceType();
while (currentType != null) {
if (currentType == binding)
return; // no need to record self-reference
currentType = currentType.enclosingType();
}
while (scope != null) {
TypeDeclaration site = scope.referenceType();
if (site == null)
return;
site.getModel().registerReferencedTeam(type);
scope = scope.parent;
}
}
/**
* Mark that the current class references the given team type.
* @param teamType
*/
private void registerReferencedTeam(ReferenceBinding teamType) {
if (TypeAnalyzer.isOrgObjectteamsTeam(teamType))
return; // optimize since Team has no bindings.
if (this._referencedTeams == null) {
this._referencedTeams = new HashSet<ReferenceBinding>();
addAttribute(new ReferencedTeamsAttribute(this));
}
this._referencedTeams.add(teamType);
}
/**
* Retrieve the gathered data about referenced teams.
* @return Object[] containing ReferenceBindings
*/
public ReferenceBinding[] getReferencedTeams() {
return this._referencedTeams.toArray(new ReferenceBinding[this._referencedTeams.size()]);
}
public boolean isIgnoreFurtherInvestigation() {
if (this._ast == null)
return false;
return TypeModel.isIgnoreFurtherInvestigation(this._ast);
}
public static boolean isIgnoreFurtherInvestigation(TypeDeclaration type)
{
if (type.ignoreFurtherInvestigation)
return true;
if ( type.compilationUnit != null
&& type.compilationUnit.ignoreFurtherInvestigation)
return true;
if ( type.scope != null
&& type.scope.cuIgnoreFurtherInvestigation())
return true;
if ( type.enclosingType != null
&& type.enclosingType.ignoreFurtherInvestigation)
return true;
if((type.bits & ASTNode.IsLocalType)!=0 && type.scope != null) {
MethodScope outerMostMethodScope = type.scope.outerMostMethodScope();
if (outerMostMethodScope != null) {
if (outerMostMethodScope.referenceContext instanceof AbstractMethodDeclaration)
{ // weired: MethodScope.referenceContext can be a TypeDeclaration!?!
AbstractMethodDeclaration method = (AbstractMethodDeclaration)outerMostMethodScope.referenceContext;
if ( method != null
&& method.ignoreFurtherInvestigation)
return true;
}
}
}
return false;
}
/**
* Has this type been compiled with an old compiler,
* meaning byte code is not compatible to current commpiler?
*
* Should only be used when a problem has already been detected,
* because we might be over-cautious here which means a <code>true</code>
* answer from this method does not necessarily imply actual incompatibility.
*/
public boolean isIncompatibleCompilerVersion() {
// require major and minor to match the current compiler, for now.
int minVersion = (IOTConstants.OTVersion.getMajor() << 9)
+ (IOTConstants.OTVersion.getMinor() << 5);
return this._compilerVersion < minVersion;
}
public CPTypeAnchorAttribute getTypeAnchors() {
return this._typeAnchors;
}
/**
* Note: this setter is overridden in RoleModel
*/
public void setTypeAnchors(CPTypeAnchorAttribute attribute) {
this._typeAnchors = attribute;
}
public void addTypeAnchor(ITeamAnchor anchor, int cpIndex) {
if (this._typeAnchors == null) {
this._typeAnchors = new CPTypeAnchorAttribute();
addAttribute(this._typeAnchors);
}
this._typeAnchors.addTypeAnchor(anchor, cpIndex);
}
/**
* Some attributes can only be evaluated after translation reaches a specific state
* at end of FAULT_IN_TYPES:
* CallinMethodMappingAttribute, CalloutMethodMappingAttribute
* task of LATE_ATTRIBUTES_EVALUATED:
* CopyInheritanceSrc (roles)
* CallinPrecedence (teams)
*/
public void evaluateLateAttributes(int state) {
try {
try {
if (this._binding != null && this._binding.isBinaryBinding()) {
if (this._attributes != null) {
for (int i = 0; i < this._attributes.length; i++) {
this._attributes[i].evaluateLateAttribute(this._binding, state);
}
}
}
} catch (AbortCompilation ac) {
// abort may e.g. happen if a binary tsuper role is inconsistent.
TypeDeclaration type = this._ast;
if (type == null) {
ReferenceBinding enclosing = this._binding.enclosingType();
if (enclosing != null && !enclosing.isBinaryBinding())
type = enclosing.model.getAst();
}
if (type != null)
ac.updateContext(type, type.compilationResult);
throw ac;
}
if (this._binding != null) {
ReferenceBinding[] memberTypes = this._binding.memberTypes();
if (memberTypes != null) {
for (int i = 0; i < memberTypes.length; i++) {
ModelElement.evaluateLateAttributes(memberTypes[i], state);
}
}
}
} finally {
// don't advance the state when working on behalf of another state
if (state == ITranslationStates.STATE_LATE_ATTRIBUTES_EVALUATED)
setState(ITranslationStates.STATE_LATE_ATTRIBUTES_EVALUATED);
}
}
// ==== allow to re-read bytes from the written class file ====
// See comment in ClassFile (near field "generatingModel").
protected String _classFilePath = null;
/** Store different elements accessed by this class, which require special treatment:
* methods and base-classes requiring decapsulation.
* fields requiring a getter and/or setter.
*/
protected OTSpecialAccessAttribute _specialAccess = null;
/**
* @param classFilePath
*/
public void setClassFilePath(String classFilePath) {
this._classFilePath = classFilePath;
}
public ClassFileReader read () throws IOException, ClassFormatException
{
if (this._classFilePath != null) {
FileNotFoundException fileNotFoundException = null;
for (int i=0; i<5; i++) { // make <= 5 attempts thus waiting <= 500 ms
try {
return ClassFileReader.read(this._classFilePath); // not recording attributes
} catch (FileNotFoundException ex) {
fileNotFoundException = ex;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
if (fileNotFoundException != null)
throw fileNotFoundException;
}
return null;
}
/**
* supports CALLIN_METHOD_MAPPINGS_ATTRIBUTE and STATE_REPLACE_BINDINGS_ATTRIBUTE
* @param superDeclaringType represents the type from which the attribute is being copied
* @param attributeName
*/
public void copyAttributeFrom(TypeModel superDeclaringType, char[] attributeName) {
if (superDeclaringType._attributes == null)
return;
for (int i = 0; i < superDeclaringType._attributes.length; i++) {
if (superDeclaringType._attributes[i].nameEquals(attributeName)) {
addOrMergeAttribute(superDeclaringType._attributes[i]);
return;
}
}
}
public void setSpecialAccess(OTSpecialAccessAttribute attribute) {
assert this._specialAccess == null;
this._specialAccess = attribute;
}
public OTSpecialAccessAttribute getSpecialAccessAttribute() {
if (this._specialAccess == null) {
this._specialAccess = new OTSpecialAccessAttribute(this._binding, getWeavingScheme());
addAttribute(this._specialAccess);
}
return this._specialAccess;
}
public LineNumberProvider getLineNumberProvider() {
if (this._lineNumberProvider == null)
this._lineNumberProvider = new LineNumberProvider(this._binding, this._maxLineNumber);
return this._lineNumberProvider;
}
public static LineNumberProvider getLineNumberProvider(TypeDeclaration type) {
if (type.isRole())
return type.getRoleModel().getLineNumberProvider();
if (type.isTeam())
return type.getTeamModel().getLineNumberProvider();
return type.getModel().getLineNumberProvider();
}
public static boolean isConverted(ReferenceBinding declaringClass) {
TypeDeclaration result;
if (declaringClass instanceof SourceTypeBinding) {
Scope scope = ((SourceTypeBinding) declaringClass).scope;
if (scope != null && scope.referenceType() != null)
return scope.referenceType().isConverted;
}
// TODO(SH): the rest of this method is probably obsoleted the above
if (declaringClass.isRole()) {
RoleModel roleModel = declaringClass.roleModel;
if (roleModel != null) {
if ( (result = roleModel.getClassPartAst()) != null
&& result.isConverted)
return true;
if ( (result = roleModel.getInterfaceAst()) != null
&& result.isConverted)
return true;
}
}
if (declaringClass.isTeam()) {
TeamModel teamModel = declaringClass.getTeamModel();
if (teamModel != null) {
if ( (result = teamModel.getAst()) != null
&& result.isConverted)
return true;
}
}
return false;
}
public WeavingScheme getWeavingScheme() {
if (this.weavingScheme == null && this._ast != null && this._ast.scope != null) {
this.weavingScheme = this._ast.scope.compilerOptions().weavingScheme;
if (this.weavingScheme == WeavingScheme.OTDRE) {
if (this._attributes != null)
for (int i = 0; i < this._attributes.length; i++)
if (this._attributes[i].nameEquals(IOTConstants.OT_COMPILER_VERSION)) {
((WordValueAttribute)this._attributes[i]).setWeavingScheme(this.weavingScheme);
break;
}
}
}
return this.weavingScheme;
}
}