use of com.jopdesign.common.MethodInfo in project jop by jop-devel.
the class InlineHelper method makePublic.
private void makePublic(ClassMemberInfo member) {
boolean wasPrivate = member.isPrivate();
// If we need to make it public, check if we need to make the class and all enclosing classes public too
ClassInfo cls = member.getClassInfo();
while (cls != null) {
if (!cls.isPublic()) {
cls.setAccessType(AccessType.ACC_PUBLIC);
}
cls = cls.getEnclosingClassInfo();
}
if (wasPrivate && member instanceof MethodInfo) {
// we are done here. if the method was private, there are no conflicting
// methods or we needed to rename it anyway.
member.setAccessType(AccessType.ACC_PUBLIC);
return;
}
// if we make a non-private method or any field public, need to go down to find all overriding
// members and make them public too
final MemberID memberID = member.getMemberID();
ClassVisitor visitor = new EmptyClassVisitor() {
@Override
public boolean visitClass(ClassInfo classInfo) {
ClassMemberInfo m = classInfo.getMemberInfo(memberID);
if (m == null) {
return true;
}
if (m.isPublic()) {
// we do not need to go further down if we find a public member
return false;
}
m.setAccessType(AccessType.ACC_PUBLIC);
return true;
}
};
new ClassHierarchyTraverser(visitor).traverseDown(member.getClassInfo());
}
use of com.jopdesign.common.MethodInfo in project jop by jop-devel.
the class InlineHelper method checkCode.
/**
* check the code of the invoked method if it contains instructions which prevent inlining.
*
* @param invoker the method into which the invokee will be inlined.
* @param invokee the invoked method.
* @return true if the code can be inlined and {@link #prepareInlining(MethodInfo, MethodInfo)} will succeed.
*/
private boolean checkCode(MethodInfo invoker, MethodInfo invokee) {
MethodCode code = invokee.getCode();
// Go through code, check for access to fields and invocations
for (InstructionHandle ih : code.getInstructionList(true, false).getInstructionHandles()) {
Instruction instr = ih.getInstruction();
if (instr instanceof InvokeInstruction) {
InvokeSite invokeSite = code.getInvokeSite(ih);
MethodRef ref = invokeSite.getInvokeeRef();
MethodInfo method = ref.getMethodInfo();
if (method == null) {
// TODO perform basic check on classnames if invoked method must already be public?
return false;
}
// invokespecial is somewhat, well, special..
if (invokeSite.isInvokeSpecial()) {
if (checkInvokeSpecial(invoker, invokee, invokeSite, ref.getClassInfo(), method) == CheckResult.SKIP) {
return false;
}
} else {
// check if fields need to be set to public
if (checkNeedsPublic(invoker, invokee, ref.getClassInfo(), method) == CheckResult.SKIP) {
return false;
}
}
} else if (instr instanceof FieldInstruction) {
FieldRef ref = code.getFieldRef(ih);
FieldInfo field = ref.getFieldInfo();
if (field == null) {
// TODO perform basic check on classnames if field is already public?
return false;
}
// check if fields need to be set to public
if (checkNeedsPublic(invoker, invokee, ref.getClassInfo(), field) == CheckResult.SKIP) {
return false;
}
}
}
return true;
}
use of com.jopdesign.common.MethodInfo in project jop by jop-devel.
the class InlineHelper method prepareInlining.
/**
* Prepare the invokee for inlining into the invokesite, by widening access restrictions or renaming
* methods to prevent incorrect method resolving.
* <p>
* This may change the code of the invokee, so this needs to be done before inlining the code.
* The CFG of the invokee will be removed.
* </p><p>
* This code assumes that {@link #canInline(CallString, InvokeSite, MethodInfo)} returned true for this invoke.
* </p>
*
* @param invoker the method where the code will be inlined to.
* @param invokee the method to inline.
*/
public void prepareInlining(MethodInfo invoker, MethodInfo invokee) {
MethodCode code = invokee.getCode();
InstructionList il = code.getInstructionList();
for (InstructionHandle ih : il.getInstructionHandles()) {
Instruction instr = ih.getInstruction();
if (instr instanceof InvokeInstruction) {
InvokeSite invokeSite = code.getInvokeSite(ih);
MethodRef ref = invokeSite.getInvokeeRef();
MethodInfo method = ref.getMethodInfo();
// we already checked that everything can be resolved
// nothing special to do for invokespecial here (checkInvokeSpecial only skips, no special return codes)
// check what we need to do
CheckResult rs = checkNeedsPublic(invoker, invokee, ref.getClassInfo(), method);
if (rs == CheckResult.NEEDS_PUBLIC) {
makePublic(method);
}
if (rs == CheckResult.NEEDS_PUBLIC_RENAME) {
if (method.isPrivate()) {
// TODO check the class for invokers, change to invokevirtual
} else {
// if the method is package visible, we need to rename all overriding methods
// too (but not methods from subclasses in different packages which do not override this)
// TODO update overriding methods
// TODO need to update all possible call sites
}
makePublic(method);
throw new AppInfoError("Implement me!");
}
} else if (instr instanceof FieldInstruction) {
FieldRef ref = code.getFieldRef(ih);
FieldInfo field = ref.getFieldInfo();
// we already checked that everything can be resolved
// check if fields need to be set to public
CheckResult rs = checkNeedsPublic(invoker, invokee, ref.getClassInfo(), field);
if (rs == CheckResult.NEEDS_PUBLIC) {
makePublic(field);
}
if (rs == CheckResult.NEEDS_PUBLIC_RENAME) {
throw new AppInfoError("Invalid returncode: renaming of fields not required");
}
}
}
}
use of com.jopdesign.common.MethodInfo in project jop by jop-devel.
the class InlineHelper method checkNeedsPublic.
/**
* Check if access of a field or method must/can be set to public.
*
* @param invoker the method containing the invocation to be checked.
* @param invoked the invoked method to inline.
* @param classInfo the class accessed in the invoked method.
* @param memberInfo the modifier info of the accessed object.
* @return a CheckResult status defining what should be done about the member access.
*/
private CheckResult checkNeedsPublic(MethodInfo invoker, MethodInfo invoked, ClassInfo classInfo, ClassMemberInfo memberInfo) {
// don't inline if the original code contains access errors
if (!invoked.canAccess(memberInfo)) {
return CheckResult.SKIP;
}
// if the invoker can access the member, everything is fine
if (invoker.canAccess(memberInfo)) {
return CheckResult.OK;
}
// need to be changed to public, check if this can be done
if (!inlineConfig.allowChangeAccess()) {
return CheckResult.SKIP;
}
// fields are not virtually resolved
if (memberInfo instanceof MethodInfo) {
MethodInfo method = (MethodInfo) memberInfo;
// check if full class hierarchy is known, else the method may be overwritten if set to public by an unkonwn class.
if (jcopter.getJConfig().doAssumeIncompleteAppInfo() && !memberInfo.getClassInfo().isFinal()) {
return CheckResult.SKIP;
}
// search all subclasses for same method
List<ClassInfo> queue = new LinkedList<ClassInfo>(method.getClassInfo().getDirectSubclasses());
while (!queue.isEmpty()) {
ClassInfo cls = queue.remove(0);
MethodInfo subMethod = cls.getMethodInfo(method.getMethodSignature());
if (subMethod != null) {
// We can simply make private methods public, but we must not change call sites to invokevirtual
if (method.getAccessType() == AccessType.ACC_PACKAGE && !subMethod.overrides(method, false)) {
// Need to rename method and all its overriding methods, but not the not-overriding subMethod (or vice-versa)
return inlineConfig.allowRename() ? CheckResult.NEEDS_PUBLIC_RENAME : CheckResult.SKIP;
}
}
}
}
return CheckResult.NEEDS_PUBLIC;
}
use of com.jopdesign.common.MethodInfo in project jop by jop-devel.
the class MethodCacheAnalysis method findRemovedMethods.
private Map<ExecutionContext, Set<MethodInfo>> findRemovedMethods(Set<ExecutionContext> roots, Collection<MethodInfo> removed) {
Map<ExecutionContext, Set<MethodInfo>> removeMethods = new LinkedHashMap<ExecutionContext, Set<MethodInfo>>();
LinkedHashSet<ExecutionContext> queue = new LinkedHashSet<ExecutionContext>(roots);
while (!queue.isEmpty()) {
ExecutionContext node = queue.iterator().next();
queue.remove(node);
boolean changed = false;
boolean isRoot = roots.contains(node);
boolean isNew = false;
// we initialize (lazily) by assuming that all removed methods are no longer reachable in any node,
// and then removing entries from the set if they are found to be still reachable.
// This ensures that the size of the sets only decreases and we eventually reach a fixpoint
Set<MethodInfo> set = removeMethods.get(node);
if (set == null) {
set = new LinkedHashSet<MethodInfo>(removed.size());
removeMethods.put(node, set);
for (MethodInfo m : removed) {
// initially add method to remove set if it is reachable from this node
if (reachableMethods.get(node).contains(m)) {
set.add(m);
}
}
changed = true;
isNew = true;
}
// be removed from this node
for (MethodInfo r : removed) {
// already removed
if (!set.contains(r))
continue;
for (ExecutionContext child : callGraph.getChildren(node)) {
// we ignore native methods in the cache analysis
if (child.getMethodInfo().isNative())
continue;
// skip childs which will be removed
if (isRoot && removed.contains(child.getMethodInfo()))
continue;
// TODO this is incorrect for cyclic call graphs.. need to fix this!
if (reachableMethods.get(child).contains(r)) {
set.remove(r);
changed = true;
}
}
}
if (isNew && set.isEmpty()) {
// we did not remove anything here and we did not visit the parents yet, so nothing changes
changed = false;
}
if (changed) {
// we have found more methods, need to update parents
queue.addAll(callGraph.getParents(node));
}
}
return removeMethods;
}
Aggregations