blob: cd566553aa465fdaaef43d7af76ee9e643f26f4c [file] [log] [blame]
/**********************************************************************
* This file is part of "Object Teams Development Tooling"-Software
*
* Copyright 2004, 2006 Fraunhofer Gesellschaft, Munich, Germany,
* for its Fraunhofer Institute for Computer Architecture and Software
* Technology (FIRST), Berlin, Germany and Technical University Berlin,
* Germany.
*
* 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: BuildManager.java 23451 2010-02-04 20:33:32Z 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.compiler.adaptor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.objectteams.otdt.core.compiler.IOTConstants;
import base org.eclipse.jdt.core.compiler.CategorizedProblem;
import base org.eclipse.jdt.internal.compiler.CompilationResult;
import base org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; // base-class of a role file
import base org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding;
import base org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import base org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
import base org.eclipse.jdt.internal.compiler.problem.AbortType;
import base org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
import base org.eclipse.objectteams.otdt.internal.core.compiler.lifting.LiftingEnvironment;
import base org.eclipse.jdt.internal.core.builder.IncrementalImageBuilder; // base-class of a role file
/**
* This Team observes the build/compile process and advises the IncrementalImageBuilder,
* when and what to recompile due to OT-specific dependencies.
*
* @author stephan
* @version $Id: BuildManager.java 23451 2010-02-04 20:33:32Z stephan $
*/
@SuppressWarnings("restriction")
public team class BuildManager extends CompilationThreadWatcher
{
public static int DEBUG = 0; // levels: 0 nothing, 1 some, 2 more.
/** Remember the (one) ImageBuilder role. */
ImageBuilder builder = null;
// ====== Data common for all roles (incl. accessors and life-cycle). ======
/** Manage cross-ref table for copied roles:
* PRODUCER: RoleCopyTracker.trackCopyRole()
* CONSUMER: ClassFileChangeTracker.nonStructuralChange()
*
* ENCODING: RoleTypeName -> Set<TeamSourceFileName> */
HashMap<String, Set<String>> roleToSubTeams = new HashMap<String, Set<String>>();
void recordCopiedRole(char[] roleName, char[] subTeamSourceFileName) {
String roleString = new String(roleName);
Set<String> teams = roleToSubTeams.get(roleString);
if (teams == null)
roleToSubTeams.put(roleString, teams = new HashSet<String>());
teams.add(new String(subTeamSourceFileName));
if (DEBUG >= 2)
System.out.println("role "+roleString+" is COPIED to "+new String(subTeamSourceFileName)); //$NON-NLS-1$//$NON-NLS-2$
}
// // debug helper
// void printCopyTable() {
// System.out.print("-------------------------");
// for (Map.Entry<String, Set<String>> entry : roleToSubTeams.entrySet()) {
// System.out.print("\nRole "+entry.getKey());
// for (String value : entry.getValue()) {
// System.out.print("\n\t"+value);
// }
// }
// System.out.println("\n-------------------------");
// }
/** If a tsuper role has non-structural changes, some sub-teams need to be recompiled.
* This set holds the teams to be recompiled.
*
* PRODUCERS: ClassFileChangeTracker.nonStructuralChange()
* ImageBuilder.shouldPreserveBinary() (during compile->finishedWith)
* CONSUMER: ImageBuilder.addAffectedTeamFiles()
*
* ENCODING: source file name
*/
HashSet<String> teamsToRecompile = new HashSet<String>();
/** Get and clear the set of teams waiting for recompilation. */
HashSet<String> fetchTeamsToRecompile() {
HashSet<String> result = teamsToRecompile;
teamsToRecompile = new HashSet<String>();
return result;
}
/** If a tsuper role has non-structural changes, some sub-teams need to be recompiled.
* This set holds the roles that should NOT be preserved.
* PRODUCER: ClassFileChangeTracker.nonStructuralChange()
* CONSUMER: shouldPreserveBinary() (via shouldCandidateBePreserved())
*
* ENCODING: Qualified Type Name. p.T$I, canonical form, i.e., __OT__ prefixes removed.
*/
HashSet<String> staleRoles = new HashSet<String>();
synchronized void initializeDependencyStorage() {
this.roleToSubTeams = new HashMap<String, Set<String>>();
this.teamsToRecompile = new HashSet<String>();
this.staleRoles = new HashSet<String>();
}
protected class BinaryType playedBy BinaryTypeBinding
{
/** Record if a given type depends on an unresolvable type.
* More specifically, we look for roles depending on unresolvable tsuper roles.
*/
superInterfaces <- replace superInterfaces;
callin ReferenceBinding[] superInterfaces() {
try {
return base.superInterfaces();
} catch (AbortCompilation as Abort abort) {
abort.referencedBinaries.add(this);
throw abort;
}
}
// ==== Callouts: ====
char[][] compoundName() -> get char[][] compoundName;
// DEBUGGING:
String internalName() -> char[] internalName()
with { result <- new String(result) }
}
protected class ClassScope playedBy ClassScope
{
@SuppressWarnings("decapsulation")
void connectTypeHierarchy() <- replace void connectTypeHierarchy();
@SuppressWarnings("basecall")
callin void connectTypeHierarchy() {
try {
base.connectTypeHierarchy();
} catch (org.eclipse.jdt.internal.compiler.problem.AbortType at) {
TypeDeclaration referenceContext = referenceContext();
SourceTypeBinding sourceType = referenceContext.binding;
at.updateContext(referenceContext, referenceCompilationUnit().compilationResult);
referenceContext.ignoreFurtherInvestigation = true;
if (sourceType.superInterfaces == null)
sourceType.superInterfaces = TypeBinding.NO_SUPERINTERFACES; // TODO(SH): recurse?
// don't rethrow, marking ignoreFurtherInvestigation is enough abortion
}
}
TypeDeclaration referenceContext() -> get TypeDeclaration referenceContext;
CompilationUnitDeclaration referenceCompilationUnit() -> CompilationUnitDeclaration referenceCompilationUnit();
}
protected class LiftingEnv playedBy LiftingEnvironment
{
callin void init(TypeDeclaration teamType) {
try {
base.init(teamType);
} catch (org.eclipse.jdt.internal.compiler.problem.AbortType at) {
teamType.ignoreFurtherInvestigation = true;
BuildManager.this.teamsToRecompile.add(String.valueOf(teamType.compilationResult.fileName));
throw at; // TODO(SH): might want to mark certain AT-instances as recoverable?
}
}
init <- replace init;
}
/** This role helps to link Problem->Abort->BinaryType,
* in order to determine whether a problem was caused by an unresolvable tsuper role.
*/
protected class Abort playedBy AbortCompilation
{
public List<BinaryType> referencedBinaries = new ArrayList<BinaryType>();
/** Trigger: this role has more to be updated from the context. */
void updateContext(ASTNode astNode, CompileResult unitResult)
<- after void updateContext(ASTNode astNode, CompilationResult unitResult);
/**
* If the current abort exception could possibly be fixed by a recompile,
* + convert the exception onto a less drastic AbortType
* + record the link Problem->Abort.
*/
public void updateContext(ASTNode astNode, CompileResult unitResult) {
Problem problem = getProblem();
if ( problem != null
&& problem.couldBeFixedByRecompile()
&& unitResult.isReusingBinaryMember())
{
// convert AbortCompilation into AbortType
Abort abort = new AbortType(
new org.eclipse.jdt.internal.compiler.problem.AbortType(
unitResult, problem));
abort.referencedBinaries = this.referencedBinaries;
problem.abortException = abort;
RuntimeException ex = abort;
throw ex;
}
}
protected Problem getProblem() -> get CategorizedProblem problem;
}
protected class AbortType extends Abort playedBy AbortType
{
// // FIXME : this ctor gives a VerifyError:
// AbortType (CompileResult result, Problem problem) {
// base(result, problem);
// problem.abortException = this;
// }
@Override
public void updateContext(ASTNode astNode, CompileResult unitResult) {
// no super call: would cause recursive creation of roles
Problem problem = getProblem();
if (problem != null) {
problem.abortException = this;
}
}
}
/** Intermediate purely callout role. */
protected class CompileResult playedBy CompilationResult
{
protected // don't publically export protected role Problem
Problem[] problems() -> get CategorizedProblem[] problems;
// TODO(SH): workaround for lowering problem concerning base call in ImageBuilder.shouldPreserveBinary()
boolean hasBinaryMember(char[] typeName) -> boolean hasBinaryMember(char[] typeName);
@SuppressWarnings({"decapsulation", "rawtypes"})
protected
boolean isReusingBinaryMember() -> get ArrayList binaryMemberNames
with { result <- !(result == null || result.isEmpty()) }
char[] getFileName() -> char[] getFileName();
// debugging:
toString => toString;
}
/** Let a problem know about the associated exception. */
protected class Problem playedBy CategorizedProblem
{
public Abort abortException = null;
public char[] typeToRemove= null;
/** Certain problem reasons might be fixed by recompilation. */
protected boolean couldBeFixedByRecompile() {
switch (getID()) {
case IProblem.IsClassPathCorrect:
case IProblem.StaleTSuperRole:
case IProblem.StaleSubRole:
case IProblem.MissingRoleInBinaryTeam:
case IProblem.RoleFileInBinaryTeam:
case IProblem.CorruptBytecode:
case IProblem.MissingAccessorInBinary:
case IProblem.MismatchingRoleParts:
case IProblem.InconsistentlyResolvedRole:
case IProblem.NotGeneratingCallinBinding:
return true;
default:
return false;
}
}
int getID() -> int getID();
// debugging:
toString => getMessage;
}
/** Watch specific error reports. */
protected class ProblemReporter playedBy ProblemReporter {
// a missing role in a binary team means the binary team should probably be deleted.
void missingRoleInBinaryTeam(ReferenceBinding type)
<- replace void missingRoleInBinaryTeam(char[] roleName, ReferenceBinding enclosingTeam)
with { type <- enclosingTeam }
callin void missingRoleInBinaryTeam(ReferenceBinding type) {
ReferenceContext context= getReferenceContext();
try {
base.missingRoleInBinaryTeam(type);
} finally {
if (context != null && context.compilationResult() != null) {
int count= context.compilationResult().problemCount;
recordTypeToRemove(context.compilationResult().problems[count-1],
type.constantPoolName());
}
}
}
ReferenceContext getReferenceContext() -> get ReferenceContext referenceContext;
}
// ---- ImageBuilder is a role file ----
// ---- Team level features: ----
void recordTypeToRemove(CategorizedProblem as Problem prob, char[] roleName) {
prob.typeToRemove= roleName;
}
public static boolean isPredefinedRole(char[] roleName) {
int dollarPos = CharOperation.lastIndexOf('$', roleName);
if (dollarPos != -1)
roleName = CharOperation.subarray(roleName, dollarPos+1, -1);
return CharOperation.equals(roleName, IOTConstants.CONFINED)
|| CharOperation.equals(roleName, IOTConstants.OTCONFINED)
|| CharOperation.equals(roleName, IOTConstants.ICONFINED)
|| CharOperation.equals(roleName, IOTConstants.IBOUNDBASE)
|| CharOperation.equals(roleName, IOTConstants.ILOWERABLE);
}
/** Answer whether the given role type should be re-used (ask the ImageBuilder). */
public boolean shouldPreserveBinaryRole(ReferenceBinding role, CompilationResult as CompileResult cResult) {
if (builder == null)
return true;
return builder.shouldPreserveBinaryRole(role, cResult);
}
public String canonicalName(String roleName) {
return roleName.replace(IOTConstants.OT_DELIM, ""); //$NON-NLS-1$
}
}