(list.size());
62 | Handler cur = null;
63 | for (Handler h: list) {
64 | if (cur == null) {
65 | cur = h;
66 | newList.add(cur);
67 | continue;
68 | }
69 | // Two options here. Either h is contiguous with c or it isn't. Contiguous
70 | // means that it has to be the same type and the same catchBB and
71 | // from == to+1
72 | if (cur.type.equals(h.type) && (cur.catchBB == h.catchBB) && (h.from == cur.to + 1)) {
73 | cur.to = h.to;
74 | } else {
75 | cur = h;
76 | newList.add(cur);
77 | }
78 | }
79 | return newList;
80 | }
81 |
82 | }
--------------------------------------------------------------------------------
/src/kilim/analysis/IncompatibleTypesException.java:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2006, Sriram Srinivasan
2 | *
3 | * You may distribute this software under the terms of the license
4 | * specified in the file "License"
5 | */
6 |
7 | package kilim.analysis;
8 |
9 | public class IncompatibleTypesException extends Exception {
10 | public IncompatibleTypesException(String message) {
11 | super(message);
12 | }
13 | private static final long serialVersionUID = 1270645277746840738L;
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/src/kilim/analysis/NopInsn.java:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2006, Sriram Srinivasan
2 | *
3 | * You may distribute this software under the terms of the license
4 | * specified in the file "License"
5 | */
6 |
7 | package kilim.analysis;
8 |
9 | import static org.objectweb.asm.Opcodes.NOP;
10 |
11 | import java.util.Map;
12 |
13 | import org.objectweb.asm.MethodVisitor;
14 | import org.objectweb.asm.tree.AbstractInsnNode;
15 |
16 | class NopInsn extends AbstractInsnNode {
17 | public NopInsn() {
18 | super(NOP);
19 | }
20 |
21 | public int getType() {
22 | return 0;
23 | }
24 |
25 | @Override
26 | public void accept(MethodVisitor mv) {
27 | // Do nothing
28 | }
29 |
30 |
31 | @Override
32 | public String toString() {
33 | return "NOP";
34 | }
35 |
36 | @Override
37 | public AbstractInsnNode clone(@SuppressWarnings("rawtypes") Map labels) {
38 | return new NopInsn();
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/kilim/analysis/Range.java:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2006, Sriram Srinivasan
2 | *
3 | * You may distribute this software under the terms of the license
4 | * specified in the file "License"
5 | */
6 |
7 | package kilim.analysis;
8 |
9 | /**
10 | * Used by catch handlers to handle overlapping ranges
11 | *
12 | */
13 | public class Range {
14 | int from;
15 | int to;
16 |
17 | public Range(int aFrom, int aTo) {
18 | from = aFrom;
19 | to = aTo;
20 | }
21 |
22 | static Range intersect(int a1, int e1, int a2, int e2) {
23 | // a2 lies between a1 and e1 or a1 between a2 and e2
24 | // all comparisons are inclusive of endpoints
25 | assert a1 <= e1 && a2 <= e2;
26 | int a;
27 | if (a1 <= a2 && a2 <= e1) {
28 | a = a2;
29 | } else if (a2 <= a1 && a1 <= e2) {
30 | a = a1;
31 | } else {
32 | return null;
33 | }
34 | return new Range(a, e1 < e2 ? e1 : e2);
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/src/kilim/analysis/SAMweaver.java:
--------------------------------------------------------------------------------
1 | package kilim.analysis;
2 |
3 | import kilim.Constants;
4 | import kilim.mirrors.ClassMirror;
5 | import kilim.mirrors.ClassMirrorNotFoundException;
6 | import kilim.mirrors.Detector;
7 | import kilim.mirrors.MethodMirror;
8 |
9 | import org.objectweb.asm.ClassVisitor;
10 | import org.objectweb.asm.MethodVisitor;
11 | import org.objectweb.asm.Opcodes;
12 | import org.objectweb.asm.tree.MethodInsnNode;
13 |
14 | /**
15 | * {@code SAMweaver} generates code to support functional interfaces (also known
16 | * as SAM, for Single Abstract Method), where the SAM method is pausable. What
17 | * makes SAM interfaces special compared to regular interfaces is that they can
18 | * represent a lambda function in java8. More on this later.
19 | *
20 | * Imagine that a class called X
uses a I
of the
21 | * following form:
22 | *
23 | *
24 | * interface I {
25 | * int foo(double d) throws Pausable;
26 | * }
27 | *
28 | *
29 | * Since I
is a SAM, {@code CallWeaver} replaces all
30 | * invokeinterface I.foo(double)
with a static invocation of a tiny
31 | * wrapper method ('shim') in X like this:
32 | *
33 | *
34 | * invokestatic X.$shim$2(I callee, double d, Fiber f)
35 | *
36 | *
37 | * The shim method in turn turns around and calls I.foo(double)
:
38 | *
39 | *
40 | * private static int X.$shim$2(I callee, double d, Fiber f) {
41 | * int ret = callee.f(d, f);
42 | * f.setCallee(callee); // this is the purpose of the shim
43 | * return ret;
44 | * }
45 | *
46 | *
47 | * The purpose of {@code SAMweaver} is to generate the shim above.
48 | *
49 | * Why?
50 | *
51 | * Ordinarily, all hand-written code is modified by the weaver if it contains
52 | * definitions or invocations of pausable methods. Lambda expressions however
53 | * rely on the VM generating a class at run-time, which implements the
54 | * functional interface (I
in the example). The problem is that
55 | * this class needs to be woven to support kilim Fibers, but we don't have an
56 | * easy portable hook to weave it at run-time.
57 | *
58 | * As it turns out, practically all the weaving work is already complete at
59 | * compile time. This is because, the body of the lambda expression is already
60 | * available to the weaver as an ordinary method in the host class
61 | * X
, and is treated like any another pausable method. In other
62 | * words, the transformations at the calling site and in the body of the called
63 | * method are as usual.
64 | *
65 | * All that this is left for the shim to do is to capture the object's reference
66 | * (f.setCallee
); the fiber needs it while resuming. The call to
67 | * setCallee is redundant for ordinary hand-written implementations of SAM
68 | * interfaces, but is necessary if the implementation happens to be generated by
69 | * the VM as described above.
70 | *
71 | *
72 | * Of course, all this applies only if the functional method is pausable.
73 | */
74 |
75 | public class SAMweaver implements Constants {
76 | String interfaceName;
77 | String methodName;
78 | String desc;
79 | boolean itf;
80 | int index = -1;
81 |
82 | public SAMweaver(String interfaceName, String methodName, String desc,
83 | boolean itf) {
84 | this.interfaceName = interfaceName;
85 | this.methodName = methodName;
86 | this.desc = desc;
87 | this.itf = itf;
88 | }
89 |
90 | public void setIndex(int index) {
91 | this.index = index;
92 | }
93 |
94 | public boolean equals(Object obj) {
95 | if (obj instanceof SAMweaver) {
96 | SAMweaver that = (SAMweaver) obj;
97 | return desc.equals(that.desc) && methodName.equals(that.methodName)
98 | && interfaceName.equals(that.interfaceName);
99 | }
100 | return false;
101 | }
102 |
103 | public String toString() {
104 | return interfaceName+"."+methodName+desc + " ->" +getShimMethodName();
105 | }
106 |
107 | public int hashCode() {
108 | return methodName.hashCode() ^ desc.hashCode();
109 | }
110 |
111 | String getShimMethodName() {
112 | assert index >= 0;
113 | return "$shim$" + index;
114 | }
115 |
116 | String getShimDesc() {
117 | return this.desc.replace("(", "(" + TypeDesc.getInterned(this.interfaceName))
118 | .replace(")", Constants.D_FIBER_LAST_ARG);
119 | }
120 |
121 | /**
122 | * Generate a method like this:
123 | *
124 | *
125 | * private static $shim$1 (I callee, ...args..., f fiber) {
126 | * load each arg
127 | * call interface.method
128 | * f.setCallee(arg0)
129 | * xret
130 | * }
131 | *
132 | *
133 | * @param cv
134 | */
135 | public void accept(ClassVisitor cv) {
136 | String shimDesc = getShimDesc();
137 | MethodVisitor mv = cv.visitMethod(ACC_STATIC | ACC_PRIVATE, getShimMethodName(), shimDesc, null,
138 | getExceptions());
139 | // load arguments
140 | int ivar = 0;
141 | for (String argType : TypeDesc.getArgumentTypes(shimDesc)) {
142 | int vmt = VMType.toVmType(argType);
143 | mv.visitVarInsn(VMType.loadInsn[vmt], ivar);
144 | ivar += VMType.category[vmt]; // register width = 2 if double or
145 | // long, 1 otherwise
146 | }
147 | int fiberVar = ivar - 1;
148 |
149 | // invoke interface
150 | String fiberDesc = desc.replace(")", Constants.D_FIBER + ")");
151 | mv.visitMethodInsn(INVOKEINTERFACE, interfaceName, methodName, fiberDesc);
152 |
153 | // store callee object reference in fiber
154 | mv.visitVarInsn(ALOAD, fiberVar);
155 | mv.visitVarInsn(ALOAD, 0);
156 | mv.visitMethodInsn(INVOKEVIRTUAL, FIBER_CLASS, "setCallee",
157 | "(" + D_OBJECT + ")V");
158 |
159 | // return .. RETURN (if void) or ARETURN, IRETURN, etc.
160 | String retDesc = TypeDesc.getReturnTypeDesc(shimDesc);
161 | if (retDesc.charAt(0) == 'V') {
162 | mv.visitInsn(RETURN);
163 | } else {
164 | int vmt = VMType.toVmType(retDesc);
165 | mv.visitInsn(VMType.retInsn[vmt]);
166 | }
167 | mv.visitMaxs(0, 0); // maxLocals and maxStackDepth will be computed by asm's MethodWriter
168 | mv.visitEnd();
169 | }
170 |
171 | private String[] getExceptions() {
172 | try {
173 | ClassMirror cm = Detector.getDetector().classForName(interfaceName);
174 | for (MethodMirror m : cm.getDeclaredMethods()) {
175 | if (m.getName().equals(this.methodName)
176 | && m.getMethodDescriptor().equals(this.desc)) {
177 | // Convert dots to slashes.
178 | String[] ret = m.getExceptionTypes();
179 | if (ret != null) {
180 | for (int i = 0; i < ret.length; i++) {
181 | ret[i] = ret[i].replace('.', '/');
182 | }
183 | return ret;
184 | }
185 | break;
186 | }
187 | }
188 | } catch (ClassMirrorNotFoundException cmnfe) {
189 | }
190 |
191 | // Should not happen at this stage. If this class weren't found, it
192 | // would have created a
193 | // problem much earlier on.
194 | assert false : "Class Mirror not found for interface " + interfaceName;
195 | return new String[0];
196 | }
197 | }
--------------------------------------------------------------------------------
/src/kilim/analysis/TypeDesc.java:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2006, Sriram Srinivasan
2 | *
3 | * You may distribute this software under the terms of the license
4 | * specified in the file "License"
5 | */
6 |
7 | package kilim.analysis;
8 |
9 | import static kilim.Constants.D_BOOLEAN;
10 | import static kilim.Constants.D_BYTE;
11 | import static kilim.Constants.D_CHAR;
12 | import static kilim.Constants.D_DOUBLE;
13 | import static kilim.Constants.D_FLOAT;
14 | import static kilim.Constants.D_INT;
15 | import static kilim.Constants.D_LONG;
16 | import static kilim.Constants.D_NULL;
17 | import static kilim.Constants.D_OBJECT;
18 | import static kilim.Constants.D_SHORT;
19 | import static kilim.Constants.D_STRING;
20 | import static kilim.Constants.D_UNDEFINED;
21 |
22 | import java.lang.reflect.Field;
23 | import java.util.HashMap;
24 |
25 | import kilim.Constants;
26 | import kilim.mirrors.ClassMirrorNotFoundException;
27 | import kilim.mirrors.Detector;
28 |
29 | import org.objectweb.asm.Type;
30 |
31 | /**
32 | * A utility class that provides static methods for interning type strings and merging type
33 | * descriptors.
34 | *
35 | */
36 | public class TypeDesc {
37 | static final HashMap knownTypes = new HashMap(30);
38 |
39 | static {
40 | Field[] fields = Constants.class.getFields();
41 | try {
42 | for (int i = 0; i < fields.length; i++) {
43 | Field f = fields[i];
44 | if (f.getName().startsWith("D_")) {
45 | String val = (String) f.get(null);
46 | knownTypes.put(val, val);
47 | }
48 | }
49 | } catch (IllegalAccessException iae) {
50 | iae.printStackTrace();
51 | }
52 | knownTypes.put("java/lang/Object", D_OBJECT);
53 | knownTypes.put("java/lang/String", D_STRING);
54 | }
55 |
56 | static boolean isDoubleWord(String desc) {
57 | return (desc == D_DOUBLE || desc == D_LONG);
58 | }
59 |
60 | public static String getInterned(String desc) {
61 | String ret = knownTypes.get(desc);
62 | if (ret == null) {
63 | switch (desc.charAt(0)) {
64 | case 'L':
65 | case '[':
66 | return desc;
67 | default:
68 | return "L" + desc + ';';
69 | }
70 | } else {
71 | return ret;
72 | }
73 | }
74 |
75 | public static String getReturnTypeDesc(String desc) {
76 | return getInterned(desc.substring(desc.indexOf(")") + 1));
77 | }
78 |
79 | static boolean isSingleWord(String desc) {
80 | return !isDoubleWord(desc);
81 | }
82 |
83 | public static String getComponentType(String t) {
84 | if (t.charAt(0) != '[') {
85 | throw new InternalError("Can't get component type of " + t);
86 | }
87 | return getInterned(t.substring(1));
88 | }
89 |
90 | public static String getTypeDesc(Object object) {
91 | if (object instanceof Integer)
92 | return D_INT;
93 | if (object instanceof Long)
94 | return D_LONG;
95 | if (object instanceof Float)
96 | return D_FLOAT;
97 | if (object instanceof Double)
98 | return D_DOUBLE;
99 | if (object instanceof String)
100 | return D_STRING;
101 | if (object instanceof Boolean)
102 | return D_BOOLEAN;
103 | if (object instanceof Type)
104 | return TypeDesc.getInterned(((Type) object).getDescriptor());
105 | throw new InternalError("Unrecognized ldc constant: " + object);
106 | }
107 |
108 | private static int typelen(char[] buf, int off) {
109 | int start = off;
110 | switch (buf[off]) {
111 | case 'L':
112 | while (buf[off++] != ';') {}
113 | return off - start;
114 | case 'B':
115 | case 'C':
116 | case 'D':
117 | case 'F':
118 | case 'I':
119 | case 'J':
120 | case 'S':
121 | case 'Z':
122 | case 'V':
123 | return 1;
124 | case '[':
125 | return typelen(buf, off + 1) + 1;
126 | default:
127 | throw new InternalError("Unknown descriptor type: " + buf[0]);
128 | }
129 | }
130 |
131 | public static String[] getArgumentTypes(String methodDescriptor) {
132 | char[] buf = methodDescriptor.toCharArray();
133 | int size = getNumArgumentTypes(buf);
134 | String[] args = new String[size];
135 | size = 0;
136 | int off = 1;
137 | while (buf[off] != ')') {
138 | int len = typelen(buf, off);
139 | args[size] = getInterned(new String(buf, off, len));
140 | off += len;
141 | size += 1;
142 | }
143 | return args;
144 | }
145 |
146 | public static int getNumArgumentTypes(String desc) {
147 | return getNumArgumentTypes(desc.toCharArray());
148 | }
149 |
150 | public static int getNumArgumentTypes(char[] buf) {
151 | int off = 1;
152 | int size = 0;
153 | while (true) {
154 | if (buf[off] == ')') {
155 | break;
156 | }
157 | off += typelen(buf, off);
158 | size++;
159 | }
160 | return size;
161 | }
162 |
163 | /**
164 | * Given two type descriptors, it returns an appropriate merge: 1) If they are Array types, the
165 | * result is a an array of the merged component types 2) If they are ref types, it returns the
166 | * least common super type. If one of them is an interface, the result is D_OBJECT 3) All other
167 | * types must match exactly in order to not raise an error.
168 | */
169 |
170 | public static String mergeType(String a, String b) throws IncompatibleTypesException {
171 | // given: a and b are different.
172 | if (a == D_UNDEFINED)
173 | return b;
174 | if (b == D_UNDEFINED)
175 | return a;
176 | char ac = a.charAt(0);
177 | char bc = b.charAt(0);
178 | if (a == D_NULL) {
179 | assert b == D_NULL || bc == 'L' || bc == '[' : "merging NULL type with non ref type: "
180 | + b;
181 | return b;
182 | }
183 | if (b == D_NULL) {
184 | assert b == D_NULL || bc == 'L' || bc == '[' : "merging NULL type with non ref type: "
185 | + a;
186 | return a;
187 | }
188 | if (a == b || a.equals(b))
189 | return a;
190 | switch (ac) {
191 | case 'N': // D_NULL
192 | if (bc == 'L')
193 | return b;
194 | break;
195 | case 'L':
196 | if (bc == 'L') {
197 | return commonSuperType(a, b);
198 | } else if (bc == 'N') {
199 | return a;
200 | } else if (bc == '[') {
201 | return D_OBJECT; // common supertype of Ref and ArrayRef
202 | }
203 | break;
204 | case '[':
205 | if (bc == '[') {
206 | try {
207 | return "["
208 | + mergeType(TypeDesc.getComponentType(a), TypeDesc.getComponentType(b));
209 | } catch (IncompatibleTypesException ite) {
210 | // The component types are incompatible, but two disparate arrays still
211 | // inherit from Object
212 | return D_OBJECT;
213 | }
214 | } else if (bc == 'L') {
215 | return D_OBJECT; // common supertype of Ref and ArrayRef
216 | }
217 | break;
218 | case 'I':
219 | case 'Z':
220 | case 'B':
221 | case 'C':
222 | case 'S':
223 | // all int types are interchangeable
224 | switch (bc) {
225 | case 'I':
226 | case 'Z':
227 | case 'B':
228 | case 'C':
229 | case 'S':
230 | return D_INT;
231 | }
232 | break;
233 | }
234 | throw new IncompatibleTypesException("" + a + "," + b);
235 | }
236 |
237 | static String JAVA_LANG_OBJECT = "java.lang.Object";
238 |
239 | // public for testing purposes
240 | public static String commonSuperType(String oa, String ob) {
241 | try {
242 | if (oa == D_OBJECT || ob == D_OBJECT)
243 | return D_OBJECT;
244 | if (oa.equals(ob))
245 | return oa;
246 |
247 | String lub = Detector.getDetector()
248 | .commonSuperType(getInternalName(oa),
249 | getInternalName(ob));
250 |
251 | if (lub.equals("java/lang/Object"))
252 | return D_OBJECT;
253 | return "L" + lub + ";";
254 |
255 | } catch (ClassMirrorNotFoundException cnfe) {
256 | throw new InternalError(cnfe.getMessage());
257 | }
258 | }
259 |
260 | public static boolean isIntType(String typeDesc) {
261 | return (typeDesc == D_INT || typeDesc == D_CHAR || typeDesc == D_SHORT
262 | || typeDesc == D_BYTE || typeDesc == D_BOOLEAN);
263 | }
264 |
265 | public static boolean isRefType(String typeDesc) {
266 | char c = typeDesc.charAt(0);
267 | return typeDesc == D_NULL || c == '[' || c == 'L';
268 | }
269 |
270 | public static String getInternalName(String desc) {
271 | if (desc.charAt(0) == 'L') {
272 | return desc.substring(1, desc.length() - 1);
273 | } else {
274 | assert desc.charAt(0) == '[' : "Unexpected internal name " + desc;
275 | return desc;
276 | }
277 | }
278 |
279 | // public static void main(String[] args) throws Exception {
280 | // System.out.println(mergeType("Lkilim/test/ex/ExC;", "Lkilim/test/ex/ExD;"));
281 | // }
282 |
283 | }
--------------------------------------------------------------------------------
/src/kilim/analysis/Usage.java:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2006, Sriram Srinivasan
2 | *
3 | * You may distribute this software under the terms of the license
4 | * specified in the file "License"
5 | */
6 |
7 | package kilim.analysis;
8 |
9 | import java.util.ArrayList;
10 | import java.util.BitSet;
11 |
12 | /**
13 | * Each BasicBlock owns one instance of Usage. This class maintains, in essence, three vectors of
14 | * booleans, indexed by the local variable number. Since it is very rare for a method to have
15 | * more than 31 local variables, the vectors are represented by int bitmaps. For more than this, the
16 | * basic block creates an instance of BigUsage that is functionally identical (TODO)
17 | *
18 | * Note that we don't need to track usage of operand stack. All elements of the operand stack are
19 | * always live, and always need to be stored and restored (during stack switching). This is not true
20 | * of local vars; a var may have a valid value which may not be used downstream, so we track which
21 | * vars must be taken seriously.
22 | *
23 | * @see BasicBlock
24 | */
25 | public class Usage {
26 | /**
27 | * The number of local vars in the owning BB's frame
28 | */
29 | private int nLocals;
30 |
31 | /**
32 | * bit(i) == 1 (counting from LSB) if the ith local var is live downstream
33 | */
34 | private BitSet in;
35 |
36 | /**
37 | * use.bit(i) == 1 (from LSB) if the ith var is read before it has been written. The bit vector
38 | * as a whole represents the set of vars that the BB needs from its predecessors.
39 | */
40 | private BitSet use;
41 |
42 | /**
43 | * def.bit(i) == 1 (from LSB) if the ith var is written into before it has been read. It
44 | * represents all the vars that this BB is capable of supplying downstream on its own, hence
45 | * those vars are not required to be supplied by its predecessors (even if they do supply them,
46 | * they will be overwritten anyway).
47 | */
48 | private BitSet def;
49 |
50 | public Usage(int numLocals) {
51 | nLocals = numLocals;
52 | in = new BitSet(numLocals);
53 | use = new BitSet(numLocals);
54 | def = new BitSet(numLocals);
55 | }
56 |
57 | public void read(int var) {
58 | assert var < nLocals : "local var num=" + var + " exceeds nLocals = " + nLocals;
59 | if (!def.get(var)) {
60 | // is not def'd earlier
61 | use.set(var);
62 | }
63 | }
64 |
65 | public void write(int var) {
66 | assert var < nLocals : "local var num=" + var + " exceeds nLocals = " + nLocals;
67 | def.set(var);
68 | }
69 |
70 | /**
71 | * return true if var is live at the entrance to this BB.
72 | */
73 | public boolean isLiveIn(int var) {
74 | return in.get(var);
75 | }
76 |
77 | /**
78 | * This is the standard liveness calculation (Dragon Book, section 10.6). At each BB (and its
79 | * corresponding usage), we evaluate "in" using use and def. in = use U (out \ def) where out =
80 | * U succ.in, for all successors
81 | */
82 | public boolean evalLiveIn(ArrayList succUsage) {
83 | BitSet out = new BitSet(nLocals);
84 | BitSet old_in = (BitSet) in.clone();
85 | if (succUsage.size() == 0) {
86 | in = use;
87 | } else {
88 | // calculate out = U succ.in
89 | out = (BitSet) succUsage.get(0).in.clone();
90 | for (int i = 1; i < succUsage.size(); i++) {
91 | out.or(succUsage.get(i).in);
92 | }
93 | // calc out \ def == out & ~def == ~(out | def)
94 | BitSet def1 = (BitSet) def.clone();
95 | def1.flip(0, nLocals);
96 | out.and(def1);
97 | out.or(use);
98 | in = out;
99 | }
100 | return !(in.equals(old_in));
101 | }
102 |
103 | /**
104 | * Called to coalesce a successor's usage into the current BB. Important: This should be called
105 | * before live variable analysis begins, because we don't bother merging this.in.
106 | */
107 | void absorb(Usage succ) {
108 | BitSet b = (BitSet) this.def.clone();
109 | b.flip(0, nLocals);
110 | b.and(succ.use);
111 | this.use.or(b);
112 | this.def.or(succ.def);
113 | }
114 |
115 | public String toString() {
116 | StringBuffer sb = new StringBuffer();
117 | sb.append("use");
118 | printBits(sb, use);
119 | sb.append("def");
120 | printBits(sb, def);
121 | sb.append("in");
122 | printBits(sb, in);
123 | return sb.toString();
124 | }
125 |
126 | private void printBits(StringBuffer sb, BitSet b) {
127 | int numDefined = 0;
128 | for (int i = 0; i < nLocals; i++) {
129 | if (b.get(i))
130 | numDefined++;
131 | }
132 | sb.append('(').append(numDefined).append("): ");
133 | for (int i = 0; i < nLocals; i++) {
134 | if (b.get(i))
135 | sb.append(i).append(' ');
136 | }
137 | sb.append('\n');
138 | }
139 |
140 | /**
141 | * This is purely for testing purposes.
142 | *
143 | * @param var
144 | * local var index
145 | */
146 | public void setLiveIn(int var) {
147 | in.set(var);
148 | }
149 |
150 | Usage copy() {
151 | Usage ret = new Usage(nLocals);
152 | ret.use = use;
153 | ret.def = def;
154 | return ret;
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/src/kilim/analysis/Utils.java:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2006, Sriram Srinivasan
2 | *
3 | * You may distribute this software under the terms of the license
4 | * specified in the file "License"
5 | */
6 |
7 | package kilim.analysis;
8 |
9 | /**
10 | * Simple string utils for pretty printing support
11 | *
12 | */
13 | public class Utils {
14 | public static String indentStr = "";
15 | public static String spaces = " ";
16 |
17 | public static void indentWith(String s) {
18 | indentStr = indentStr + s;
19 | }
20 |
21 | public static void indent(int numSpaces) {
22 | indentWith(spaces.substring(0, numSpaces));
23 | }
24 |
25 | public static void dedent(int numSpaces) {
26 | indentStr = indentStr.substring(0, indentStr.length() - numSpaces);
27 | }
28 |
29 | public static String format(String s) {
30 | if (indentStr.length() == 0)
31 | return s;
32 | int i = s.indexOf('\n'); // i is always the index of newline
33 | if (i >= 0) {
34 | StringBuffer sb = new StringBuffer(100);
35 | sb.append(indentStr); // leading indent
36 | int prev = 0; // prev value of i in loop
37 | do {
38 | // copy from prev to i (including \n)
39 | sb.append(s, prev, i + 1);
40 | // add indentation wherever \n occurs
41 | sb.append(indentStr);
42 | prev = i + 1;
43 | if (prev >= s.length())
44 | break;
45 | i = s.indexOf('\n', prev);
46 | } while (i != -1);
47 | // copy left over chars from the last segment
48 | sb.append(s, prev, s.length());
49 | return sb.toString();
50 | } else {
51 | return indentStr + s;
52 | }
53 | }
54 |
55 | public static void resetIndentation() {
56 | indentStr = "";
57 | }
58 |
59 | public static void p(String s) {
60 | System.out.print(format(s));
61 | }
62 |
63 | public static void pn(String s) {
64 | System.out.println(format(s));
65 | }
66 |
67 | public static void pn(int i) {
68 | System.out.println(format("" + i));
69 | }
70 |
71 | public static void pn() {
72 | System.out.println();
73 | }
74 |
75 | public static void pn(Object o) {
76 | pn((o == null) ? "null" : o.toString());
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/kilim/analysis/Value.java:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2006, Sriram Srinivasan
2 | *
3 | * You may distribute this software under the terms of the license
4 | * specified in the file "License"
5 | */
6 |
7 | package kilim.analysis;
8 | import static kilim.Constants.D_UNDEFINED;
9 | import static kilim.Constants.D_NULL;
10 |
11 | import java.util.Arrays;
12 |
13 | /**
14 | * A SSA value that represents all objects produced at a particular
15 | * location in the code. Value objects are used by dataflow analysis
16 | * (@see BasicBlock)
17 | *
18 | */
19 | public class Value {
20 | public static Object NO_VAL = new Object();
21 | public static Value V_UNDEFINED = new Value(0, D_UNDEFINED, NO_VAL);
22 |
23 | private String typeDesc;
24 |
25 | private Object constVal;
26 |
27 | private int numSites;
28 | private int[] sites;
29 |
30 | public int getNumSites() {return numSites;}
31 |
32 | public int[] getCreationSites() {return sites;}
33 |
34 | public String getTypeDesc() {return typeDesc;}
35 |
36 | public Object getConstVal() {return constVal;}
37 |
38 | private Value(int aPos, String aDesc, Object aConst) {
39 | sites = new int[2];
40 | numSites = 1;
41 | sites[0] = aPos;
42 | typeDesc = aDesc;
43 | constVal = aConst;
44 | //System.out.println("V[" + aPos + ":" + aDesc + ((aConst == NO_VAL) ? "" : (": " + aConst)) + "]");
45 | }
46 |
47 | private Value(int newNumSites, int[] newSites, String newType, Object newConst) {
48 | Arrays.sort(newSites, 0, newNumSites);
49 | numSites = newNumSites;
50 | sites = newSites;
51 | typeDesc = newType;
52 | constVal = newConst;
53 | /*//debug
54 | StringBuilder sb = new StringBuilder(80);
55 | sb.append("V[");
56 | for (int i = 0; i < newNumSites; i++) {
57 | if (i > 0) sb.append(",");
58 | sb.append(newSites[i]);
59 | }
60 | sb.append(":").append(newType).append(":");
61 | if (newConst != NO_VAL) {
62 | sb.append(": ").append(newConst.toString());
63 | }
64 | sb.append("]");
65 | System.out.println(sb);
66 | */
67 | }
68 |
69 | /**
70 | * Produces a new value (if necessary), if the instructions are different or
71 | * the types are different. The types are merged to form a least common
72 | * upper bound, and the instruction sets are unioned.
73 | * @param vb
74 | * @return this if the result of the merge is no different, or the new value
75 | */
76 | public Value merge(Value other) {
77 | int[] newSites = new int[this.numSites + other.numSites];
78 | for (int i = 0; i < newSites.length; i++) newSites[i] = -1;
79 | int newNumSites = mergeSites(newSites, other);
80 | String newType;
81 | try {
82 | newType = TypeDesc.mergeType(this.typeDesc, other.typeDesc);
83 | } catch (IncompatibleTypesException e) {
84 | newType = D_UNDEFINED;
85 | }
86 | Object newConst = (constVal.equals(other.constVal)) ? constVal : NO_VAL;
87 | if (newNumSites != numSites || newType != typeDesc || newConst != constVal) {
88 | return new Value(newNumSites, newSites, newType, newConst);
89 | } else {
90 | return this; // no change
91 | }
92 | }
93 |
94 | private int mergeSites(int[]newSites, Value other) {
95 | int uniqueNumSites = 0;
96 | for (int i = 0; i < numSites; i++) {
97 | uniqueNumSites += addTo(newSites, sites[i]);
98 | }
99 | for (int i = 0; i < other.numSites; i++) {
100 | uniqueNumSites += addTo(newSites, other.sites[i]);
101 | }
102 | return uniqueNumSites;
103 | }
104 |
105 |
106 | private int addTo(int[] newSites, int site) {
107 | for (int i = 0; i < newSites.length; i++) {
108 | int s = newSites[i];
109 | if (s == -1) {
110 | newSites[i] = site;
111 | return 1; // added an element
112 | }
113 | if (s == site) return 0; // added no elements
114 | }
115 | return 0;
116 | }
117 |
118 | @Override
119 | public boolean equals(Object obj) {
120 | // TODO FIXME : This is WRONG. Two values can be created at the same site when
121 | // entering a method (all incoming parameter values are given location 0).
122 | // That would make two distinct params with the same type equal.
123 | if (this == obj) return true;
124 | Value other = (Value)obj;
125 | if (this.typeDesc.equals(other.typeDesc) &&
126 | this.constVal.equals(other.constVal) &&
127 | this.numSites == other.numSites) {
128 | // Check sites
129 | for (int i = 0; i < this.numSites; i++) {
130 | if (sites[i] != other.sites[i]) {
131 | return false;
132 | }
133 | }
134 | return true;
135 | }
136 | return false;
137 | }
138 |
139 | @Override
140 | public int hashCode() {
141 | int h = typeDesc.hashCode();
142 | for (int i = 0; i < numSites; i++) {
143 | h ^= sites[i];
144 | }
145 | return h;
146 | }
147 |
148 | public static Value make(int pos, String desc) {
149 | return new Value(pos, desc, NO_VAL);
150 | }
151 |
152 | public static Value make(int pos, String desc, Object aConstVal) {
153 | return new Value(pos, desc, aConstVal);
154 | }
155 |
156 | public boolean isCategory2() {
157 | return category() == 2;
158 | }
159 |
160 | public boolean isCategory1() {
161 | return category() == 1;
162 | }
163 |
164 | @Override
165 | public String toString() {
166 | if (numSites == 0 && typeDesc == D_UNDEFINED) return "undef";
167 | StringBuffer sb = new StringBuffer(40);
168 | sb.append(typeDesc).append('[');
169 | for (int i = 0; i < numSites; i++) {
170 | if (i > 0) sb.append(' ');
171 | sb.append(sites[i]);
172 | }
173 | sb.append(']');
174 | if (constVal != NO_VAL) {
175 | sb.append(" == ").append(constVal.toString());
176 | }
177 | return sb.toString();
178 | }
179 |
180 | public boolean isConstant() {
181 | return constVal != NO_VAL || typeDesc == D_NULL;
182 | }
183 |
184 | public int category() {
185 | return TypeDesc.isDoubleWord(typeDesc) ? 2 : 1;
186 | }
187 |
188 | }
189 |
--------------------------------------------------------------------------------
/src/kilim/mirrors/CachedClassMirrors.java:
--------------------------------------------------------------------------------
1 | package kilim.mirrors;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 | import java.util.concurrent.ConcurrentHashMap;
6 |
7 | import org.objectweb.asm.AnnotationVisitor;
8 | import org.objectweb.asm.Attribute;
9 | import org.objectweb.asm.ClassReader;
10 | import org.objectweb.asm.ClassVisitor;
11 | import org.objectweb.asm.FieldVisitor;
12 | import org.objectweb.asm.MethodVisitor;
13 | import org.objectweb.asm.Opcodes;
14 |
15 |
16 | /**
17 | * CachedClassMirrors caches information about a set of classes that are loaded through byte arrays, and which
18 | * are not already loaded by the classloader
19 | **/
20 |
21 | public class CachedClassMirrors implements Mirrors {
22 | final static String[] EMPTY_SET = new String[0];
23 |
24 | final RuntimeClassMirrors delegate;
25 | ConcurrentHashMap cachedClasses = new ConcurrentHashMap();
26 |
27 | public CachedClassMirrors(ClassLoader cl) {
28 | delegate = new RuntimeClassMirrors(cl);
29 | }
30 |
31 | @Override
32 | public ClassMirror classForName(String className)
33 | throws ClassMirrorNotFoundException {
34 | // defer to loaded class objects first, then to cached class mirrors.
35 | ClassMirror ret = cachedClasses.get(className);
36 |
37 | if (ret == null) {
38 | ret = delegate.classForName(className);
39 | }
40 | if (ret == null) {
41 | throw new ClassMirrorNotFoundException(className);
42 | }
43 | return ret;
44 | }
45 |
46 | @Override
47 | public ClassMirror mirror(Class> clazz) {
48 | // param is already a class; use the delegate to get the appropriate runtime mirror
49 | return delegate.mirror(clazz);
50 | }
51 |
52 | @Override
53 | public ClassMirror mirror(String className, byte[] bytecode) {
54 | // if it is loaded by the classLoader already, we will
55 | // not load the classNode, even if the bytes are different
56 | ClassMirror ret = null;
57 | if (!delegate.isLoaded(className)) {
58 | ret = new CachedClassMirror(bytecode);
59 | String name = ret.getName().replace('/', '.'); // Class.forName format
60 | this.cachedClasses.put(name, ret);
61 | }
62 | return ret;
63 | }
64 | }
65 |
66 | class CachedClassMirror extends ClassVisitor implements ClassMirror {
67 |
68 | String name;
69 | boolean isInterface;
70 | MethodMirror[] declaredMethods;
71 | String[] interfaceNames;
72 | String superName;
73 |
74 | private List tmpMethodList; //used only while processing bytecode.
75 |
76 | public CachedClassMirror(byte []bytecode) {
77 | super(Opcodes.ASM4);
78 | ClassReader cr = new ClassReader(bytecode);
79 | cr.accept(this, /*flags*/0);
80 | }
81 |
82 | @Override
83 | public String getName() {
84 | return name;
85 | }
86 |
87 | @Override
88 | public boolean isInterface() {
89 | return isInterface;
90 | }
91 |
92 | @Override
93 | public boolean equals(Object obj) {
94 | if (obj instanceof CachedClassMirror) {
95 | CachedClassMirror mirr = (CachedClassMirror) obj;
96 | return mirr.name == this.name && mirr.isInterface == this.isInterface;
97 | }
98 |
99 | return false;
100 | }
101 |
102 | @Override
103 | public int hashCode() {
104 | return this.name.hashCode();
105 | }
106 |
107 | @Override
108 | public MethodMirror[] getDeclaredMethods() {
109 | return (declaredMethods == null) ?
110 | new MethodMirror[0] : declaredMethods;
111 | }
112 |
113 | @Override
114 | public String[] getInterfaces() throws ClassMirrorNotFoundException {
115 | return interfaceNames;
116 | }
117 |
118 | @Override
119 | public String getSuperclass() throws ClassMirrorNotFoundException {
120 | return superName;
121 | }
122 |
123 | @Override
124 | public boolean isAssignableFrom(ClassMirror c) throws ClassMirrorNotFoundException {
125 | Detector d = Detector.getDetector();
126 | if (this.equals(c)) return true;
127 |
128 | ClassMirror supcl = d.classForName(c.getSuperclass());
129 | if (isAssignableFrom(supcl)) return true;
130 | for (String icl: c.getInterfaces()) {
131 | supcl = d.classForName(icl);
132 | if (isAssignableFrom(supcl))
133 | return true;
134 | }
135 | return false;
136 | }
137 |
138 |
139 | // ClassVisitor implementation
140 | public void visit(int version, int access, String name, String signature, String superName,
141 | String[] interfaces) {
142 | this.name = name;
143 | this.superName = superName;
144 | this.interfaceNames = interfaces == null ? CachedClassMirrors.EMPTY_SET : interfaces;
145 | this.isInterface = (access & Opcodes.ACC_INTERFACE) > 0;
146 | }
147 |
148 |
149 |
150 | public MethodVisitor visitMethod(int access, String name, String desc, String signature,
151 | String[] exceptions) {
152 | if (tmpMethodList == null) {
153 | tmpMethodList = new ArrayList();
154 | }
155 | tmpMethodList.add(new CachedMethodMirror(access, name, desc, exceptions));
156 | return null; // null MethodVisitor to avoid examining the instructions.
157 | }
158 |
159 | public void visitEnd() {
160 | if (tmpMethodList != null) {
161 | declaredMethods = new MethodMirror[tmpMethodList.size()];
162 | int i = 0;
163 | for (MethodMirror mm: tmpMethodList) {
164 | declaredMethods[i++] = mm;
165 | }
166 | tmpMethodList = null;
167 | }
168 | }
169 |
170 | // Dummy methods
171 |
172 | public void visitSource(String source, String debug) {}
173 | public void visitOuterClass(String owner, String name, String desc) {}
174 | public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
175 | return DummyAnnotationVisitor.singleton;
176 | }
177 | public void visitAttribute(Attribute attr) {}
178 | public void visitInnerClass(String name, String outerName, String innerName, int access) {}
179 | public FieldVisitor visitField(int access, String name, String desc, String signature,
180 | Object value) {
181 | return null;
182 | }
183 | static class DummyAnnotationVisitor extends AnnotationVisitor {
184 | public DummyAnnotationVisitor() {
185 | super(Opcodes.ASM4);
186 | }
187 | static DummyAnnotationVisitor singleton = new DummyAnnotationVisitor();
188 | public void visit(String name, Object value) {}
189 | public AnnotationVisitor visitAnnotation(String name, String desc) {return this;}
190 | public AnnotationVisitor visitArray(String name) {return DummyAnnotationVisitor.singleton;}
191 | public void visitEnd() {}
192 | public void visitEnum(String name, String desc, String value) {}
193 | }
194 | }
195 |
196 | class CachedMethodMirror implements MethodMirror {
197 |
198 | private String[] exceptions;
199 | private String desc;
200 | private String name;
201 | private int modifiers;
202 | private boolean isBridge;
203 |
204 | public CachedMethodMirror(int modifiers, String name, String desc, String[] exceptions) {
205 | this.modifiers = modifiers;
206 | this.name = name;
207 | this.desc = desc;
208 | this.exceptions = (exceptions == null) ? CachedClassMirrors.EMPTY_SET : exceptions;
209 | isBridge = (modifiers & Opcodes.ACC_BRIDGE) > 0;
210 | }
211 |
212 | public String getName() {
213 | return name;
214 | }
215 |
216 | public String[] getExceptionTypes() {
217 | return exceptions;
218 | }
219 |
220 | public String getMethodDescriptor() {
221 | return desc;
222 | }
223 |
224 | public boolean isBridge() {
225 | return isBridge;
226 | }
227 |
228 | public int getModifiers() {
229 | return modifiers;
230 | }
231 | }
232 |
233 |
234 |
--------------------------------------------------------------------------------
/src/kilim/mirrors/ClassMirror.java:
--------------------------------------------------------------------------------
1 | package kilim.mirrors;
2 |
3 | public interface ClassMirror {
4 |
5 | public abstract MethodMirror[] getDeclaredMethods();
6 |
7 | public abstract boolean isAssignableFrom(ClassMirror c) throws ClassMirrorNotFoundException;
8 |
9 | public abstract String getSuperclass() throws ClassMirrorNotFoundException;
10 |
11 | public abstract String[] getInterfaces() throws ClassMirrorNotFoundException;
12 |
13 | public abstract boolean isInterface();
14 |
15 | public abstract String getName();
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/src/kilim/mirrors/ClassMirrorNotFoundException.java:
--------------------------------------------------------------------------------
1 | package kilim.mirrors;
2 |
3 | public class ClassMirrorNotFoundException extends Exception {
4 |
5 | /**
6 | *
7 | */
8 | private static final long serialVersionUID = 5147833200948234264L;
9 |
10 | public ClassMirrorNotFoundException (String msg) {
11 | super(msg);
12 | }
13 | public ClassMirrorNotFoundException(Throwable cause) {
14 | super(cause);
15 | }
16 | public ClassMirrorNotFoundException(String className,
17 | ClassNotFoundException e) {
18 | super(className, e);
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/kilim/mirrors/Detector.java:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2006, Sriram Srinivasan
2 | *
3 | * You may distribute this software under the terms of the license
4 | * specified in the file "License"
5 | */
6 | package kilim.mirrors;
7 |
8 | import static kilim.Constants.D_OBJECT;
9 |
10 | import java.util.ArrayList;
11 |
12 | import kilim.NotPausable;
13 | import kilim.Pausable;
14 | import kilim.analysis.AsmDetector;
15 |
16 | /**
17 | * Utility class to check if a method has been marked pausable
18 | *
19 | */
20 | public class Detector {
21 | public static final int METHOD_NOT_FOUND_OR_PAUSABLE = 0; // either not found, or not pausable if found.
22 | public static final int PAUSABLE_METHOD_FOUND = 1; // known to be pausable
23 | public static final int METHOD_NOT_PAUSABLE = 2; // known to be not pausable
24 |
25 |
26 | // Note that we don't have the kilim package itself in the following list.
27 | static final String[] STANDARD_DONT_CHECK_LIST = { "java.", "javax." };
28 |
29 | public static final Detector DEFAULT = new Detector(new RuntimeClassMirrors());
30 |
31 | public final Mirrors mirrors;
32 |
33 | public Detector(Mirrors mirrors) {
34 | this.mirrors = mirrors;
35 |
36 | NOT_PAUSABLE = mirrors.mirror(NotPausable.class);
37 | PAUSABLE = mirrors.mirror(Pausable.class);
38 | OBJECT = mirrors.mirror(Object.class);
39 |
40 | }
41 |
42 | ClassMirror NOT_PAUSABLE, PAUSABLE, OBJECT;
43 |
44 | public boolean isPausable(String className, String methodName, String desc) {
45 | return getPausableStatus(className, methodName, desc) == PAUSABLE_METHOD_FOUND;
46 | }
47 |
48 | /**
49 | * @return one of METHOD_NOT_FOUND, PAUSABLE_METHOD_FOUND, METHOD_NOT_PAUSABLE
50 | */
51 |
52 | static boolean isNonPausableClass(String className) {
53 | return className == null || className.charAt(0) == '[' ||
54 | className.startsWith("java.") || className.startsWith("javax.");
55 | }
56 |
57 | static boolean isNonPausableMethod(String methodName) {
58 | return methodName.endsWith("init>");
59 | }
60 |
61 |
62 | public int getPausableStatus(String className, String methodName, String desc) {
63 | int ret = METHOD_NOT_FOUND_OR_PAUSABLE;
64 | // array methods (essentially methods deferred to Object (clone, wait etc)
65 | // and constructor methods are not pausable
66 | if (isNonPausableClass(className) || isNonPausableMethod(methodName)) {
67 | return METHOD_NOT_FOUND_OR_PAUSABLE;
68 | }
69 | className = className.replace('/', '.');
70 | try {
71 | MethodMirror m = findPausableMethod(className, methodName, desc);
72 | if (m != null) {
73 | for (String ex : m.getExceptionTypes()) {
74 | if (isNonPausableClass(ex)) continue;
75 | ClassMirror c = classForName(ex);
76 | if (NOT_PAUSABLE.isAssignableFrom(c)) {
77 | return METHOD_NOT_PAUSABLE;
78 | }
79 | if (PAUSABLE.isAssignableFrom(c)) {
80 | return PAUSABLE_METHOD_FOUND;
81 | }
82 | }
83 | return METHOD_NOT_PAUSABLE;
84 | }
85 | } catch (ClassMirrorNotFoundException ignore) {
86 |
87 | } catch (VerifyError ve) {
88 | return AsmDetector.getPausableStatus(className, methodName, desc, this);
89 | }
90 | return ret;
91 | }
92 |
93 | public ClassMirror classForName(String className) throws ClassMirrorNotFoundException {
94 | className = className.replace('/', '.');
95 | return mirrors.classForName(className);
96 | }
97 |
98 | public ClassMirror[] classForNames(String[] classNames) throws ClassMirrorNotFoundException {
99 | if (classNames == null) {
100 | return new ClassMirror[0];
101 | }
102 | ClassMirror[] ret = new ClassMirror[classNames.length];
103 | int i = 0;
104 | for (String cn : classNames) {
105 | ret[i++] = classForName(cn);
106 | }
107 | return ret;
108 | }
109 |
110 | private MethodMirror findPausableMethod(String className, String methodName, String desc)
111 | throws ClassMirrorNotFoundException {
112 |
113 | if (isNonPausableClass(className) || isNonPausableMethod(methodName))
114 | return null;
115 |
116 | ClassMirror cl = classForName(className);
117 | if (cl == null) return null;
118 |
119 | for (MethodMirror om : cl.getDeclaredMethods()) {
120 | if (om.getName().equals(methodName)) {
121 | // when comparing descriptors only compare arguments, not return types
122 | String omDesc= om.getMethodDescriptor();
123 |
124 | if (omDesc.substring(0,omDesc.indexOf(")")).equals(desc.substring(0,desc.indexOf(")")))) {
125 | if (om.isBridge()) continue;
126 | return om;
127 | }
128 | }
129 | }
130 |
131 | if (OBJECT.equals(cl))
132 | return null;
133 |
134 | MethodMirror m = findPausableMethod(cl.getSuperclass(), methodName, desc);
135 | if (m != null)
136 | return m;
137 |
138 | for (String ifname : cl.getInterfaces()) {
139 | if (isNonPausableClass(ifname)) continue;
140 | m = findPausableMethod(ifname, methodName, desc);
141 | if (m != null)
142 | return m;
143 | }
144 | return null;
145 | }
146 |
147 | @SuppressWarnings("unused")
148 | private static String statusToStr(int st) {
149 | switch (st) {
150 | case METHOD_NOT_FOUND_OR_PAUSABLE:
151 | return "not found or pausable";
152 | case PAUSABLE_METHOD_FOUND:
153 | return "pausable";
154 | case METHOD_NOT_PAUSABLE:
155 | return "not pausable";
156 | default:
157 | throw new AssertionError("Unknown status");
158 | }
159 | }
160 |
161 | static private final ThreadLocal DETECTOR = new ThreadLocal();
162 |
163 | public static Detector getDetector() {
164 | Detector d = DETECTOR.get();
165 | if (d == null)
166 | return Detector.DEFAULT;
167 | return d;
168 | }
169 |
170 | public static Detector setDetector(Detector d) {
171 | Detector res = DETECTOR.get();
172 | DETECTOR.set(d);
173 | return res;
174 | }
175 |
176 | public String commonSuperType(String oa, String ob) throws ClassMirrorNotFoundException {
177 | String a = toClassName(oa);
178 | String b = toClassName(ob);
179 |
180 | try {
181 | ClassMirror ca = classForName(a);
182 | ClassMirror cb = classForName(b);
183 | if (ca.isAssignableFrom(cb))
184 | return oa;
185 | if (cb.isAssignableFrom(ca))
186 | return ob;
187 | if (ca.isInterface() && cb.isInterface()) {
188 | return "java/lang/Object"; // This is what the java bytecode verifier does
189 | }
190 | } catch (ClassMirrorNotFoundException e) {
191 | // try to see if the below works...
192 | }
193 |
194 | if (a.equals(b)) {
195 | return oa;
196 | }
197 |
198 | ArrayList sca = getSuperClasses(a);
199 | ArrayList scb = getSuperClasses(b);
200 | int lasta = sca.size() - 1;
201 | int lastb = scb.size() - 1;
202 | do {
203 | if (sca.get(lasta).equals(scb.get(lastb))) {
204 | lasta--;
205 | lastb--;
206 | } else {
207 | break;
208 | }
209 | } while (lasta >= 0 && lastb >= 0);
210 |
211 | if (sca.size() == lasta+1) {
212 | return "java/lang/Object";
213 | }
214 |
215 | return sca.get(lasta + 1).replace('.', '/');
216 | }
217 |
218 | final private static ArrayList EMPTY_STRINGS = new ArrayList(0);
219 | public ArrayList getSuperClasses(String name) throws ClassMirrorNotFoundException {
220 | if (name == null) {
221 | return EMPTY_STRINGS;
222 | }
223 | ArrayList ret = new ArrayList(3);
224 | while (name != null) {
225 | ret.add(name);
226 | ClassMirror c = classForName(name);
227 | name = c.getSuperclass();
228 | }
229 | return ret;
230 |
231 | }
232 |
233 | private static String toDesc(String name) {
234 | return (name.equals(JAVA_LANG_OBJECT)) ? D_OBJECT : "L" + name.replace('.', '/') + ';';
235 | }
236 |
237 | private static String toClassName(String s) {
238 | if (s.endsWith(";"))
239 | return s.replace('/', '.').substring(1, s.length() - 1);
240 | else
241 | return s.replace('/', '.');
242 | }
243 |
244 | static String JAVA_LANG_OBJECT = "java.lang.Object";
245 |
246 | }
247 |
--------------------------------------------------------------------------------
/src/kilim/mirrors/MethodMirror.java:
--------------------------------------------------------------------------------
1 | package kilim.mirrors;
2 |
3 | public interface MethodMirror {
4 |
5 | public abstract String getName();
6 |
7 | /** @see org.objectweb.asm.Type#getMethodDescriptor(java.lang.reflect.Method) */
8 | public abstract String getMethodDescriptor();
9 |
10 | public abstract String[] getExceptionTypes();
11 |
12 | public abstract boolean isBridge();
13 |
14 | public abstract int getModifiers();
15 | }
16 |
--------------------------------------------------------------------------------
/src/kilim/mirrors/Mirrors.java:
--------------------------------------------------------------------------------
1 | package kilim.mirrors;
2 |
3 | /**
4 | * Mirrors provides a uniform facade for class and method related information
5 | * (via ClassMirror and MethodMirror). This information is obtained either through
6 | * loaded Class objects or parsed bytecode.
7 | */
8 | public interface Mirrors {
9 | abstract public ClassMirror classForName(String className)
10 | throws ClassMirrorNotFoundException;
11 |
12 | public abstract ClassMirror mirror(Class> clazz);
13 | public abstract ClassMirror mirror(String className, byte[] bytecode);
14 | }
15 |
--------------------------------------------------------------------------------
/src/kilim/mirrors/RuntimeClassMirrors.java:
--------------------------------------------------------------------------------
1 | package kilim.mirrors;
2 |
3 | import java.lang.reflect.Method;
4 | import java.util.Collections;
5 | import java.util.Map;
6 | import java.util.WeakHashMap;
7 |
8 | import kilim.KilimClassLoader;
9 |
10 | import org.objectweb.asm.Type;
11 |
12 | /**
13 | * This class provides the Mirrors facade over a set of Class objects
14 | * @see Mirrors
15 | */
16 |
17 | public class RuntimeClassMirrors implements Mirrors {
18 | // Weakly cache the mirror objects.
19 | Map cachedClasses = Collections
20 | .synchronizedMap(new WeakHashMap());
21 |
22 | public final KilimClassLoader classLoader;
23 |
24 | public RuntimeClassMirrors() {
25 | this(Thread.currentThread().getContextClassLoader());
26 | }
27 |
28 | public RuntimeClassMirrors(ClassLoader cl) {
29 | if (!(cl instanceof KilimClassLoader)) {
30 | cl = new KilimClassLoader(cl);
31 | }
32 | this.classLoader = (KilimClassLoader) cl;
33 | }
34 |
35 | @Override
36 | public ClassMirror classForName(String className) throws ClassMirrorNotFoundException {
37 | try {
38 | RuntimeClassMirror ret = cachedClasses.get(className);
39 | if (ret == null) {
40 | ret = make(classLoader.loadClass(className));
41 | }
42 | return ret;
43 | } catch (ClassNotFoundException e) {
44 | throw new ClassMirrorNotFoundException(className, e);
45 | }
46 | }
47 |
48 | @Override
49 | public ClassMirror mirror(Class> clazz) {
50 | if (clazz == null)
51 | return null;
52 | return make(clazz);
53 | }
54 |
55 | @Override
56 | public ClassMirror mirror(String className, byte[] bytecode) {
57 | try {
58 | return classForName(className);
59 | } catch (ClassMirrorNotFoundException ignore) {}
60 | return null;
61 | }
62 |
63 | /**
64 | * Like classForName, but only if the class is already loaded. This does not force loading of a
65 | * class.
66 | *
67 | * @param className
68 | * @return null if className not loaded, else a RuntimeClassMirror to represent the loaded
69 | * class.
70 | */
71 | public ClassMirror loadedClassForName(String className) {
72 | Class> c = classLoader.getLoadedClass(className);
73 | return (c == null) ? null : make(c);
74 | }
75 |
76 | public Class> getLoadedClass(String className) {
77 | return classLoader.getLoadedClass(className);
78 | }
79 |
80 | public boolean isLoaded(String className) {
81 | return classLoader.isLoaded(className);
82 | }
83 |
84 | private RuntimeClassMirror make(Class> c) {
85 | if (c == null) {
86 | throw new NullPointerException();
87 | }
88 | RuntimeClassMirror ret = new RuntimeClassMirror(c);
89 | cachedClasses.put(c.getName(), ret);
90 | return ret;
91 | }
92 | }
93 |
94 | class RuntimeMethodMirror implements MethodMirror {
95 |
96 | private final Method method;
97 |
98 | public RuntimeMethodMirror(Method method) {
99 | this.method = method;
100 | }
101 |
102 | public String getName() {
103 | return method.getName();
104 | }
105 |
106 | public int getModifiers() {
107 | return method.getModifiers();
108 | }
109 |
110 | public String[] getExceptionTypes() {
111 | String[] ret = new String[method.getExceptionTypes().length];
112 | int i = 0;
113 | for (Class> excl : method.getExceptionTypes()) {
114 | ret[i++] = excl.getName();
115 | }
116 | return ret;
117 | }
118 |
119 | public String getMethodDescriptor() {
120 | return Type.getMethodDescriptor(method);
121 | }
122 |
123 | public boolean isBridge() {
124 | return method.isBridge();
125 | }
126 | }
127 |
128 | class RuntimeClassMirror implements ClassMirror {
129 |
130 | private final Class> clazz;
131 | private MethodMirror[] methods;
132 |
133 | public RuntimeClassMirror(Class> clazz) {
134 | this.clazz = clazz;
135 | }
136 |
137 | @Override
138 | public String getName() {
139 | return clazz.getName();
140 | }
141 |
142 | @Override
143 | public boolean isInterface() {
144 | return clazz.isInterface();
145 | }
146 |
147 | @Override
148 | public boolean equals(Object obj) {
149 | if (obj instanceof ClassMirror) {
150 | return ((ClassMirror) obj).getName().equals(this.getName());
151 | }
152 | return false;
153 | }
154 |
155 | @Override
156 | public int hashCode() {
157 | return clazz.hashCode();
158 | }
159 |
160 | @Override
161 | public MethodMirror[] getDeclaredMethods() {
162 | if (methods == null) {
163 | Method[] declaredMethods = clazz.getDeclaredMethods();
164 | methods = new MethodMirror[declaredMethods.length];
165 | for (int i = 0; i < declaredMethods.length; i++) {
166 | methods[i] = new RuntimeMethodMirror(declaredMethods[i]);
167 | }
168 | }
169 | return methods;
170 | }
171 |
172 | @Override
173 | public String[] getInterfaces() {
174 | Class>[] ifs = clazz.getInterfaces();
175 | String[] result = new String[ifs.length];
176 | for (int i = 0; i < result.length; i++) {
177 | result[i] = ifs[i].getName();
178 | }
179 | return result;
180 | }
181 |
182 | @Override
183 | public String getSuperclass() {
184 | Class> supcl = clazz.getSuperclass();
185 | return supcl != null ? supcl.getName() : null;
186 | }
187 |
188 | @Override
189 | public boolean isAssignableFrom(ClassMirror c) {
190 | if (c instanceof RuntimeClassMirror) {
191 | RuntimeClassMirror cc = (RuntimeClassMirror) c;
192 | return clazz.isAssignableFrom(cc.clazz);
193 | } else {
194 | return false;
195 | }
196 | }
197 |
198 | }
--------------------------------------------------------------------------------
/src/kilim/tools/FlowAnalyzer.java:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2006, Sriram Srinivasan
2 | *
3 | * You may distribute this software under the terms of the license
4 | * specified in the file "License"
5 | */
6 |
7 | package kilim.tools;
8 | import static kilim.analysis.Utils.dedent;
9 | import static kilim.analysis.Utils.indent;
10 | import static kilim.analysis.Utils.pn;
11 | import static kilim.analysis.Utils.resetIndentation;
12 | import static org.objectweb.asm.Opcodes.INVOKESTATIC;
13 |
14 | import java.io.FileInputStream;
15 | import java.io.IOException;
16 | import java.io.PrintStream;
17 | import java.util.ArrayList;
18 | import java.util.Arrays;
19 | import java.util.Collections;
20 | import java.util.Enumeration;
21 | import java.util.jar.JarEntry;
22 | import java.util.jar.JarFile;
23 | import java.util.zip.ZipEntry;
24 |
25 | import kilim.analysis.BasicBlock;
26 | import kilim.analysis.ClassFlow;
27 | import kilim.analysis.Frame;
28 | import kilim.analysis.MethodFlow;
29 | import kilim.analysis.TypeDesc;
30 | import kilim.analysis.Usage;
31 | import kilim.analysis.Value;
32 | import kilim.mirrors.Detector;
33 |
34 | import org.objectweb.asm.tree.AbstractInsnNode;
35 | import org.objectweb.asm.tree.MethodInsnNode;
36 |
37 | /**
38 | * Used to dump the stack and locals at the beginning of each basic block
39 | * @author ram
40 | */
41 | public class FlowAnalyzer {
42 | public static void main(String[] args) throws Exception {
43 | if (args.length == 0) {
44 | System.err.println("Usage [methodName]");
45 | System.exit(1);
46 | }
47 | String name = args[0];
48 | if (name.endsWith(".jar")) {
49 | analyzeJar(name, Detector.DEFAULT);
50 | } else {
51 | analyzeClass(name, Detector.DEFAULT);
52 | }
53 | }
54 |
55 | private static void analyzeClass(String className, Detector detector) {
56 | try {
57 | pn("-------------------------------------------------");
58 | pn("Class: " + className);
59 | System.out.flush();
60 | ClassFlow cf = null;
61 | if (className.endsWith(".class")) {
62 | FileInputStream fis = null;
63 | try {
64 | fis = new FileInputStream(className);
65 | cf = new ClassFlow(fis, detector);
66 | } finally {
67 | if (fis != null) {fis.close();}
68 | }
69 | }
70 | if (cf == null) {
71 | cf = new ClassFlow(className, detector);
72 | }
73 | ArrayList flows = cf.analyze(true);
74 | for (MethodFlow flow: flows) {
75 | reportFlow(flow, className);
76 | }
77 | } catch (IOException e) {
78 | pn("##################################################");
79 | stackTrace(e);
80 | } catch (Throwable ie) {
81 | pn("##################################################");
82 | stackTrace(ie);
83 | }
84 | }
85 |
86 | private static void stackTrace(Throwable t) {
87 | PrintStream ps = new PrintStream(System.out);
88 | t.printStackTrace(ps);
89 | }
90 |
91 | private static void reportFlow(MethodFlow method, String className) {
92 | resetIndentation();
93 | pn("Method : "+ className + '.' + method.name);
94 |
95 | pn("MaxStack: " + method.maxStack);
96 | pn("MaxLocals: " + method.maxLocals);
97 | ArrayList bbs = method.getBasicBlocks();
98 | Collections.sort(bbs);
99 | indent(2);
100 | for (BasicBlock bb: bbs) {
101 | AbstractInsnNode ainode = bb.getInstruction(bb.startPos);
102 | if (ainode instanceof MethodInsnNode) {
103 | MethodInsnNode m = (MethodInsnNode)ainode;
104 | int n = getNumArgs(m); // This many will get consumed from stack
105 | pn("Call(" + n + "): " + m.owner + "." + m.name + m.desc);
106 | indent(2);
107 | pn("Inframe: ");
108 | indent(2);
109 | Frame f = bb.startFrame;
110 | pn(f.toString());
111 | dedent(2);
112 | pn("Live locals:");
113 | indent(2);
114 | Usage u = bb.getVarUsage();
115 | pn(u.toString());
116 | dedent(2);
117 | pn("Actual usage: " + uniqueItems(bb, f, u, n));
118 | dedent(2);
119 | }
120 | }
121 | dedent(2);
122 | }
123 |
124 | private static String uniqueItems(BasicBlock bb, Frame f, Usage u, int nStack) {
125 | StringBuffer sb = new StringBuffer(80);
126 | int numNonConstants = 0;
127 | int numLive = 0;
128 | ArrayList set = new ArrayList(10);
129 | for (int i = 0; i < f.getMaxLocals(); i++) {
130 | if (u.isLiveIn(i)) {
131 | numLive++;
132 | Value v = f.getLocal(i);
133 | if (!set.contains(v)) set.add(v);
134 | }
135 | }
136 | nStack = f.getStackLen() - nStack;
137 | for (int i = 0; i < nStack; i++) {
138 | Value v = f.getStack(i);
139 | if (!set.contains(v)) set.add(v);
140 | }
141 | char[] sig = new char[set.size()];
142 | // create canonical sig. Convert types to one of 'O', 'I', 'F', 'L', 'D' and
143 | // put in sorted order
144 | // Also count non constants while we are iterating anyway.
145 | for (int i = 0; i < set.size(); i++) {
146 | Value v = set.get(i);
147 | char c = v.getTypeDesc().charAt(0);
148 | switch (c) {
149 | case 'L': case '[': case 'N':
150 | c = 'O'; break;
151 | case 'I': case 'B': case 'S': case 'Z': case 'C':
152 | c = 'I'; break;
153 | case 'J':
154 | c = 'J'; break;
155 | case 'F':
156 | c = 'F'; break;
157 | case 'U':
158 | default: {
159 | c = 'U';
160 | System.err.println("***************************************");
161 | System.err.println("Undefined/unrecognized value " + v);
162 | System.err.println("BasicBlock:\n" + bb);
163 | break;
164 | }
165 | }
166 | sig[i] = c;
167 | if (v.getConstVal() == Value.NO_VAL) {
168 | numNonConstants++;
169 | }
170 | }
171 | Arrays.sort(sig);
172 | numLive += nStack;
173 | sb.append("avail: ").append(nStack + f.getMaxLocals());
174 | sb.append(", live: " + numLive);
175 | sb.append(", unique: ").append(set.size());
176 | sb.append(", unique non-const: ").append(numNonConstants);
177 | sb.append("\nState signature: ").append(set.size() == 0 ? "None" : new String(sig));
178 | return sb.toString();
179 | }
180 |
181 | private static int getNumArgs(MethodInsnNode m) {
182 | int ret = TypeDesc.getNumArgumentTypes(m.desc);
183 | if (m.getOpcode() != INVOKESTATIC) ret++;
184 | return ret;
185 | }
186 |
187 | public static void analyzeJar(String jarFile, Detector detector) {
188 | try {
189 | Enumeration e = new JarFile(jarFile).entries();
190 | while (e.hasMoreElements()) {
191 | ZipEntry en = (ZipEntry) e.nextElement();
192 | String n = en.getName();
193 | if (!n.endsWith(".class")) continue;
194 | n = n.substring(0, n.length() - 6).replace('/','.');
195 | analyzeClass(n, detector);
196 | }
197 | } catch (Exception e) {
198 | e.printStackTrace();
199 | }
200 | }
201 | }
202 |
--------------------------------------------------------------------------------
/src/kilim/tools/Javac.java:
--------------------------------------------------------------------------------
1 | package kilim.tools;
2 |
3 | import java.io.File;
4 | import java.io.FileInputStream;
5 | import java.io.FileOutputStream;
6 | import java.io.IOException;
7 | import java.util.ArrayList;
8 | import java.util.List;
9 | import java.util.Random;
10 | import java.util.regex.Matcher;
11 | import java.util.regex.Pattern;
12 |
13 | import javax.tools.JavaCompiler;
14 | import javax.tools.ToolProvider;
15 |
16 | import kilim.analysis.ClassInfo;
17 |
18 | /**
19 | * Simple utility class to invoke the java compiler.
20 | */
21 |
22 | public class Javac {
23 |
24 | /**
25 | * Given a list of file-scope java code (equivalent to a .java file, including package and
26 | * import declarations), compile() invokes javac to compile them, produce classfiles and return
27 | * a list of pairs.
28 | *
29 | * compile() dumps the source strings into their respective files, has javac compile them, then
30 | * reads back the equivalent class files. The name of the source file is gleaned from the string
31 | * itself; a string containing "public class Foo" is stored in tmpDir/Foo.java (where tmpDir is
32 | * a temporary directory that's deleted after the compilation), and if no public class or
33 | * interface is found, the name of the first class in the string is used.
34 | *
35 | * Note that the list of returned classes may be larger than
36 | *
37 | * @param srcCodes
38 | * . List of strings.
39 | * @return List. className is fully qualified, and byte[] contains the
40 | * bytecode of the class.
41 | * @throws IOException
42 | */
43 | public static List compile(List srcCodes) throws IOException {
44 |
45 | List srcInfos = getSourceInfos(srcCodes);
46 |
47 | File rootDir = getTmpDir(); // something like "/tmp/kilim$2348983948"
48 |
49 | File classDir = new File(rootDir.getAbsolutePath() + File.separatorChar + "classes");
50 | classDir.mkdir(); // "/classes"
51 |
52 | String options[] = { "-d", classDir.getAbsolutePath() };
53 |
54 | String args[] = new String[options.length + srcCodes.size()];
55 | System.arraycopy(options, 0, args, 0, options.length);
56 | int i = options.length;
57 |
58 | for (SourceInfo srci : srcInfos) {
59 | String name = rootDir.getAbsolutePath() + File.separatorChar + srci.className + ".java";
60 | writeFile(new File(name), srci.srcCode.getBytes());
61 | args[i++] = name;
62 | }
63 |
64 | JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
65 | compiler.run(null, null, null, args);
66 |
67 | List ret = new ArrayList();
68 | addClasses(ret, "", classDir);
69 | deleteDir(rootDir);
70 | return ret;
71 | }
72 |
73 | private static List getSourceInfos(List srcCodes) {
74 | List srcInfos = new ArrayList(srcCodes.size());
75 | for (String srcCode : srcCodes) {
76 | srcInfos.add(getSourceInfo(srcCode));
77 | }
78 | return srcInfos;
79 | }
80 |
81 | static Pattern publicClassNameRegexp = Pattern.compile("public +(?:class|interface) +(\\w+)");
82 | static Pattern classNameRegexp = Pattern.compile("(?:class|interface) +(\\w+)");
83 |
84 | private static SourceInfo getSourceInfo(String srcCode) {
85 | Matcher m = publicClassNameRegexp.matcher(srcCode);
86 | if (m.find())
87 | return new SourceInfo(m.group(1), srcCode);
88 | else {
89 | m = classNameRegexp.matcher(srcCode);
90 | if (m.find())
91 | return new SourceInfo(m.group(1), srcCode);
92 | else
93 | throw new IllegalArgumentException(
94 | "No class or interface definition found in src: \n'" + srcCode + "'");
95 | }
96 | }
97 |
98 | private static File getTmpDir() throws IOException {
99 | String tmpDirName = System.getProperty("java.io.tmpdir");
100 | if (tmpDirName == null) {
101 | tmpDirName = "";
102 | } else {
103 | tmpDirName += File.separator;
104 | }
105 | Random r = new Random();
106 | String name = tmpDirName + "kilim$" + r.nextLong();
107 | File rootDir = new File(name);
108 | if (!rootDir.mkdir()) {
109 | throw new IOException("Unable to make tmp directory " + rootDir.getAbsolutePath());
110 | }
111 | return rootDir;
112 | }
113 |
114 | private static void deleteDir(File rootDir) {
115 | for (File f : rootDir.listFiles()) {
116 | if (f.isDirectory()) {
117 | deleteDir(f);
118 | } else {
119 | if (!f.delete()) {
120 | System.err.println("Unable to delete " + f.getAbsolutePath());
121 | }
122 | }
123 | }
124 | if (!rootDir.delete()) {
125 | System.err.println("Unable to delete " + rootDir.getAbsolutePath());
126 | }
127 | }
128 |
129 | private static void addClasses(List ret, String pkgName, File dir)
130 | throws IOException {
131 | for (File f : dir.listFiles()) {
132 | String fname = f.getName();
133 | if (f.isDirectory()) {
134 | String qname = pkgName + fname + ".";
135 | addClasses(ret, qname, f);
136 | } else if (fname.endsWith(".class")) {
137 | String qname = pkgName + fname.substring(0, fname.length() - 6);
138 | ret.add(new ClassInfo(qname, readFile(f)));
139 | } else {
140 | System.err.println("Unexpected file : " + f.getAbsolutePath());
141 | }
142 | }
143 | }
144 |
145 | private static byte[] readFile(File f) throws IOException {
146 | int len = (int) f.length();
147 | byte[] buf = new byte[len];
148 | FileInputStream fis = new FileInputStream(f);
149 | int off = 0;
150 | while (len > 0) {
151 | int n = fis.read(buf, off, len);
152 | if (n == -1)
153 | throw new IOException("Unexpected EOF reading " + f.getAbsolutePath());
154 | off += n;
155 | len -= n;
156 | }
157 | return buf;
158 | }
159 |
160 | private static void writeFile(File f, byte[] srcCode) throws IOException {
161 | FileOutputStream fos = new FileOutputStream(f);
162 | fos.write(srcCode);
163 | fos.close();
164 | }
165 |
166 | private static class SourceInfo {
167 | public SourceInfo(String nm, String code) {
168 | className = nm;
169 | srcCode = code;
170 | }
171 |
172 | public String className;
173 | public String srcCode;
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/src/kilim/tools/Kilim.java:
--------------------------------------------------------------------------------
1 | package kilim.tools;
2 |
3 | import java.lang.reflect.Method;
4 |
5 | import kilim.WeavingClassLoader;
6 |
7 |
8 |
9 | /**
10 | * Invoke as java -Dkilim.classpath="classDir1:classDir2:jar1.jar:..." Kilim class args...
11 | *
12 | * This class dynamically weaves kilim-related classes and runs "class". The classpath
13 | * specified must not be in the main classpath, otherwise the system class loader will
14 | * use the raw, unwoven classes.
15 | */
16 | public class Kilim {
17 | public static void main(String[] args) throws Exception {
18 | if (args.length == 0) {
19 | usage();
20 | }
21 | String className = args[0];
22 | args = processArgs(args);
23 | WeavingClassLoader wcl = new WeavingClassLoader(Thread.currentThread().getContextClassLoader());
24 | Class> mainClass = wcl.loadClass(className);
25 | Method mainMethod = mainClass.getMethod("main", new Class[]{String[].class});
26 | mainMethod.invoke(null,new Object[] {args});
27 | }
28 |
29 | private static void usage() {
30 | System.out.println("java -Dkilim.classpath kilim.tools.Kilim class [args ...]");
31 | System.exit(1);
32 | }
33 |
34 | private static String[] processArgs(String[] args) {
35 | String[] ret = new String[args.length-1];
36 | if (ret.length > 0)
37 | System.arraycopy(args, 1, ret, 0, ret.length);
38 | return ret;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/kilim/tools/P.java:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2006, Sriram Srinivasan
2 | *
3 | * You may distribute this software under the terms of the license
4 | * specified in the file "License"
5 | */
6 |
7 | package kilim.tools;
8 |
9 | // Various print routines to call from jvml files (*.j). More convenient
10 | // than calling System.out.println.
11 |
12 | public class P {
13 | // Call as invokestatic kilim/tools/P/pi(I)V
14 | public static void pi(int i) {
15 | System.out.println(i);
16 | }
17 |
18 | // Call as invokestatic kilim/tools/P/pn()V
19 | public static void pn() {
20 | System.out.println();
21 | }
22 |
23 | // Call as invokestatic kilim/tools/P/pn(Ljava/lang/Object;)V
24 | public static void pn(Object o) {
25 | System.out.println(o);
26 | }
27 |
28 | // Call as invokestatic kilim/tools/P/p(Ljava/lang/Object;)V
29 | public static void p(Object o) {
30 | System.out.print(o);
31 | }
32 |
33 | // Call as invokestatic kilim/tools/P/ps(Ljava/lang/Object;)V
34 | public static void ps(Object o) {
35 | System.out.print(o);
36 | System.out.print(" ");
37 | }
38 | // Call as invokestatic kilim/tools/P/ptest()V
39 | public static void ptest() {
40 | System.out.println("test");
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/kilim/tools/Weaver.java:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2006, Sriram Srinivasan
2 | *
3 | * You may distribute this software under the terms of the license
4 | * specified in the file "License"
5 | */
6 |
7 | package kilim.tools;
8 |
9 | import java.io.BufferedInputStream;
10 | import java.io.File;
11 | import java.io.FileInputStream;
12 | import java.io.FileOutputStream;
13 | import java.io.IOException;
14 | import java.io.InputStream;
15 | import java.util.ArrayList;
16 | import java.util.List;
17 | import java.util.regex.Pattern;
18 |
19 | import kilim.KilimException;
20 | import kilim.analysis.ClassInfo;
21 | import kilim.analysis.ClassWeaver;
22 | import kilim.analysis.FileLister;
23 | import kilim.mirrors.CachedClassMirrors;
24 | import kilim.mirrors.Detector;
25 |
26 | /**
27 | * This class supports both command-line and run time weaving of Kilim bytecode.
28 | */
29 |
30 | public class Weaver {
31 | public static String outputDir = null;
32 | public static boolean verbose = true;
33 | public static Pattern excludePattern = null;
34 | static int err = 0;
35 |
36 | /**
37 | *
38 | * Usage: java kilim.tools.Weaver -d <output directory> {source classe, jar, directory ...}
39 | *
40 | *
41 | * If directory names or jar files are given, all classes in that container are processed. It is
42 | * perfectly fine to specify the same directory for source and output like this:
43 | *
44 | * java kilim.tools.Weaver -d ./classes ./classes
45 | *
46 | * Ensure that all classes to be woven are in the classpath. The output directory does not have to be
47 | * in the classpath during weaving.
48 | *
49 | * @see #weave(List) for run-time weaving.
50 | */
51 | public static void main(String[] args) throws IOException {
52 |
53 | Detector detector = Detector.DEFAULT;
54 |
55 | String currentName = null;
56 | for (String name : parseArgs(args)) {
57 | try {
58 | if (name.endsWith(".class")) {
59 | if (exclude(name))
60 | continue;
61 | currentName = name;
62 | weaveFile(name, new BufferedInputStream(new FileInputStream(name)), detector);
63 | } else if (name.endsWith(".jar")) {
64 | for (FileLister.Entry fe : new FileLister(name)) {
65 | currentName = fe.getFileName();
66 | if (currentName.endsWith(".class")) {
67 | currentName = currentName.substring(0, currentName.length() - 6)
68 | .replace('/', '.');
69 | if (exclude(currentName))
70 | continue;
71 | weaveFile(currentName, fe.getInputStream(), detector);
72 | }
73 | }
74 | } else if (new File(name).isDirectory()) {
75 | for (FileLister.Entry fe : new FileLister(name)) {
76 | currentName = fe.getFileName();
77 | if (currentName.endsWith(".class")) {
78 | if (exclude(currentName))
79 | continue;
80 | weaveFile(currentName, fe.getInputStream(), detector);
81 | }
82 | }
83 | } else {
84 | weaveClass(name, detector);
85 | }
86 | } catch (KilimException ke) {
87 | System.err.println("Error weaving " + currentName + ". " + ke.getMessage());
88 | // ke.printStackTrace();
89 | System.exit(1);
90 | } catch (IOException ioe) {
91 | System.err.println("Unable to find/process '" + currentName + "'");
92 | System.exit(1);
93 | } catch (Throwable t) {
94 | System.err.println("Error weaving " + currentName);
95 | t.printStackTrace();
96 | System.exit(1);
97 | }
98 | }
99 | System.exit(err);
100 | }
101 |
102 | static boolean exclude(String name) {
103 | return excludePattern == null ? false : excludePattern.matcher(name).find();
104 | }
105 |
106 | static void weaveFile(String name, InputStream is, Detector detector) throws IOException {
107 | try {
108 | ClassWeaver cw = new ClassWeaver(is, detector);
109 | cw.weave();
110 | writeClasses(cw);
111 | } catch (KilimException ke) {
112 | System.err.println("***** Error weaving " + name + ". " + ke.getMessage());
113 | // ke.printStackTrace();
114 | err = 1;
115 | } catch (RuntimeException re) {
116 | System.err.println("***** Error weaving " + name + ". " + re.getMessage());
117 | re.printStackTrace();
118 | err = 1;
119 | } catch (IOException ioe) {
120 | err = 1;
121 | System.err.println("***** Unable to find/process '" + name + "'\n" + ioe.getMessage());
122 | }
123 | }
124 |
125 | static void weaveClass(String name, Detector detector) {
126 | try {
127 | ClassWeaver cw = new ClassWeaver(name, detector);
128 | writeClasses(cw);
129 | } catch (KilimException ke) {
130 | err = 1;
131 | System.err.println("***** Error weaving " + name + ". " + ke.getMessage());
132 | // ke.printStackTrace();
133 |
134 | } catch (IOException ioe) {
135 | err = 1;
136 | System.err.println("***** Unable to find/process '" + name + "'\n" + ioe.getMessage());
137 | }
138 | }
139 |
140 | /** public only for testing purposes */
141 | public static void weaveClass2(String name, Detector detector) throws IOException {
142 | try {
143 | ClassWeaver cw = new ClassWeaver(name, detector);
144 | cw.weave();
145 | writeClasses(cw);
146 | } catch (KilimException ke) {
147 | err = 1;
148 | System.err.println("***** Error weaving " + name + ". " + ke.getMessage());
149 | // ke.printStackTrace();
150 | throw ke;
151 |
152 | } catch (IOException ioe) {
153 | err = 1;
154 | System.err.println("***** Unable to find/process '" + name + "'\n" + ioe.getMessage());
155 | throw ioe;
156 | }
157 | }
158 |
159 | static void writeClasses(ClassWeaver cw) throws IOException {
160 | List cis = cw.getClassInfos();
161 | if (cis.size() > 0) {
162 | for (ClassInfo ci : cis) {
163 | writeClass(ci);
164 | }
165 | }
166 | }
167 |
168 | static void writeClass(ClassInfo ci) throws IOException {
169 | String className = ci.className.replace('.', File.separatorChar);
170 | String dir = outputDir + File.separatorChar + getDirName(className);
171 | mkdir(dir);
172 | // Convert name to fully qualified file name
173 | className = outputDir + File.separatorChar + className + ".class";
174 | if (ci.className.startsWith("kilim.S_")) {
175 | // Check if we already have that file
176 | if (new File(className).exists())
177 | return;
178 | }
179 | FileOutputStream fos = new FileOutputStream(className);
180 | fos.write(ci.bytes);
181 | fos.close();
182 | if (verbose) {
183 | System.out.println("Wrote: " + className);
184 | }
185 | }
186 |
187 | static void mkdir(String dir) throws IOException {
188 | File f = new File(dir);
189 | if (!f.exists()) {
190 | if (!f.mkdirs()) {
191 | throw new IOException("Unable to create directory: " + dir);
192 | }
193 | }
194 | }
195 |
196 | static String getDirName(String className) {
197 | int end = className.lastIndexOf(File.separatorChar);
198 | return (end == -1) ? "" : className.substring(0, end);
199 | }
200 |
201 | static void help() {
202 | System.err.println("java kilim.tools.Weaver opts -d (class/directory/jar)+");
203 | System.err.println(" where opts are -q : quiet");
204 | System.err.println(" -x : exclude all classes matching regex");
205 | System.exit(1);
206 | }
207 |
208 | static ArrayList parseArgs(String[] args) throws IOException {
209 | if (args.length == 0)
210 | help();
211 |
212 | ArrayList ret = new ArrayList(args.length);
213 | String regex = null;
214 | for (int i = 0; i < args.length; i++) {
215 | String arg = args[i];
216 | if (arg.equals("-d")) {
217 | outputDir = args[++i];
218 | } else if (arg.equals("-q")) {
219 | verbose = false;
220 | } else if (arg.equals("-h")) {
221 | help();
222 | } else if (arg.equals("-x")) {
223 | regex = args[++i];
224 | excludePattern = Pattern.compile(regex);
225 | } else {
226 | ret.add(arg);
227 | }
228 | }
229 | if (outputDir == null) {
230 | System.err.println("Specify output directory with -d option");
231 | System.exit(1);
232 | }
233 | mkdir(outputDir);
234 | return ret;
235 | }
236 |
237 | private Detector detector;
238 | private CachedClassMirrors mirrors;
239 |
240 | public Weaver() {
241 | this(Thread.currentThread().getContextClassLoader());
242 | }
243 |
244 | public Weaver(ClassLoader cl) {
245 | mirrors = new CachedClassMirrors(cl);
246 | detector = new Detector(mirrors);
247 | }
248 |
249 | /**
250 | * See #weave(List)
251 | */
252 | public List weave(ClassInfo cl) throws KilimException {
253 | List ret = new ArrayList(1);
254 | ret.add(cl);
255 | ret = weave(ret);
256 | return ret;
257 | }
258 |
259 | /**
260 | * Analyzes the list of supplied classes and inserts Kilim-related bytecode if necessary. If a
261 | * supplied class is dependent upon another class X, it is the caller's responsibility to ensure
262 | * that X is either in the classpath, or loaded by the context classloader, or has been seen in
263 | * an earlier invocation of weave().
264 | *
265 | * Since weave() remembers method signatures from earlier invocations, the woven classes do not
266 | * have to be classloaded to help future invocations of weave.
267 | *
268 | * If two classes A and B are not in the classpath, and are mutually recursive, they can be woven
269 | * only if supplied in the same input list.
270 | *
271 | * This method is thread safe.
272 | *
273 | * @param classes A list of (className, byte[]) pairs. The first part is a fully qualified class
274 | * name, and the second part is the bytecode for the class.
275 | *
276 | * @return A list of (className, byte[]) pairs. Some of the classes may or may not have been
277 | * modified, and new ones may be added.
278 | *
279 | * @throws KilimException
280 | */
281 | public List weave(List classes) throws KilimException {
282 | // save the detector attached to this thread, if any. It will be restored
283 | // later.
284 | ArrayList ret = new ArrayList(classes.size());
285 | Detector origDetector = Detector.getDetector();
286 | Detector.setDetector(detector); // / set thread local detector.
287 | try {
288 | // First cache all the method signatures from the supplied classes to allow
289 | // the weaver to lookup method signatures from mutually recursive classes.
290 | for (ClassInfo cl : classes) {
291 | detector.mirrors.mirror(cl.className, cl.bytes);
292 | }
293 |
294 | // Now weave them individually
295 | for (ClassInfo cl : classes) {
296 | ClassWeaver cw = new ClassWeaver(cl.bytes, detector);
297 | cw.weave();
298 | ret.addAll(cw.getClassInfos()); // one class file can result in multiple classes
299 | }
300 | return ret;
301 | } finally {
302 | Detector.setDetector(origDetector);
303 | }
304 | }
305 | }
306 |
--------------------------------------------------------------------------------
/test.sh:
--------------------------------------------------------------------------------
1 | echo "Testing Kilim Weaver"
2 | java -ea -cp ./classes:./libs/asm-all-5.0.3.jar:./libs/junit.jar junit.textui.TestRunner kilim.test.AllNotWoven
3 |
4 | echo "Task, mailbox tests"
5 | java -ea -Dkilim.Scheduler.numThreads=10 -cp ./testclasses:./classes:./libs/asm-all-5.0.3.jar:./libs/junit.jar junit.textui.TestRunner kilim.test.AllWoven
6 |
--------------------------------------------------------------------------------
/test/kilim/test/AllNotWoven.java:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2006, Sriram Srinivasan
2 | *
3 | * You may distribute this software under the terms of the license
4 | * specified in the file "License"
5 | */
6 |
7 | package kilim.test;
8 |
9 | import junit.framework.Test;
10 | import junit.framework.TestSuite;
11 |
12 | public class AllNotWoven extends TestSuite {
13 | public static Test suite() {
14 | TestSuite ret = new AllNotWoven();
15 | ret.addTestSuite(TestTypeDesc.class);
16 | ret.addTestSuite(TestUsage.class);
17 | ret.addTestSuite(TestValue.class);
18 | ret.addTestSuite(TestFrame.class);
19 | ret.addTestSuite(TestBasicBlock.class);
20 | ret.addTestSuite(TestJSR.class);
21 | ret.addTestSuite(TestFlow.class);
22 | ret.addTestSuite(TestExprs.class);
23 | ret.addTestSuite(TestClassInfo.class);
24 | // ret.addTestSuite(TestDynamicWeaver.class);
25 | return ret;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/test/kilim/test/Base.java:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2006, Sriram Srinivasan
2 | *
3 | * You may distribute this software under the terms of the license
4 | * specified in the file "License"
5 | */
6 |
7 | package kilim.test;
8 |
9 | import java.util.ArrayList;
10 |
11 | import junit.framework.TestCase;
12 | import kilim.analysis.BasicBlock;
13 | import kilim.analysis.ClassFlow;
14 | import kilim.analysis.MethodFlow;
15 | import kilim.mirrors.Detector;
16 |
17 | import org.objectweb.asm.tree.AbstractInsnNode;
18 | import org.objectweb.asm.tree.MethodInsnNode;
19 |
20 | public class Base extends TestCase {
21 | private static ArrayList stflows;
22 | private static String lastClassName = null;
23 |
24 | protected void cache(String className) throws Exception {
25 | if (lastClassName != className) {
26 | ClassFlow cf = new ClassFlow(className, Detector.DEFAULT);
27 | stflows = cf.analyze(/* forceAnalysis = */true);
28 | lastClassName = className;
29 | }
30 | }
31 |
32 | protected MethodFlow getFlow(String methodName) {
33 | for (int i = 0; i < stflows.size(); i++) {
34 | MethodFlow flow = stflows.get(i);
35 | if (flow.name.equals(methodName)) {
36 | return flow;
37 | }
38 | }
39 | fail("No method called " + methodName);
40 | return null;
41 | }
42 |
43 | /**
44 | * Returns the first basic block in the flow that has a method invocation of
45 | *
46 | */
47 | protected BasicBlock getBBForMethod(MethodFlow flow, String methodName) {
48 | for (BasicBlock bb : flow.getBasicBlocks()) {
49 | AbstractInsnNode ainode = bb.getInstruction(bb.startPos);
50 | if (ainode instanceof MethodInsnNode
51 | && ((MethodInsnNode) ainode).name.equals(methodName)) {
52 | return bb;
53 | }
54 | }
55 | fail("No method invocation found for " + methodName);
56 | return null;
57 | }
58 |
59 | protected ArrayList getFlows() {
60 | return stflows;
61 | }
62 |
63 | protected void checkCov(String methodName) {
64 | MethodFlow flow = getFlow(methodName);
65 | if (flow == null)
66 | return;
67 | ArrayList bbs = flow.getBasicBlocks();
68 | // Verify that all instructions are covered and that the only ones that
69 | // aren't are labelnodes. Also verify that there are no overlaps.
70 | int size = flow.instructions.size();
71 | boolean coverage[] = new boolean[size];
72 | for (int i = 0; i < size; i++) {
73 | coverage[i] = false;
74 | }
75 | for (BasicBlock bb : bbs) {
76 | /*
77 | * if (bb.startFrame == null) { fail("BB doesn't have a starting
78 | * frame"); return; }
79 | */
80 | int end = bb.endPos;
81 | for (int i = bb.startPos; i <= end; i++) {
82 | if (coverage[i]) {
83 | fail("BasicBlock overlap");
84 | return;
85 | }
86 | coverage[i] = true;
87 | }
88 | }
89 | for (int i = 0; i < size; i++) {
90 | if (!coverage[i]) {
91 | fail("Instruction " + i + " not covered");
92 | return;
93 | }
94 | }
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/test/kilim/test/TaskTestClassLoader.java:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2006, Sriram Srinivasan
2 | *
3 | * You may distribute this software under the terms of the license
4 | * specified in the file "License"
5 | */
6 |
7 | package kilim.test;
8 |
9 | import java.io.File;
10 | import java.io.FileInputStream;
11 | import java.io.IOException;
12 | import java.net.URL;
13 |
14 | public class TaskTestClassLoader extends ClassLoader {
15 | static String wclassDir;
16 |
17 | static {
18 | URL baseURL = Thread.currentThread().getContextClassLoader().getResource("kilim/test/TaskTestClassLoader.class");
19 | String path = baseURL.getPath();
20 | wclassDir = path.substring(0, path.indexOf("/classes/")) + "/wclasses/";
21 | }
22 |
23 | public TaskTestClassLoader(ClassLoader aParent) {
24 | super(aParent);
25 | }
26 |
27 | @Override
28 | public Class> loadClass(String className, boolean resolve)
29 | throws ClassNotFoundException {
30 | Class> ret = findLoadedClass(className);
31 | if (ret == null && className.startsWith("kilim")) {
32 | File f = new File(wclassDir + className.replace('.', '/') + ".class");
33 | if (f.exists()) {
34 | try {
35 | byte[] bytes = getBytes(f);
36 | // if (resolve) {
37 | ret = defineClass(className, bytes, 0, bytes.length);
38 | // }
39 | } catch (IOException ioe) {
40 | System.err.println("Error loading class " + className + " from file " + f.getPath());
41 | ioe.printStackTrace();
42 | // Not supposed to happen
43 | System.exit(1);
44 | }
45 | }
46 | }
47 | if (ret == null) {
48 | return resolve ? findSystemClass(className)
49 | : getParent().loadClass(className);
50 | } else {
51 | return ret;
52 | }
53 | }
54 |
55 | private byte[] getBytes(File f) throws IOException {
56 | int size = (int)f.length();
57 | byte[] bytes = new byte[size];
58 | int remaining = size;
59 | int i = 0;
60 | FileInputStream fis = new FileInputStream(f);
61 | while (remaining > 0) {
62 | int n = fis.read(bytes, i, remaining);
63 | if (n == -1) break;
64 | remaining -= n;
65 | i += n;
66 | }
67 | return bytes;
68 | }
69 |
70 | public static void main(String[] args) throws Exception {
71 | Class> c = new TaskTestClassLoader(Thread.currentThread().getContextClassLoader()).loadClass(args[0], true);
72 | c.newInstance();
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/test/kilim/test/TestBasicBlock.java:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2006, Sriram Srinivasan
2 | *
3 | * You may distribute this software under the terms of the license
4 | * specified in the file "License"
5 | */
6 |
7 | package kilim.test;
8 |
9 | import kilim.analysis.MethodFlow;
10 |
11 | public class TestBasicBlock extends Base {
12 |
13 | @Override
14 | protected void setUp() throws Exception {
15 | cache("kilim.test.ex.ExBasicBlock");
16 | }
17 |
18 | public void testNumFlows() {
19 | assertEquals(getFlows().size(), 8);
20 | }
21 |
22 | private void checkSize(String methodName, int expectedSize) {
23 | MethodFlow f = getFlow(methodName);
24 | if (f == null)
25 | return;
26 | if (f.getBasicBlocks().size() != expectedSize) {
27 | fail("Method " + methodName + ": expected flow size = "
28 | + expectedSize + ", instead got "
29 | + f.getBasicBlocks().size());
30 | }
31 | }
32 |
33 | public void testNoopSize() {
34 | checkSize("noop", 1);
35 | }
36 |
37 | public void testLoopSize() {
38 | checkSize("loop", 4);
39 | }
40 |
41 | public void testExceptionSize() {
42 | checkSize("exception", 6);
43 | }
44 |
45 | public void testNestedSize() {
46 | checkSize("nestedloop", 6);
47 | }
48 |
49 | public void testComplexSize() {
50 | checkSize("complex", 12);
51 | }
52 |
53 | public void testNoopCov() {
54 | checkCov("noop");
55 | }
56 |
57 | public void testLoopCov() {
58 | checkCov("loop");
59 | }
60 |
61 | public void testExceptionCov() {
62 | checkCov("exception");
63 | }
64 |
65 | public void testNestedCov() {
66 | checkCov("nestedloop");
67 | }
68 |
69 | public void testComplexCov() {
70 | checkCov("complex");
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/test/kilim/test/TestClassInfo.java:
--------------------------------------------------------------------------------
1 | package kilim.test;
2 |
3 | import java.util.LinkedList;
4 | import java.util.List;
5 |
6 | import junit.framework.TestCase;
7 | import kilim.analysis.ClassInfo;
8 |
9 | public class TestClassInfo extends TestCase {
10 | public void testContains() throws Exception {
11 | List classInfoList = new LinkedList();
12 |
13 | ClassInfo classOne = new ClassInfo("kilim/S_01.class", "whocares".getBytes("UTF-8"));
14 | classInfoList.add(classOne);
15 |
16 | ClassInfo classTwo = new ClassInfo("kilim/S_01.class", "whocares".getBytes("UTF-8"));
17 | assertTrue(classInfoList.contains(classTwo));
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/test/kilim/test/TestDynamicWeaver.java:
--------------------------------------------------------------------------------
1 | package kilim.test;
2 |
3 | import java.util.Arrays;
4 | import java.util.HashSet;
5 | import java.util.List;
6 |
7 | import junit.framework.TestCase;
8 | import kilim.analysis.ClassInfo;
9 | import kilim.tools.Javac;
10 | import kilim.tools.Weaver;
11 |
12 | public class TestDynamicWeaver extends TestCase {
13 | /**
14 | * Sample code to test a wide range of functionality: separate packages, import statements,
15 | * mutually recursive classes across packages, Pausable methods, inner classes, public
16 | * and non-public classes, etc.
17 | */
18 | String code1 =
19 | "package code1;" +
20 | "import java.io.IOException;" +
21 | "import kilim.*;" +
22 | "public class A {" +
23 | " code2.B bar;" +
24 | " class Inner {" +
25 | " void foo() throws Pausable, IOException {" +
26 | " for (int i = 0; i < 10; i++) {" +
27 | " Outer.xxx();" +
28 | " }" +
29 | " }" +
30 | " }" +
31 | "}" +
32 | "class Outer { " +
33 | " static void xxx() throws Pausable, java.io.IOException {}" +
34 | "}";
35 |
36 | String code2 =
37 | "package code2;" +
38 | "public class B { " +
39 | " code1.A foo;" +
40 | "}";
41 |
42 |
43 | public List compile() throws Exception {
44 | List classes = Javac.compile(Arrays.asList(code1, code2));
45 | assertTrue(classes.size() == 4);
46 | HashSet expectedClasses = new HashSet(
47 | Arrays.asList("code1.A", "code1.A$Inner", "code1.Outer", "code2.B"));
48 |
49 | for (ClassInfo cl : classes) {
50 | assertTrue(expectedClasses.contains(cl.className));
51 | assertTrue(cl.bytes.length > 200);
52 | }
53 | return classes;
54 | }
55 |
56 | public void testWeave() throws Exception {
57 | List classes = compile();
58 |
59 | classes = new Weaver().weave(classes);
60 |
61 |
62 | HashSet expectedClasses = new HashSet(
63 | Arrays.asList("kilim.S_I", "code1.A$Inner", "code1.Outer"));
64 |
65 | assertTrue(expectedClasses.size() == classes.size());
66 |
67 | for (ClassInfo cl : classes) {
68 | assertTrue(expectedClasses.contains(cl.className));
69 | assertTrue(cl.bytes != null && cl.bytes.length > 0);
70 | // ensure classes are loadable
71 | TestClassLoader cll = new TestClassLoader();
72 | Class> c = null;
73 | try {
74 | c = cll.loadClass(cl.className);
75 | // The only class that should be loadable is "kilim.S_I"
76 | assertTrue(c.getName().startsWith("kilim"));
77 | } catch (ClassNotFoundException ignore) {
78 | // the new classes should not have been in the classpath, and
79 | // ClassNotFoundException is thrown as expected
80 | assertTrue(cl.className.startsWith("code"));
81 | // define these classes
82 | try {
83 | cll.load(cl);
84 | } catch (Throwable t) {
85 | fail(t.getMessage());
86 | }
87 | }
88 | }
89 | }
90 |
91 | static class TestClassLoader extends ClassLoader {
92 | public void load(ClassInfo cl) {
93 | Class> c = super.defineClass(cl.className, cl.bytes, 0, cl.bytes.length);
94 | super.resolveClass(c);
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/test/kilim/test/TestExprs.java:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2006, Sriram Srinivasan
2 | *
3 | * You may distribute this software under the terms of the license
4 | * specified in the file "License"
5 | */
6 |
7 | package kilim.test;
8 |
9 |
10 | /**
11 | * An attempt to exercise all the bytecode associated with primitive
12 | * types (loading/storing from registers, array operations, arithmetic operations etc.)
13 | */
14 | public class TestExprs extends Base {
15 | public void testInts() throws Exception {
16 | cache("kilim.test.ex.ExInts");
17 | }
18 | public void testLongs() throws Exception {
19 | cache("kilim.test.ex.ExLongs");
20 | }
21 |
22 | public void testFloats() throws Exception {
23 | cache("kilim.test.ex.ExFloats");
24 | }
25 |
26 | public void testDoubles() throws Exception {
27 | cache("kilim.test.ex.ExDoubles");
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/test/kilim/test/TestFlow.java:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2006, Sriram Srinivasan
2 | *
3 | * You may distribute this software under the terms of the license
4 | * specified in the file "License"
5 | */
6 |
7 | package kilim.test;
8 |
9 | import static kilim.Constants.D_BYTE;
10 | import kilim.analysis.BasicBlock;
11 | import kilim.analysis.Frame;
12 | import kilim.analysis.IncompatibleTypesException;
13 | import kilim.analysis.MethodFlow;
14 | import kilim.analysis.TypeDesc;
15 | import kilim.analysis.Value;
16 |
17 | public class TestFlow extends Base {
18 |
19 | @Override
20 | protected void setUp() throws Exception {
21 | cache("kilim.test.ex.ExFlow");
22 | }
23 |
24 | public void testMerge() throws IncompatibleTypesException {
25 | MethodFlow flow = getFlow("loop");
26 | if (flow == null)
27 | return;
28 | // Make sure the merging is fine. There used to be a bug
29 | assertEquals("Lkilim/test/ex/ExA;", TypeDesc.mergeType("Lkilim/test/ex/ExC;", "Lkilim/test/ex/ExD;"));
30 | assertEquals("Lkilim/test/ex/ExA;", TypeDesc.mergeType("Lkilim/test/ex/ExD;", "Lkilim/test/ex/ExC;"));
31 | BasicBlock bb = getBBForMethod(flow, "join");
32 | assertTrue(bb != null);
33 | Frame f = bb.startFrame;
34 | // Check Locals
35 | // assertEquals("Lkilim/test/ex/ExFlow;", f.getLocal(0));
36 | assertEquals("Lkilim/test/ex/ExA;", f.getLocal(1).getTypeDesc());
37 | // assertSame(D_INT, f.getLocal(2));
38 | // Check operand stack
39 | assertSame(D_BYTE, f.getStack(0).getTypeDesc());
40 | assertEquals("Lkilim/test/ex/ExFlow;", f.getStack(1).getTypeDesc());
41 | assertEquals("Lkilim/test/ex/ExA;", f.getStack(2).getTypeDesc());
42 | }
43 |
44 | public void testConstants() throws IncompatibleTypesException {
45 | MethodFlow flow = getFlow("loop");
46 | if (flow == null)
47 | return;
48 | BasicBlock bb = getBBForMethod(flow, "join");
49 | Frame f = bb.startFrame;
50 | assertSame(f.getLocal(2).getConstVal(), Value.NO_VAL);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/test/kilim/test/TestFrame.java:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2006, Sriram Srinivasan
2 | *
3 | * You may distribute this software under the terms of the license
4 | * specified in the file "License"
5 | */
6 |
7 | package kilim.test;
8 |
9 | import static kilim.Constants.D_ARRAY_BOOLEAN;
10 | import static kilim.Constants.D_BOOLEAN;
11 | import static kilim.Constants.D_DOUBLE;
12 | import static kilim.Constants.D_INT;
13 | import static kilim.Constants.D_LONG;
14 | import static kilim.Constants.D_OBJECT;
15 | import static kilim.Constants.D_RETURN_ADDRESS;
16 | import static kilim.Constants.D_STRING;
17 | import static kilim.Constants.D_THROWABLE;
18 | import static kilim.Constants.D_UNDEFINED;
19 | import kilim.analysis.BasicBlock;
20 | import kilim.analysis.Frame;
21 | import kilim.analysis.MethodFlow;
22 | import kilim.analysis.Usage;
23 | import kilim.analysis.Value;
24 |
25 | public class TestFrame extends Base {
26 | protected void setUp() throws Exception {
27 | cache("kilim.test.ex.ExFrame");
28 | }
29 |
30 | public void testMethodFrame() {
31 | MethodFlow flow = getFlow("kitchensink");
32 | if (flow == null)
33 | return;
34 | for (BasicBlock bb : flow.getBasicBlocks()) {
35 | if (bb.startPos == 0) {
36 | Frame f = bb.startFrame;
37 | assertEquals("Lkilim/test/ex/ExFrame;", f.getLocal(0).getTypeDesc());
38 | assertSame(D_INT, f.getLocal(1).getTypeDesc());
39 | assertSame(D_LONG, f.getLocal(2).getTypeDesc());
40 | // Note LONG and BOOLEAN take up two words
41 | assertSame(D_BOOLEAN, f.getLocal(4).getTypeDesc());
42 | assertSame(D_DOUBLE, f.getLocal(5).getTypeDesc());
43 | assertEquals("[[Ljava/lang/String;", f.getLocal(7).getTypeDesc());
44 | }
45 | }
46 | }
47 |
48 | public void testStack() {
49 | Frame f = new Frame(1, 4);
50 | f.push(Value.make(0, D_LONG));
51 | f.push(Value.make(0, D_DOUBLE));
52 | f.push(Value.make(0, D_ARRAY_BOOLEAN));
53 | f.push(Value.make(0, D_RETURN_ADDRESS));
54 | f.pop();
55 | f.pop();
56 | f.pop();
57 | assertSame(D_LONG, f.pop().getTypeDesc());
58 | }
59 |
60 | public void testLocals() {
61 | Frame f = new Frame(4, 1);
62 | f.setLocal(0, Value.make(10, D_LONG));
63 | f.setLocal(2, Value.make(12, D_DOUBLE));
64 | f.setLocal(0, Value.make(20, D_INT));
65 | f.setLocal(1, Value.make(31, D_STRING));
66 | assertSame(D_INT, f.getLocal(0).getTypeDesc());
67 | assertSame(D_STRING, f.getLocal(1).getTypeDesc());
68 | assertSame(D_DOUBLE, f.getLocal(2).getTypeDesc());
69 | }
70 |
71 | public void testMergeUnchangedTypes() {
72 | Frame f = new Frame(4, 4);
73 | f.setLocal(1, Value.make(0, D_INT));
74 | f.setLocal(2, Value.make(0, "[Ljava/lang/Object;"));
75 | f.setLocal(3, Value.make(0, "Ljava/lang/reflect/AccessibleObject;"));
76 | f.push(Value.make(0, "Ljava/lang/Object;"));
77 |
78 | Frame g = new Frame(4, 4);
79 | g.setLocal(1, Value.make(0, D_INT));
80 | g.setLocal(2, Value.make(0, "[Ljava/lang/Object;"));
81 | g.setLocal(3, Value.make(0, "Ljava/lang/reflect/Field;"));
82 | g.push(Value.make(0, "Ljava/io/Serializable;"));
83 | Usage usage = new Usage(4);
84 | usage.setLiveIn(1);
85 | usage.setLiveIn(2);
86 | usage.setLiveIn(3);
87 | assertEquals(f, f.merge(g, /* localsOnly= */false, usage));
88 | }
89 |
90 | public void testMergeChangedTypes() {
91 | Frame f = new Frame(4, 4);
92 | f.setLocal(0, Value.make(0, D_INT));
93 | f.setLocal(1, Value.make(0, "Ljava/lang/reflect/Field;"));
94 | f.setLocal(2, Value.make(0, "[Ljava/lang/Object;"));
95 | f.push(Value.make(0, "Ljava/io/Serializable;"));
96 |
97 | Frame g = new Frame(4, 4);
98 | g.setLocal(0, Value.make(0, D_INT));
99 | g.setLocal(1, Value.make(0, "Ljava/lang/reflect/AccessibleObject;"));
100 | g.setLocal(2, Value.make(0, "[Ljava/lang/Object;"));
101 | g.push(Value.make(0, "Ljava/lang/Object;"));
102 |
103 | Usage usage = new Usage(4);
104 | for (int i = 0; i < 4; i++)
105 | usage.setLiveIn(i);
106 | Frame h = f.merge(g, /* localsOnly= */false, usage);
107 | assertNotSame(f, h);
108 | for (int i = 0; i < 4; i++) {
109 | assertEquals(g.getLocal(i), h.getLocal(i));
110 | }
111 | }
112 |
113 | public void testMergeUnchangedIfNoUsage() {
114 | Frame f = new Frame(4, 4);
115 | f.setLocal(0, Value.make(0, D_RETURN_ADDRESS));
116 | f.setLocal(1, Value.make(0, D_INT));
117 | f.setLocal(2, Value.make(0, D_DOUBLE));
118 |
119 | Frame g = new Frame(4, 4);
120 | g.setLocal(0, Value.make(0, D_INT));
121 | g.setLocal(1, Value.make(0, D_DOUBLE));
122 | g.setLocal(3, Value.make(0, D_THROWABLE));
123 |
124 | Usage noUsage = new Usage(4); // default, everything is untouched.
125 | assertSame(f, f.merge(g, /* localsOnly= */true, noUsage));
126 |
127 | for (int i = 0; i < 4; i++) {
128 | noUsage.write(i); // set everything to OVERWRITTEN
129 | }
130 | assertSame(f, f.merge(g, /* localsOnly= */true, noUsage));
131 | }
132 |
133 | public void testIncompatibleMerge() {
134 | Frame f = new Frame(4, 4);
135 | f.setLocal(0, Value.make(0, D_OBJECT));
136 | Frame g = new Frame(4, 4);
137 | g.setLocal(0, Value.make(0, D_INT));
138 |
139 | Usage usage = new Usage(4);
140 | for (int i = 0; i < 4; i++) {
141 | usage.setLiveIn(i); // set everything to READ
142 | }
143 | f = f.merge(g, true, usage);
144 | assertTrue(f.getLocal(0).getTypeDesc() == D_UNDEFINED);
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/test/kilim/test/TestInvalidPausables.java:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2006, Sriram Srinivasan
2 | *
3 | * You may distribute this software under the terms of the license
4 | * specified in the file "License"
5 | */
6 |
7 | package kilim.test;
8 |
9 | import junit.framework.TestCase;
10 | import kilim.KilimException;
11 | import kilim.mirrors.Detector;
12 | import kilim.tools.Weaver;
13 |
14 | public class TestInvalidPausables extends TestCase {
15 | private void ensureException(String className) {
16 | try {
17 | Weaver.weaveClass2(className, Detector.DEFAULT);
18 | fail("Expected weave exception while processing " + className);
19 | } catch (KilimException ke) {
20 | } catch (Exception e) {
21 | fail(e.toString());
22 | }
23 | }
24 | public void testWeaveConstructor() {
25 | ensureException("kilim.test.ex.ExInvalidConstructor");
26 | }
27 | public void testWeaveSynchronized() {
28 | ensureException("kilim.test.ex.ExInvalidSynchronized");
29 | ensureException("kilim.test.ex.ExInvalidSynchronized1");
30 | }
31 | public void testWeaveStatic() {
32 | ensureException("kilim.test.ex.ExInvalidStaticBlock");
33 | }
34 |
35 | public void testWeaveMethod() {
36 | ensureException("kilim.test.ex.ExInvalidCallP_NP");
37 | }
38 |
39 | public void testWeaveSuperPausable() {
40 | ensureException("kilim.test.ex.ExInvalidNPDerived");
41 |
42 | }
43 |
44 | public void testWeaveSuperNotPausable() {
45 | ensureException("kilim.test.ex.ExInvalidPDerived");
46 | }
47 |
48 | public void testWeaveInterfacePausable() {
49 | ensureException("kilim.test.ex.ExInvalidPImp");
50 |
51 | }
52 |
53 | public void testWeaveInterfaceNotPausable() {
54 | ensureException("kilim.test.ex.ExInvalidNPImp");
55 |
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/test/kilim/test/TestJSR.java:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2006, Sriram Srinivasan
2 | *
3 | * You may distribute this software under the terms of the license
4 | * specified in the file "License"
5 | */
6 |
7 | package kilim.test;
8 |
9 | import java.util.ArrayList;
10 |
11 | import kilim.analysis.BasicBlock;
12 | import kilim.analysis.MethodFlow;
13 |
14 | public class TestJSR extends Base {
15 | public void testJSRSizes() throws Exception {
16 | String className = "kilim.test.ex.ExJSR";
17 | try {
18 | Class.forName(className);
19 | } catch (ClassNotFoundException cnfe) {
20 | fail("Please use jasmin to compile " + className);
21 | } catch (VerifyError e) {
22 | fail("Verification error for " + className + ": " + e.getMessage());
23 | }
24 | cache(className);
25 | MethodFlow flow = getFlow("simpleJSR");
26 | assertEquals(3, flow.getBasicBlocks().size());
27 | flow = getFlow("pausableJSR1");
28 | // System.out.println(flow.getBasicBlocks());
29 | assertEquals(4, flow.getBasicBlocks().size());
30 |
31 | flow = getFlow("pausableJSR2");
32 | ArrayList bbs = flow.getBasicBlocks();
33 | assertEquals(7, bbs.size());
34 |
35 | // make sure the blocks are unique
36 | int flag = 1 << 12;
37 | for (BasicBlock bb: bbs) {
38 | assertFalse("BasicBlock list contains duplicates", bb.hasFlag(flag));
39 | bb.setFlag(flag);
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/test/kilim/test/TestTypeDesc.java:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2006, Sriram Srinivasan
2 | *
3 | * You may distribute this software under the terms of the license
4 | * specified in the file "License"
5 | */
6 |
7 | package kilim.test;
8 |
9 | import kilim.analysis.IncompatibleTypesException;
10 | import kilim.analysis.TypeDesc;
11 | import junit.framework.TestCase;
12 | import static kilim.Constants.*;
13 | import java.lang.reflect.*;
14 | import java.util.Arrays;
15 |
16 | public class TestTypeDesc extends TestCase {
17 | public void testInterning() throws Exception {
18 | // Verify all strings in Constants that start with "D_"
19 | // are indeed interned.
20 | Class> c = Class.forName("kilim.Constants");
21 | Field[] fields = c.getFields();
22 | for (Field f:fields) {
23 | if (f.getName().startsWith("D_")) {
24 | String val = f.get(null).toString();
25 | assertSame(TypeDesc.getInterned(new String(val)), val);
26 | }
27 | }
28 | }
29 |
30 | public void testComponentType() {
31 | assertSame(TypeDesc.getComponentType("[J"), D_LONG);
32 | assertSame(TypeDesc.getComponentType("[Ljava/lang/String;"), D_STRING);
33 | }
34 |
35 | public void testCommonSuperTypes() {
36 | // Two interfaces => Object. Checking interning at the same time.
37 | assertSame(TypeDesc.commonSuperType("Ljava/io/Serializable;", "Ljava/lang/Comparable;"),
38 | D_OBJECT);
39 | assertEquals(TypeDesc.commonSuperType("Lkilim/BasicBlock;",
40 | "Lkilim/BasicBlock;"), "Lkilim/BasicBlock;");
41 | assertSame(TypeDesc.commonSuperType("[Z", "[Z"), D_ARRAY_BOOLEAN);
42 |
43 | // least upper bound of Field and Method is AccessibleObject
44 | assertEquals("Ljava/lang/reflect/AccessibleObject;",
45 | TypeDesc.commonSuperType("Ljava/lang/reflect/Field;",
46 | "Ljava/lang/reflect/Method;"));
47 |
48 | // least upper bound of Field and AccessibleObject is AccessibleObject
49 | assertEquals("Ljava/lang/reflect/AccessibleObject;",
50 | TypeDesc.commonSuperType("Ljava/lang/reflect/Field;",
51 | "Ljava/lang/reflect/AccessibleObject;"));
52 |
53 | // Same as above, but flip the order to see if it is sensitive.
54 | assertEquals("Ljava/lang/reflect/AccessibleObject;",
55 | TypeDesc.commonSuperType("Ljava/lang/reflect/Field;",
56 | "Ljava/lang/reflect/AccessibleObject;"));
57 |
58 | assertEquals("Lkilim/test/ex/ExA;",
59 | TypeDesc.commonSuperType("Lkilim/test/ex/ExA;", "Lkilim/test/ex/ExD;"));
60 |
61 | assertEquals("Lkilim/test/ex/ExA;",
62 | TypeDesc.commonSuperType("Lkilim/test/ex/ExD;", "Lkilim/test/ex/ExA;"));
63 |
64 | assertEquals("Lkilim/test/ex/ExA;",
65 | TypeDesc.commonSuperType("Lkilim/test/ex/ExC;", "Lkilim/test/ex/ExD;"));
66 |
67 | }
68 |
69 | public void testArray() throws IncompatibleTypesException {
70 | assertSame(D_OBJECT,
71 | TypeDesc.mergeType("Lkilim/test/ex/ExC;", "[Z"));
72 |
73 | assertSame(D_OBJECT,
74 | TypeDesc.mergeType("[Z", "Lkilim/test/ex/ExC;"));
75 | }
76 |
77 | public void testNull() throws IncompatibleTypesException {
78 | assertSame(D_NULL, TypeDesc.mergeType(D_NULL, D_NULL));
79 | assertSame(D_OBJECT, TypeDesc.mergeType(D_OBJECT, D_NULL));
80 | assertSame(D_OBJECT, TypeDesc.mergeType(D_NULL, D_OBJECT));
81 | }
82 | public void testNumArgs() throws IncompatibleTypesException {
83 | assertTrue(TypeDesc.getNumArgumentTypes("()V") == 0);
84 | assertTrue(TypeDesc.getNumArgumentTypes("(Ljava/lang/String;[[[ZZBCDSIJF)V") == 10);
85 | }
86 |
87 | public void testReturnType() throws IncompatibleTypesException {
88 | assertTrue(TypeDesc.getReturnTypeDesc("()V") == D_VOID);
89 | assertTrue(TypeDesc.getReturnTypeDesc("()[I") == D_ARRAY_INT);
90 | assertTrue(TypeDesc.getReturnTypeDesc("(IIII)[Ljava/lang/Throwable;").equals("[Ljava/lang/Throwable;"));
91 | }
92 |
93 | public void testArgTypes() throws IncompatibleTypesException {
94 | String[] types = TypeDesc.getArgumentTypes("([Ljava/lang/String;[[[ZZBCDSIJF)V");
95 | String[] expected = new String[] {"[Ljava/lang/String;","[[[Z", D_BOOLEAN, D_BYTE,D_CHAR,
96 | D_DOUBLE,D_SHORT,D_INT,D_LONG,D_FLOAT};
97 | assertTrue(Arrays.equals(types, expected));
98 | }
99 |
100 | public void testMerge() throws IncompatibleTypesException {
101 | // testCommonSuperTypes() has already checked many combinations of
102 | // classes, arrays and interfaces. Handle null etc.
103 |
104 | // Null + String => String
105 | assertSame(D_STRING, TypeDesc.mergeType(D_NULL, D_STRING));
106 |
107 | // Null + X == X (order of D_NULL flipped this time)
108 | assertSame(D_ARRAY_DOUBLE, TypeDesc.mergeType("[D", D_NULL));
109 |
110 | // primitive types should return the same
111 | assertEquals(D_DOUBLE, TypeDesc.mergeType(D_DOUBLE, D_DOUBLE));
112 |
113 | // Array + Object -> Array
114 | assertSame(D_OBJECT, TypeDesc.mergeType("[I", D_OBJECT));
115 |
116 | assertSame(D_OBJECT, TypeDesc.mergeType("[I", "[D"));
117 |
118 | // common supertype of arrays
119 | assertEquals("[Ljava/lang/reflect/AccessibleObject;",
120 | TypeDesc.mergeType("[Ljava/lang/reflect/Field;","[Ljava/lang/reflect/Method;"));
121 |
122 | // A inherits from B ==> merge(A[], B[]) = B[]
123 | assertEquals("[Ljava/lang/reflect/AccessibleObject;",
124 | TypeDesc.mergeType("[Ljava/lang/reflect/Method;", "[Ljava/lang/reflect/AccessibleObject;"));
125 |
126 | // A inherits from B ==> merge(A[], B[]) = A[]
127 | assertEquals("[Ljava/lang/reflect/AccessibleObject;",
128 | TypeDesc.mergeType("[Ljava/lang/reflect/AccessibleObject;", "[Ljava/lang/reflect/Method;"));
129 |
130 | }
131 |
132 | public void testInvalidCombinations() {
133 | assertInvalidCombo("I", D_OBJECT);
134 | assertInvalidCombo(D_OBJECT, D_INT);
135 | assertInvalidCombo("Meaningless", D_OBJECT);
136 | }
137 |
138 | private void assertInvalidCombo(String a, String b) {
139 | try {
140 | TypeDesc.mergeType(a,b);
141 | fail("Types '" + a + "' and '" + b + "' aren't supposed to be compatible");
142 | } catch (IncompatibleTypesException ignore) {
143 | // Good. It is supposed to fail
144 | }
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/test/kilim/test/TestUsage.java:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2006, Sriram Srinivasan
2 | *
3 | * You may distribute this software under the terms of the license
4 | * specified in the file "License"
5 | */
6 |
7 | package kilim.test;
8 |
9 | import java.util.ArrayList;
10 |
11 | import kilim.analysis.Usage;
12 | import junit.framework.TestCase;
13 |
14 | public class TestUsage extends TestCase {
15 | /**
16 | * Tests whether a bunch of reads and writes produces the appropriate live-in
17 | */
18 | public void testReadWrite() {
19 | Usage u = new Usage(4);
20 | u.read(1);
21 | u.read(2);
22 | u.write(2);
23 | u.write(3);
24 | u.evalLiveIn(new ArrayList());
25 | assertFalse(u.isLiveIn(0));
26 | assertTrue(u.isLiveIn(1));
27 | assertTrue(u.isLiveIn(2));
28 | assertFalse(u.isLiveIn(3));
29 | }
30 |
31 | public void testChange() {
32 | Usage u = new Usage(31);
33 | Usage ufollow1 = new Usage(31);
34 | Usage ufollow2 = new Usage(31);
35 | // 29:R
36 | // 30:W
37 | // Usage 1 and 2.
38 | // 28:in 28:not_in
39 | // 29:in 29:not_in
40 | // 30:in 30:in
41 | // Expected usage.in : 28:in 29:in 30:not_in
42 | u.read(29); u.write(30);
43 | ufollow1.setLiveIn(28); ufollow1.setLiveIn(29); ufollow1.setLiveIn(30);
44 | ufollow2.setLiveIn(30);
45 | ArrayList ua = new ArrayList(2);
46 | ua.add(ufollow1); ua.add(ufollow2);
47 | assertTrue(u.evalLiveIn(ua)); // should return changed == true
48 | for (int i = 0; i < 28; i++) {
49 | assertFalse(u.isLiveIn(i));
50 | }
51 | assertTrue(u.isLiveIn(28));
52 | assertTrue(u.isLiveIn(29));
53 | assertFalse(u.isLiveIn(30));
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/test/kilim/test/TestValue.java:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2006, Sriram Srinivasan
2 | *
3 | * You may distribute this software under the terms of the license
4 | * specified in the file "License"
5 | */
6 |
7 | package kilim.test;
8 |
9 | import kilim.analysis.Value;
10 | import junit.framework.TestCase;
11 | import static kilim.Constants.*;
12 |
13 | public class TestValue extends TestCase {
14 | public void testSameSiteMerge() {
15 | Value v = Value.make(10, D_STRING);
16 | v = v.merge(Value.make(20, D_OBJECT));
17 | Value oldV = v;
18 | for (int i = 0; i < 10; i++) {
19 | v = v.merge(Value.make(10, D_STRING));
20 | }
21 | assertSame(oldV, v);
22 | }
23 |
24 | public void testDifferentSitesMerge() {
25 | Value v1 = Value.make(2, D_INT);
26 | Value v2 = Value.make(3, D_INT);
27 | Value v3 = Value.make(5, D_INT);
28 | Value v = v1.merge(v2);
29 | v = v.merge(v3);
30 | assertTrue(v.getNumSites() == 3);
31 | int[] sites = v.getCreationSites();
32 | int prod = 1;
33 | for (int i = 0; i < 3; i++) {
34 | prod *= sites[i];
35 | }
36 | assertTrue(prod == 30);
37 |
38 | Value oldV = v;
39 |
40 | // Ensure order of merges don't matter
41 | v = v3.merge(v2);
42 | v = v.merge(v1);
43 | assertEquals(v, oldV);
44 | }
45 |
46 |
47 | public void testTypeMerge() {
48 | Value v1 = Value.make(2, "Lkilim/test/ex/ExC;");
49 | Value v2 = Value.make(3, "Lkilim/test/ex/ExD;");
50 | Value v3 = Value.make(5, "Lkilim/test/ex/ExA;");
51 |
52 | Value v = v1.merge(v1);
53 | assertSame(v, v1);
54 |
55 | v = v1.merge(v2);
56 | assertEquals("Lkilim/test/ex/ExA;", v.getTypeDesc());
57 | v = v3.merge(v2);
58 | assertEquals("Lkilim/test/ex/ExA;", v.getTypeDesc());
59 |
60 | Value v4 = Value.make(7, D_INT);;
61 | v = v3.merge(v4);
62 | assertSame(D_UNDEFINED, v.getTypeDesc());
63 | }
64 |
65 | public void testConstMerge() {
66 | Value v1 = Value.make(99, D_STRING, "String1");
67 | Value v2 = Value.make(100, D_STRING, new String("String1")); // create a new String
68 | Value v= v1.merge(v2);
69 | assertTrue(v.getConstVal().equals("String1"));
70 | v = v1.merge(Value.make(101, D_STRING, "Some other string"));
71 | assertTrue(v.getConstVal().equals(Value.NO_VAL));
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/test/kilim/test/ex/ExA.java:
--------------------------------------------------------------------------------
1 | package kilim.test.ex;
2 |
3 |
4 | public class ExA {
5 |
6 | }
7 |
--------------------------------------------------------------------------------
/test/kilim/test/ex/ExB.java:
--------------------------------------------------------------------------------
1 | package kilim.test.ex;
2 |
3 | public class ExB extends ExA {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/test/kilim/test/ex/ExBasicBlock.java:
--------------------------------------------------------------------------------
1 | package kilim.test.ex;
2 |
3 | import kilim.Pausable;
4 | import kilim.Task;
5 |
6 | public class ExBasicBlock {
7 | void noop() throws ArrayIndexOutOfBoundsException {
8 | }
9 |
10 | static void pausable() throws Pausable {
11 | "afakflkaflakd".getBytes();
12 | }
13 |
14 | static int testFiber(Object testArgs1, Object[] testArgs) throws Pausable {
15 | Task.getCurrentTask();
16 | int a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0;
17 | for (int i = 0; i < g; i++) {
18 | g = a + b + c + d + e + f;
19 | }
20 | return g;
21 | }
22 |
23 | int loop() throws Pausable {
24 | int sum = 0;
25 | for (int i = 0; i < 10; i++) {
26 | sum++;
27 | }
28 | return sum;
29 | }
30 |
31 | void nestedloop() throws Pausable {
32 | for (int i = 0; i < 100; i++) {
33 | while (i > 10) {
34 | i--;
35 | }
36 | }
37 | }
38 |
39 | void exception() throws Pausable {
40 | try {
41 | try {
42 | pausable();
43 | } catch (ArrayIndexOutOfBoundsException e) {
44 | try {
45 | e.printStackTrace();
46 | } catch (Throwable t) {
47 | noop();
48 | }
49 | }
50 | } finally {
51 | noop();
52 | }
53 | }
54 |
55 | void complex() throws Pausable {
56 | double d = 10.0;
57 | Object o = new Object();
58 | for (int i = 0; i < 100; i++) {
59 | try {
60 | if (d > 10.3 && d < 10.5) {
61 | d = 20.0;
62 | try {
63 | synchronized(o) {
64 | o.hashCode();
65 | }
66 | } catch (RuntimeException re) {
67 | throw new Error(re.toString());
68 | }
69 | }
70 | } finally {
71 | d = 100.0;
72 | }
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/test/kilim/test/ex/ExC.java:
--------------------------------------------------------------------------------
1 | package kilim.test.ex;
2 |
3 | public class ExC extends ExA {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/test/kilim/test/ex/ExD.java:
--------------------------------------------------------------------------------
1 | package kilim.test.ex;
2 |
3 | public class ExD extends ExB {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/test/kilim/test/ex/ExEx.java:
--------------------------------------------------------------------------------
1 | package kilim.test.ex;
2 |
3 | import kilim.Pausable;
4 |
5 | public class ExEx {
6 | void noop(int i) throws Pausable {}
7 |
8 | void f() throws Pausable {
9 | int i = 0;
10 | try {
11 | noop(i);
12 | } catch (Exception e) {
13 | noop(i);
14 | }
15 |
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/test/kilim/test/ex/ExException.java:
--------------------------------------------------------------------------------
1 | package kilim.test.ex;
2 |
3 | public class ExException extends Exception {
4 | private static final long serialVersionUID = 1L;
5 |
6 | public ExException(String message) {
7 | super(message);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/test/kilim/test/ex/ExExpr.java:
--------------------------------------------------------------------------------
1 | package kilim.test.ex;
2 |
3 | class ExInts {
4 | int[] intstuff() {
5 | int i = 10;
6 | int[] arr = new int[10];
7 | arr[1] = arr[2] ^ i * 34 - ((arr[3] << 2) / 4);
8 | int j = arr[1];
9 | int k = i;
10 | if (i > j && intr(i) >= k ||
11 | i < j && i != arr[2] && i == arr[3]) {
12 | return null;
13 | }
14 | return arr;
15 | }
16 | int intr(int i) {
17 | if (((i + 5) % 6 > 99) || (-i < 3)) {
18 | return -1;
19 | } else {
20 | char c = '\u33d3';
21 | return (int)c;
22 | }
23 | }
24 | int bits(int i, int j) {
25 | return (~i | j) & (i >>> 2) & (j >> 3);
26 | }
27 | }
28 |
29 |
30 | class ExLongs {
31 | long[] longstuff() {
32 | long i = 10;
33 | long[] arr = new long[10];
34 | arr[1] = arr[2] ^ i * 34 - ((arr[3] << 2) / 4);
35 | long j = arr[1];
36 | long k = i;
37 | if (i > -j && longr(i) >= k ||
38 | i < j && i != arr[2] - 3L && i == arr[3]) {
39 | return null;
40 | }
41 | return arr;
42 | }
43 | long longr(long i) {
44 | if (((i + 5) % 6 > 99) || (i < 3)) {
45 | return -1;
46 | } else {
47 | char c = '\u33d3';
48 | return (long)c;
49 | }
50 | }
51 | long bits(long i, long j) {
52 | return (~i | j) & (i >>> 2) & (j >> 3);
53 | }
54 | }
55 |
56 |
57 |
58 | class ExDoubles {
59 | double[] doublestuff() {
60 | double i = 0;
61 |
62 | double[] arr = new double[10];
63 | arr[1] = (arr[2] * 34)/3;
64 | double j = arr[1];
65 | double k = 1;
66 | if (i > j && doubler(i) >= k % 5 ||
67 | i < j && i != arr[2] && i == arr[3]) {
68 | return null;
69 | }
70 | return arr;
71 | }
72 | double doubler(double i) {
73 | if (((i + 5) % 6 > 99) || (i - 2.0 < 3)) {
74 | return -1;
75 | } else {
76 | char c = '\u33d3';
77 | return (double)c;
78 | }
79 | }
80 | }
81 |
82 | class ExFloats {
83 | float[] floatstuff() {
84 | float i = 0;
85 |
86 | float[] arr = new float[10];
87 | arr[1] = (arr[2] * 34)/3;
88 | float j = arr[1];
89 | float k = 1;
90 | if (i > j && floatr(i) >= k % 5 ||
91 | i < j && i - 3 != arr[2] && i == arr[3]) {
92 | return null;
93 | }
94 | return arr;
95 | }
96 | float floatr(float i) {
97 | if (((i + 5) % 6 > 99) || (i - 1.0f < 3)) {
98 | return -1;
99 | } else {
100 | char c = '\u33d3';
101 | return (float)c;
102 | }
103 | }
104 | }
105 |
106 |
107 |
--------------------------------------------------------------------------------
/test/kilim/test/ex/ExFlow.java:
--------------------------------------------------------------------------------
1 | package kilim.test.ex;
2 | import kilim.Pausable;
3 | public class ExFlow {
4 | void loop() throws Pausable {
5 | ExA a = null;
6 | int i;
7 | for (i = 0; i < 10; i++) {
8 | if (i < 5) {
9 | a = new ExC();
10 | } else {
11 | a = new ExD();;
12 | }
13 | }
14 | // at join, the stack must have types of [I,Lkilim.test.ex.ExFlow; and Lkilim.test.ex.ExA;
15 | // local vars-> 0:Lkilim.test.ex.ExFlow; 1:Lkilim.test.ex.ExA; 2:int 3:UNDEFINED
16 | int x = 10 * join(a);
17 | System.out.println(i);
18 | System.out.println(x);
19 | }
20 |
21 | int join(ExA a) throws Pausable { return 10;}
22 | }
--------------------------------------------------------------------------------
/test/kilim/test/ex/ExFrame.java:
--------------------------------------------------------------------------------
1 | package kilim.test.ex;
2 |
3 | public class ExFrame {
4 | double[] kitchensink(int i, long j, boolean b, double d, String[][] s) {
5 | return null;
6 | }
7 | }
--------------------------------------------------------------------------------
/test/kilim/test/ex/ExInvalid.java:
--------------------------------------------------------------------------------
1 | package kilim.test.ex;
2 |
3 | import kilim.Pausable;
4 |
5 | // Just a ununsed public class to make it easy to have a bunch of related test
6 | // classes in one file
7 | public class ExInvalid {
8 | }
9 |
10 |
11 | // illegal to override a non-pausable method with a pausable one
12 | class ExNPSuper {
13 | void foo() {}
14 | }
15 |
16 | //illegal to override a pausable method with a non-pausable one
17 | class ExPSuper {
18 | void foo() throws Pausable {}
19 | }
20 | class ExInvalidNPDerived extends ExPSuper {
21 | void foo() {
22 |
23 | }
24 | }
25 |
26 |
27 | //------------------------------------------------
28 | // Illegal to override an pausable interface method with a non-pausable one
29 | interface ExPFoo {
30 | void foo() throws Pausable;
31 | }
32 | interface ExNPBar extends ExPFoo {}
33 | class ExInvalidNPImp implements ExNPBar {
34 | public void foo() {
35 | }
36 | }
37 |
38 | //------------------------------------------------
39 | //Illegal to override a non-pausable interface method with a pausable one
40 | interface ExNPFoo {
41 | void foo();
42 | }
43 |
44 | interface ExNPBaz extends ExNPFoo {
45 | }
46 |
47 |
48 |
--------------------------------------------------------------------------------
/test/kilim/test/ex/ExInvalidSynchronized.java:
--------------------------------------------------------------------------------
1 | package kilim.test.ex;
2 |
3 | import kilim.Pausable;
4 |
5 |
6 | // Ensure we don't call a pausable method from within a synchronized block
7 | public class ExInvalidSynchronized {
8 | void foo() throws Pausable {}
9 | synchronized void sync() throws Pausable {
10 | foo();
11 | }
12 | }
13 |
14 | class ExInvalidSynchronized1 {
15 | void foo() throws Pausable {}
16 | void sync() throws Pausable {
17 | synchronized(this) {
18 | foo();
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/test/kilim/test/ex/ExJSR.j:
--------------------------------------------------------------------------------
1 | .class public kilim/test/ex/ExJSR
2 | .super java/lang/Object
3 |
4 | .method public ()V
5 | aload 0
6 |
7 | invokenonvirtual java/lang/Object/()V
8 | return
9 | .end method
10 |
11 |
12 | ;; --------------------------------------------
13 | ;; Make a single jsr call that simply returns
14 | ;; --------------------------------------------
15 | .method private static simpleJSR()V
16 | .limit locals 3
17 | .limit stack 3
18 |
19 | bipush 100
20 | istore 1
21 | jsr D
22 | iload 1
23 | istore 0
24 | return
25 |
26 | D:
27 | astore 2
28 | ret 2
29 |
30 | .end method
31 |
32 | ;; --------------------------------------------
33 | ;; Single jsr call that calls pausable method
34 | ;; The number of basic blocks should be 4
35 | ;; --------------------------------------------
36 | .method private static pausableJSR1()V
37 | .throws kilim/Pausable
38 | .limit locals 3
39 | .limit stack 3
40 | ;; BB 0
41 | bipush 100
42 | istore 1
43 | jsr D
44 | ;; BB 1
45 | iload 1
46 | istore 0
47 | return
48 |
49 | D:
50 | ;; BB 2
51 | astore 2
52 | ;; BB 3
53 | invokestatic kilim/test/ex/ExBasicBlock/pausable()V
54 | ret 2
55 |
56 | .end method
57 |
58 |
59 | ;; --------------------------------------------
60 | ;; Multiple jsr calls to a pausable subr
61 | ;; The number of basic blocks should be 7
62 | ;; because the number of basic blocks without
63 | ;; inlining is 5, and inlining the second
64 | ;; jsr adds another two.
65 | ;; --------------------------------------------
66 | .method private static pausableJSR2()V
67 | .throws kilim/Pausable
68 | .limit locals 3
69 | .limit stack 3
70 |
71 | bipush 100
72 | istore 1
73 | jsr D
74 |
75 | jsr D
76 |
77 | iload 1
78 | istore 0
79 | return
80 | D:
81 | astore 2
82 | invokestatic kilim/test/ex/ExBasicBlock/pausable()V
83 | ret 2
84 |
85 | .end method
86 |
87 |
--------------------------------------------------------------------------------
/test/kilim/test/ex/ExLoop.java:
--------------------------------------------------------------------------------
1 | package kilim.test.ex;
2 |
3 | import kilim.Pausable;
4 | import kilim.Task;
5 |
6 | public class ExLoop extends Task {
7 | public String foo[] = new String[5];
8 | String dummy() throws Pausable {
9 | Task.yield();
10 | return "dummy";
11 | }
12 | @Override
13 | public void execute() throws Pausable, Exception {
14 | for (int i = 0; i < foo.length; i++) {
15 | // foo and i are on the operand stack before dummy gets called. This
16 | // test checks that the operand stack is correctly restored.
17 | foo[i] = dummy();
18 | }
19 | }
20 |
21 | public boolean verify() {
22 | // Call after ExLoop task has finished. foo[1..n] must have "dummy".
23 | for (int i = 0; i < foo.length; i++) {
24 | if (! "dummy".equals(foo[i])) {
25 | return false;
26 | }
27 | }
28 | return true;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/test/kilim/test/ex/ExPausable.java:
--------------------------------------------------------------------------------
1 | package kilim.test.ex;
2 |
3 | import kilim.Pausable;
4 |
5 | public class ExPausable {
6 | void noop() throws Pausable {
7 |
8 | }
9 |
10 | void simple() throws Pausable {
11 | noop();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------