Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@
*
* @author Shai Almog
*/
public class BytecodeMethod implements SignatureSet {
public class BytecodeMethod implements SignatureSet {
private static MethodDependencyGraph dependencyGraph;

/**
* @return the acceptStaticOnEquals
Expand All @@ -76,6 +77,10 @@ public static boolean isAcceptStaticOnEquals() {
public static void setAcceptStaticOnEquals(boolean aAcceptStaticOnEquals) {
acceptStaticOnEquals = aAcceptStaticOnEquals;
}

public static void setDependencyGraph(MethodDependencyGraph dependencyGraph) {
BytecodeMethod.dependencyGraph = dependencyGraph;
}
private List<ByteCodeMethodArg> arguments = new ArrayList<ByteCodeMethodArg>();
private Set<LocalVariable> localVariables = new HashSet<LocalVariable>();
private ByteCodeMethodArg returnType;
Expand Down Expand Up @@ -238,6 +243,10 @@ public BytecodeMethod(String clsName, int access, String name, String desc, Stri
}
currentArrayDim = 0;
}

if (dependencyGraph != null) {
dependencyGraph.registerMethod(this);
}
}

// use this instead of isMethodUsed to compare traditional with new results
Expand Down Expand Up @@ -350,20 +359,40 @@ public boolean isMethodUsedByNative(String[] nativeSources, ByteCodeClass cls) {

private Set<String> usedMethods;
public boolean isMethodUsedOldWay(BytecodeMethod bm) {
if(usedMethods == null) {
usedMethods = new TreeSet<String>();
for(Instruction ins : instructions) {
String s = ins.getMethodUsed();
if(s != null && !usedMethods.contains(s)) {
usedMethods.add(s);
}
}
}
ensureUsedMethodsInitialized();
if(bm.methodName.equals("__INIT__")) {
return usedMethods.contains(bm.desc + ".<init>");
}
return usedMethods.contains(bm.desc + "." + bm.methodName);
}

public Set<String> getCalledMethodSignatures() {
ensureUsedMethodsInitialized();
return usedMethods;
}

public String getLookupSignature() {
if(methodName.equals("__INIT__")) {
return desc + ".<init>";
}
if(methodName.equals("__CLINIT__")) {
return desc + ".<clinit>";
}
return desc + "." + methodName;
}

private void ensureUsedMethodsInitialized() {
if(usedMethods != null) {
return;
}
usedMethods = new TreeSet<String>();
for(Instruction ins : instructions) {
String s = ins.getMethodUsed();
if(s != null && !usedMethods.contains(s)) {
usedMethods.add(s);
}
}
}

public void findWritableFields(Set<String> outSet) {
int len = instructions.size();
Expand Down Expand Up @@ -950,32 +979,48 @@ public String getMethodName() {
}

public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof BytecodeMethod)) {
return false;
}

BytecodeMethod bm = (BytecodeMethod)o;
int val = bm.methodName.compareTo(methodName);
if(val != 0) {

if (!methodName.equals(bm.methodName)) {
return false;
}
if(acceptStaticOnEquals) {
if(bm.arguments.size() != arguments.size()) {
if (acceptStaticOnEquals) {
if (bm.arguments.size() != arguments.size()) {
return false;
}
}
} else {
if(staticMethod || bm.staticMethod || bm.arguments.size() != arguments.size()) {
if (staticMethod || bm.staticMethod || bm.arguments.size() != arguments.size()) {
return false;
}
}
for(int iter = 0 ; iter < arguments.size() ; iter++) {

for (int iter = 0; iter < arguments.size(); iter++) {
ByteCodeMethodArg arg1 = arguments.get(iter);
ByteCodeMethodArg arg2 = bm.arguments.get(iter);
if(!arg1.equals(arg2)) {
if (!arg1.equals(arg2)) {
return false;
}
}

if (returnType == null) {
return bm.returnType == null;
}
return returnType.equals(bm.returnType);
}

public int hashCode() {
return methodName.hashCode();
int result = methodName == null ? 0 : methodName.hashCode();
result = 31 * result + arguments.size();
result = 31 * result + (acceptStaticOnEquals || !staticMethod ? 0 : 1);
result = 31 * result + (returnType == null ? 0 : returnType.hashCode());
return result;
}

public boolean isStatic() {
Expand Down Expand Up @@ -1049,6 +1094,12 @@ public void setMaxes(int maxStack, int maxLocals) {
private void addInstruction(Instruction i) {
instructions.add(i);
i.addDependencies(dependentClasses);
if (dependencyGraph != null) {
String methodUsed = i.getMethodUsed();
if (methodUsed != null) {
dependencyGraph.recordMethodCall(this, methodUsed);
}
}
}

public void addVariableOperation(int opcode, int var) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Codename One designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/

package com.codename1.tools.translator;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* Maintains a hierarchical dependency index between classes and methods so we
* can cheaply look up callers and prune eliminated elements without rescanning
* bytecode.
*/
public class MethodDependencyGraph {
private final Map<String, Set<BytecodeMethod>> callersByLookupSignature = new HashMap<String, Set<BytecodeMethod>>();
private final Map<BytecodeMethod, Set<String>> methodToCalls = new IdentityHashMap<BytecodeMethod, Set<String>>();
private final Map<String, Set<BytecodeMethod>> methodsByClass = new HashMap<String, Set<BytecodeMethod>>();

private static Set<BytecodeMethod> newIdentitySet() {
return Collections.newSetFromMap(new IdentityHashMap<BytecodeMethod, Boolean>());
}

public void clear() {
callersByLookupSignature.clear();
methodToCalls.clear();
methodsByClass.clear();
}

public void registerMethod(BytecodeMethod method) {
Set<BytecodeMethod> byClass = methodsByClass.get(method.getClsName());
if (byClass == null) {
byClass = newIdentitySet();
methodsByClass.put(method.getClsName(), byClass);
}
byClass.add(method);
}

public void recordMethodCall(BytecodeMethod caller, String calleeSignature) {
Set<BytecodeMethod> callers = callersByLookupSignature.get(calleeSignature);
if (callers == null) {
callers = newIdentitySet();
callersByLookupSignature.put(calleeSignature, callers);
}
callers.add(caller);

Set<String> calls = methodToCalls.get(caller);
if (calls == null) {
calls = new HashSet<String>();
methodToCalls.put(caller, calls);
}
calls.add(calleeSignature);
}

public List<BytecodeMethod> getCallers(String calleeSignature) {
Set<BytecodeMethod> callers = callersByLookupSignature.get(calleeSignature);
if (callers == null) {
return new ArrayList<BytecodeMethod>();
}
return new ArrayList<BytecodeMethod>(callers);
}

public void removeMethod(BytecodeMethod method) {
Set<String> calls = methodToCalls.remove(method);
if (calls != null) {
for (String call : calls) {
Set<BytecodeMethod> callers = callersByLookupSignature.get(call);
if (callers != null) {
callers.remove(method);
if (callers.isEmpty()) {
callersByLookupSignature.remove(call);
}
}
}
}

Set<BytecodeMethod> byClass = methodsByClass.get(method.getClsName());
if (byClass != null) {
byClass.remove(method);
if (byClass.isEmpty()) {
methodsByClass.remove(method.getClsName());
}
}
}

public void removeClass(String clsName) {
Set<BytecodeMethod> methods = methodsByClass.remove(clsName);
if (methods != null) {
for (BytecodeMethod method : new ArrayList<BytecodeMethod>(methods)) {
removeMethod(method);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,20 @@ public class Parser extends ClassVisitor {
private String clsName;
private static String[] nativeSources;
private static List<ByteCodeClass> classes = new ArrayList<ByteCodeClass>();
private static MethodDependencyGraph dependencyGraph = new MethodDependencyGraph();
private int lambdaCounter;
public static void cleanup() {
nativeSources = null;
classes.clear();
LabelInstruction.cleanup();
nativeSources = null;
classes.clear();
dependencyGraph.clear();
BytecodeMethod.setDependencyGraph(null);
LabelInstruction.cleanup();
}
public static void parse(File sourceFile) throws Exception {
if(ByteCodeTranslator.verbose) {
System.out.println("Parsing: " + sourceFile.getAbsolutePath());
}
BytecodeMethod.setDependencyGraph(dependencyGraph);
ClassReader r = new ClassReader(new FileInputStream(sourceFile));
/*if(ByteCodeTranslator.verbose) {
System.out.println("Class: " + r.getClassName() + " derives from: " + r.getSuperName() + " interfaces: " + Arrays.asList(r.getInterfaces()));
Expand Down Expand Up @@ -409,8 +413,8 @@ public static void writeOutput(File outputDirectory) throws Exception {
}
}
}
// load the native sources (including user native code)

// load the native sources (including user native code)
// We need to load native sources before we clear any unmarked classes
// because native source may be the only thing referencing a class,
// and the class may be purged before it even has a shot.
Expand Down Expand Up @@ -499,8 +503,8 @@ private static int eliminateUnusedMethods(boolean forceFound, int depth) {
}

private static int cullMethods() {
int nfound = 0;
for(ByteCodeClass bc : classes) {
int nfound = 0;
for(ByteCodeClass bc : classes) {
bc.unmark();
if(bc.isIsInterface() || bc.getBaseClass() == null) {
continue;
Expand All @@ -519,14 +523,15 @@ private static int cullMethods() {
continue;
}
mtd.setEliminated(true);
dependencyGraph.removeMethod(mtd);
nfound++;
}
}

}
return nfound;
}

private static boolean isMethodUsedByBaseClassOrInterface(BytecodeMethod mtd, ByteCodeClass cls) {
boolean b = checkMethodUsedByBaseClassOrInterface(mtd, cls.getBaseClassObject());
if(b) {
Expand Down Expand Up @@ -596,6 +601,7 @@ private static int cullClasses(boolean found, int depth) {
int nfound = 0;
for (ByteCodeClass cls : removedClasses) {
nfound += cls.setEliminated(true);
dependencyGraph.removeClass(cls.getClsName());
}
classes = tmp;
return nfound + eliminateUnusedMethods(nfound > 0, depth + 1);
Expand All @@ -613,14 +619,13 @@ private static boolean isMethodUsed(BytecodeMethod m, ByteCodeClass cls) {
if (!m.isEliminated() && m.isMethodUsedByNative(nativeSources, cls)) {
return true;
}
for(ByteCodeClass bc : classes) {
for(BytecodeMethod mtd : bc.getMethods()) {
if(mtd.isEliminated() || mtd == m) {
continue;
}
if(mtd.isMethodUsed(m)) {
return true;
}
List<BytecodeMethod> callers = dependencyGraph.getCallers(m.getLookupSignature());
for (BytecodeMethod caller : callers) {
if(caller.isEliminated() || caller == m) {
continue;
}
if(caller.isMethodUsed(m)) {
return true;
}
}
return false;
Expand Down
Loading