├── .classpath ├── .gitignore ├── .idea └── uiDesigner.xml ├── .project ├── .settings └── org.eclipse.jdt.core.prefs ├── License ├── README.txt ├── build.sh ├── build.xml ├── docs ├── IFAQ.txt ├── internals │ ├── fiber_states.graffle │ ├── fiber_states.pdf │ ├── task_states.graffle │ ├── task_states.pdf │ └── task_states.txt ├── kilim_ecoop08.pdf ├── manual.html ├── style.css └── thread_of_ones_own.pdf ├── examples └── kilim │ └── examples │ ├── Fib.java │ └── Tree.java ├── libs ├── asm-all-5.0.3.jar └── junit.jar ├── pom.xml ├── src └── kilim │ ├── Constants.java │ ├── Fiber.java │ ├── Generator.java │ ├── KilimClassLoader.java │ ├── KilimException.java │ ├── NotPausable.java │ ├── Pausable.java │ ├── PauseReason.java │ ├── State.java │ ├── Task.java │ ├── TaskDoneReason.java │ ├── WeavingClassLoader.java │ ├── YieldReason.java │ ├── analysis │ ├── AsmDetector.java │ ├── BBList.java │ ├── BasicBlock.java │ ├── CallWeaver.java │ ├── ClassFlow.java │ ├── ClassInfo.java │ ├── ClassWeaver.java │ ├── ClassWriter.java │ ├── FileLister.java │ ├── Frame.java │ ├── Handler.java │ ├── IncompatibleTypesException.java │ ├── MethodFlow.java │ ├── MethodWeaver.java │ ├── NopInsn.java │ ├── Range.java │ ├── SAMweaver.java │ ├── TypeDesc.java │ ├── Usage.java │ ├── Utils.java │ └── Value.java │ ├── mirrors │ ├── CachedClassMirrors.java │ ├── ClassMirror.java │ ├── ClassMirrorNotFoundException.java │ ├── Detector.java │ ├── MethodMirror.java │ ├── Mirrors.java │ └── RuntimeClassMirrors.java │ └── tools │ ├── Asm.java │ ├── DumpClass.java │ ├── FlowAnalyzer.java │ ├── Javac.java │ ├── Kilim.java │ ├── P.java │ └── Weaver.java ├── test.sh └── test └── kilim └── test ├── AllNotWoven.java ├── Base.java ├── TaskTestClassLoader.java ├── TestBasicBlock.java ├── TestClassInfo.java ├── TestDynamicWeaver.java ├── TestExprs.java ├── TestFlow.java ├── TestFrame.java ├── TestInvalidPausables.java ├── TestJSR.java ├── TestTypeDesc.java ├── TestUsage.java ├── TestValue.java └── ex ├── ExA.java ├── ExB.java ├── ExBasicBlock.java ├── ExC.java ├── ExD.java ├── ExEx.java ├── ExException.java ├── ExExpr.java ├── ExFlow.java ├── ExFrame.java ├── ExInvalid.java ├── ExInvalidSynchronized.java ├── ExJSR.j ├── ExLoop.java └── ExPausable.java /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /classes/**/* 2 | /testclasses/**/* 3 | /classes 4 | /.idea 5 | /kilim.iml 6 | /kilim.jar 7 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | kilim 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.builder.cleanOutputFolder=clean 3 | org.eclipse.jdt.core.builder.duplicateResourceTask=warning 4 | org.eclipse.jdt.core.builder.invalidClasspath=abort 5 | org.eclipse.jdt.core.builder.recreateModifiedClassFileInOutputFolder=ignore 6 | org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch 7 | org.eclipse.jdt.core.circularClasspath=error 8 | org.eclipse.jdt.core.classpath.exclusionPatterns=enabled 9 | org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled 10 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 11 | org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate 12 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 13 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 14 | org.eclipse.jdt.core.compiler.compliance=1.8 15 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 16 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 17 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 18 | org.eclipse.jdt.core.compiler.maxProblemPerUnit=100 19 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 20 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 21 | org.eclipse.jdt.core.compiler.source=1.8 22 | org.eclipse.jdt.core.incompatibleJDKLevel=ignore 23 | org.eclipse.jdt.core.incompleteClasspath=error 24 | -------------------------------------------------------------------------------- /License: -------------------------------------------------------------------------------- 1 | ====================================================================== 2 | This license is a copy of the MIT license. 3 | - Sriram Srinivasan (kilim@malhar.net) 4 | ====================================================================== 5 | Copyright (c) 2006 Sriram Srinivasan 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files (the 9 | "Software"), to deal in the Software without restriction, including 10 | without limitation the rights to use, copy, modify, merge, publish, 11 | distribute, sublicense, and/or sell copies of the Software, and to 12 | permit persons to whom the Software is furnished to do so, subject to 13 | the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | This is a modified version 2 | Only fiber related stuff remains, mailbox and scheduler has been removed. There is no implicit 3 | multithreading, messaging or scheduling in this version. 4 | ===================================================================== 5 | 6 | Kilim v1.0 7 | Copyright (c) 2006 Sriram Srinivasan. 8 | (kilim _at_ malhar.net) 9 | ====================================================================== 10 | 11 | This software is released under an MIT-style licesne (please see the 12 | License file). 13 | 14 | Please see docs/manual.txt and docs/kilim_ecoop08.pdf for a brief 15 | introduction. 16 | 17 | This software depends on the ASM bytecode library (v. 4.x) 18 | 19 | To build, you can either run "build.sh" on Unix or ant from the top 20 | directory. Run "test.sh" or "ant test" to test. 21 | 22 | To run an example, type (say) 23 | java -cp ./classes:$CLASSPATH kilim.examples.Chain 10 24 | 25 | Please send comments, queries, code fixes, constructive criticisms to 26 | kilim _at_ malhar.net 27 | 28 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | export CLASSPATH=./classes:./testclasses:./libs/asm-all-5.0.3.jar:./libs/junit.jar:$CLASSPATH 2 | 3 | echo making dir: ./classes 4 | rm -rf ./classes 5 | rm -rf ./testclasses 6 | mkdir ./classes 7 | mkdir ./testclasses 8 | 9 | echo Compiling java source =========================================== 10 | javac -Xlint:unchecked -g -d ./classes `find . -name "*.java" ` 11 | 12 | echo Compiling .j files for testing ================================== 13 | java -ea kilim.tools.Asm -nf -d ./classes `find . -name "*.j"` 14 | 15 | echo Weaving ========================================================= 16 | # Weave all files under ./classes, compiling the tests to 17 | # ./testclasses while excluding any that match "ExInvalid". These are 18 | # negative tests for the Weaver. 19 | java -ea kilim.tools.Weaver -d ./classes -x "ExInvalid|test" ./classes 20 | java -ea kilim.tools.Weaver -d ./testclasses -x "ExInvalid" ./classes 21 | 22 | 23 | -------------------------------------------------------------------------------- /build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 26 | 27 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /docs/internals/fiber_states.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taowen/kilim/7a346a36cbf661d41cc27c294d757167ab0099d7/docs/internals/fiber_states.pdf -------------------------------------------------------------------------------- /docs/internals/task_states.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taowen/kilim/7a346a36cbf661d41cc27c294d757167ab0099d7/docs/internals/task_states.pdf -------------------------------------------------------------------------------- /docs/internals/task_states.txt: -------------------------------------------------------------------------------- 1 | See task_states.pdf 2 | 3 | Explanation of states: 4 | 5 | new: start not called 6 | 7 | ready: task.start()/resume() called. Task is in the 8 | scheduler q or has been taken out of it and handed to 9 | the thread. Note that task.running = true (doesn't reflect 10 | the 'running' state below) 11 | 12 | running: stack is resumed. task runs normally to completion 13 | or until Task.pause/yield/exit is called. 14 | 15 | pausing: pause called with a PauseReason object 16 | pauseReason can be user-defined. Predefined ones are 17 | YieldReason, TaskDoneReason, Empty_MsgAvListener, 18 | Full_SpcAvListener etc. 19 | 20 | Task.running remains true until it moves to paused state. 21 | 22 | paused: waiting for a message. 23 | Task.running = false /\ Task.pauseReason.isValid() 24 | 25 | done : task.execute() returned normally or with an unchecked exception 26 | or Task.exit() called 27 | 28 | ==================================================================== 29 | Messages and task scheduling 30 | 31 | What happens when a message arrives in each of the above states? 32 | 33 | new: no scheduling on msgs. start() explicitly schedules the task whether 34 | or not there are mailboxes. 35 | 36 | ready: 37 | running: 38 | no scheduling necessary when msg is put into mbox; task will rcv 39 | the message on get() The distinction between ready and running is 40 | not really important from a scheduling or message interaction 41 | perspective. 42 | 43 | pausing: no scheduling yet, because the task is still unwinding, 44 | But after the task is fully unwound, it'll be scheduled again if there 45 | are messages in an mbox that caused that task to pause in the first 46 | place. 47 | 48 | paused: Schedule the task. 49 | 50 | -------------------------------------------------------------------------------- /docs/kilim_ecoop08.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taowen/kilim/7a346a36cbf661d41cc27c294d757167ab0099d7/docs/kilim_ecoop08.pdf -------------------------------------------------------------------------------- /docs/manual.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taowen/kilim/7a346a36cbf661d41cc27c294d757167ab0099d7/docs/manual.html -------------------------------------------------------------------------------- /docs/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | line-height: 150%; 3 | /* font-size: 12pt;*/ 4 | border-style:none; 5 | font-family: verdana, helvetica; 6 | /* margin: 1em; */ 7 | } 8 | 9 | /* Links */ 10 | a:link {color: red; text-decoration:none} /* unvisited link */ 11 | a:visited {color: red; text-decoration:none} /* visited link */ 12 | a:hover {color: blue; background: yellow; text-decoration:none} /* mouse over link */ 13 | a:active {color: grey} /* selected link */ 14 | 15 | p,pre { 16 | line-height: 1.5em 17 | } 18 | 19 | h1 { 20 | background-color: #223322; 21 | Color: white; 22 | padding: 5px; 23 | padding-left: 20px; 24 | margin-left: -20px; 25 | margin-right: -20px; 26 | /* font-size: 15pt;*/ 27 | } 28 | 29 | 30 | pre {font-family: Monaco, Courier New, Courier; font-size:80%} 31 | code {font-family: Monaco, Courier New, Courier; font-size:80%} 32 | 33 | p code {margin: 5px} 34 | -------------------------------------------------------------------------------- /docs/thread_of_ones_own.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taowen/kilim/7a346a36cbf661d41cc27c294d757167ab0099d7/docs/thread_of_ones_own.pdf -------------------------------------------------------------------------------- /examples/kilim/examples/Fib.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.examples; 8 | 9 | import java.math.BigInteger; 10 | 11 | import kilim.Generator; 12 | import kilim.Pausable; 13 | 14 | /** 15 | * This example prints the nth Fibonacci number. 16 | * 17 | * It illustrates a generator, which is part iterator, part task. It returns the next object 'yielded' by its execute 18 | * method. The difference between a generator and a task is that the former is invoked by the caller synchronously on 19 | * the caller's stack; it is not scheduled in a separate thread. 20 | */ 21 | public class Fib extends Generator { 22 | 23 | public static void main(String[] args) { 24 | if (args.length == 0) { 25 | System.out.println("java kilim.examples.Fib for the n_th fibonacci number"); 26 | System.exit(0); 27 | } 28 | int n = Integer.parseInt(args[0]); 29 | Fib fib = new Fib(); 30 | 31 | // Iterate through and waste the first n fibonacci numbers 32 | for (int i = 0; i < n; i++) { 33 | fib.next(); 34 | } 35 | // .. and print the last one 36 | System.out.println("" + n + " : " + fib.next()); 37 | } 38 | 39 | public void execute() throws Pausable { 40 | BigInteger i = BigInteger.ZERO; 41 | BigInteger j = BigInteger.ONE; 42 | while (true) { 43 | // / NOTE: Generator yields a result 44 | // / j is now available to the caller of this generator's next() method. 45 | yield(j); 46 | BigInteger f = i.add(j); 47 | i = j; 48 | j = f; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /examples/kilim/examples/Tree.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.examples; 8 | import kilim.Generator; 9 | import kilim.Pausable; 10 | 11 | /** 12 | * This example illustrates two 'generators' that walk a tree, one in pre-order 13 | * and another in post-order. 14 | * 15 | * A generator is an iterator that generates a value (in this 16 | * case the nodes of the tree) each time its execute() method 17 | * 'yields' a value. 18 | * 19 | * Also, @see kilim.examples.Fib 20 | */ 21 | 22 | public class Tree { 23 | public String _val; 24 | Tree _left; 25 | Tree _right; 26 | 27 | public static void main(String[] args) { 28 | Tree t = new Tree("root", 29 | new Tree("a", 30 | new Tree("a1"), 31 | new Tree("a2")), 32 | new Tree("b", 33 | new Tree ("b1"), 34 | new Tree ("b2"))); 35 | 36 | System.out.println("Pre-order traversal:"); 37 | for (String s: new Pre(t)) { 38 | System.out.println(s); 39 | } 40 | 41 | System.out.println("Post-order traversal"); 42 | for (String s: new Post(t)) { 43 | System.out.println(s); 44 | } 45 | } 46 | 47 | Tree(String s) {_val = s;} 48 | 49 | Tree(String s, Tree l, Tree r) {this(s); _left = l; _right = r;} 50 | } 51 | 52 | class Pre extends Generator { 53 | Tree _t; 54 | Pre(Tree t) {_t = t;} 55 | 56 | public void execute() throws Pausable{ 57 | walk(_t); 58 | } 59 | 60 | void walk(Tree t) throws Pausable { 61 | if (t == null) return; 62 | yield(t._val); 63 | walk(t._left); 64 | walk(t._right); 65 | } 66 | } 67 | 68 | class Post extends Generator { 69 | Tree _t; 70 | Post(Tree t) {_t = t;} 71 | 72 | public void execute() throws Pausable { 73 | walk(_t); 74 | } 75 | 76 | void walk(Tree t) throws Pausable { 77 | if (t == null) return; 78 | walk(t._left); 79 | walk(t._right); 80 | yield(t._val); 81 | } 82 | } -------------------------------------------------------------------------------- /libs/asm-all-5.0.3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taowen/kilim/7a346a36cbf661d41cc27c294d757167ab0099d7/libs/asm-all-5.0.3.jar -------------------------------------------------------------------------------- /libs/junit.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taowen/kilim/7a346a36cbf661d41cc27c294d757167ab0099d7/libs/junit.jar -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.github.taowen 8 | kilim 9 | 1.0 10 | pom 11 | 12 | 13 | 14 | org.ow2.asm 15 | asm-all 16 | 5.0.3 17 | 18 | 19 | junit 20 | junit 21 | 4.11 22 | test 23 | 24 | 25 | 26 | 27 | 28 | 29 | maven-antrun-plugin 30 | 31 | 32 | 33 | 34 | com.sun 35 | tools 36 | 1.8.0 37 | system 38 | ${java.home}/../lib/tools.jar 39 | 40 | 41 | 42 | 43 | process-resources 44 | 45 | 46 | 47 | 48 | 49 | 50 | run 51 | 52 | 53 | 54 | 55 | 56 | 57 | org.codehaus.mojo 58 | build-helper-maven-plugin 59 | 1.3 60 | 61 | 62 | add-jar 63 | package 64 | 65 | attach-artifact 66 | 67 | 68 | 69 | 70 | kilim.jar 71 | jar 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /src/kilim/Constants.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; 7 | 8 | import org.objectweb.asm.Opcodes; 9 | 10 | public interface Constants extends Opcodes { 11 | 12 | String KILIM_VERSION = "1.0"; 13 | 14 | // Type descriptors 15 | String D_BOOLEAN = "Z"; 16 | String D_BYTE = "B"; 17 | String D_CHAR = "C"; 18 | String D_DOUBLE = "D"; 19 | String D_FLOAT = "F"; 20 | String D_INT = "I"; 21 | String D_LONG = "J"; 22 | String D_SHORT = "S"; 23 | String D_VOID = "V"; 24 | 25 | String D_ARRAY_BOOLEAN = "[Z"; 26 | String D_ARRAY_BYTE = "[B"; 27 | String D_ARRAY_CHAR = "[C"; 28 | String D_ARRAY_DOUBLE = "[D"; 29 | String D_ARRAY_FLOAT = "[F"; 30 | String D_ARRAY_SHORT = "[S"; 31 | String D_ARRAY_INT = "[I"; 32 | String D_ARRAY_LONG = "[J"; 33 | 34 | String D_NULL = "NULL"; 35 | String D_RETURN_ADDRESS = "A"; 36 | String D_OBJECT = "Ljava/lang/Object;"; 37 | String D_STRING = "Ljava/lang/String;"; 38 | String D_THROWABLE = "Ljava/lang/Throwable;"; 39 | String D_UNDEFINED = "UNDEFINED"; 40 | 41 | String D_FIBER = "Lkilim/Fiber;"; 42 | String D_STATE = "Lkilim/State;"; 43 | String D_TASK = "Lkilim/Task;"; 44 | String D_PAUSABLE = "Lkilim/Pausable;"; 45 | String D_FIBER_LAST_ARG = D_FIBER + ')'; // Last argument in a method descriptor 46 | 47 | String THROWABLE_CLASS = "java/lang/Throwable"; 48 | String FIBER_CLASS = "kilim/Fiber"; 49 | String STATE_CLASS = "kilim/State"; 50 | String TASK_CLASS = "kilim/Task"; 51 | String PAUSABLE_CLASS = "kilim/Pausable"; 52 | String NOT_PAUSABLE_CLASS = "kilim/NotPausable"; 53 | 54 | String WOVEN_FIELD = "$isWoven"; 55 | 56 | // Constant opcodes missing from asm's opcodes (as of asm 3.0) 57 | int ILOAD_0 = 26; 58 | int LLOAD_0 = 30; 59 | int FLOAD_0 = 34; 60 | int DLOAD_0 = 38; 61 | int ALOAD_0 = 42; 62 | int ISTORE_0 = 59; 63 | int LSTORE_0 = 63; 64 | int FSTORE_0 = 67; 65 | int DSTORE_0 = 71; 66 | int ASTORE_0 = 75; 67 | int LDC2_W = 20; 68 | } 69 | -------------------------------------------------------------------------------- /src/kilim/Generator.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; 8 | 9 | import java.util.Iterator; 10 | import java.util.NoSuchElementException; 11 | 12 | /** 13 | * A Generator, from the caller's perspective, looks like a normal iterator 14 | * that produces values. Because a standard iterator's next() method 15 | * must return every time, the programmer is forced to manage the stack 16 | * explicitly. The Generator class instead allows one to write a 17 | * task with an automatically managed stack and couple it to an 18 | * iterator interface. 19 | * 20 | * For example: 21 | * 22 | *
23 |  * class StringGenerator extends Generator{
24 |  *   public void execute() throws Pausable {
25 |  *       while (!done) {
26 |  *           String s = getNextWord(); // this can pause
27 |  *           yield(s);  
28 |  *       }
29 |  *   }
30 |  *   private String getNextWord() throws Pausable {
31 |  *   }
32 |  * }
33 |  * 
34 |  * 
35 |  * 
36 | * @see kilim.examples.Fib, kilim.examples.Tree 37 | */ 38 | 39 | public abstract class Generator extends Task implements Iterator, Iterable { 40 | T nextVal; 41 | 42 | public boolean hasNext() { 43 | if (nextVal == null) { 44 | if (isDone()) 45 | return false; 46 | _runExecute(); 47 | return nextVal != null; 48 | } else { 49 | return true; 50 | } 51 | } 52 | 53 | public T next() { 54 | T ret; 55 | if (nextVal != null) { 56 | ret = nextVal; 57 | nextVal = null; 58 | return ret; 59 | } 60 | if (isDone()) { 61 | throw new NoSuchElementException(); 62 | } 63 | _runExecute(); 64 | ret = nextVal; 65 | nextVal = null; 66 | return ret; 67 | } 68 | 69 | public void remove() { 70 | throw new AssertionError("Not Supported"); 71 | } 72 | 73 | public Iterator iterator() { 74 | return this; 75 | } 76 | 77 | public void yield(T val) throws Pausable { 78 | nextVal = val; 79 | Task.yield(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/kilim/KilimClassLoader.java: -------------------------------------------------------------------------------- 1 | package kilim; 2 | 3 | /** 4 | * Extends Classloader just to have access to the (protected) findLoadedClass method 5 | */ 6 | public class KilimClassLoader extends ClassLoader { 7 | public KilimClassLoader(ClassLoader cl) { 8 | super(cl); 9 | } 10 | 11 | public Class getLoadedClass(String className) { 12 | return super.findLoadedClass(className); 13 | } 14 | 15 | public boolean isLoaded(String className) { 16 | return getLoadedClass(className) != null; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/kilim/KilimException.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; 8 | 9 | 10 | public class KilimException extends RuntimeException { 11 | private static final long serialVersionUID = 7856831331381969854L; 12 | 13 | public KilimException(String msg) {super(msg);} 14 | } 15 | -------------------------------------------------------------------------------- /src/kilim/NotPausable.java: -------------------------------------------------------------------------------- 1 | package kilim; 2 | 3 | public class NotPausable extends RuntimeException { 4 | private static final long serialVersionUID = 1L; 5 | } 6 | -------------------------------------------------------------------------------- /src/kilim/Pausable.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; 8 | 9 | public class Pausable extends Exception { 10 | private static final long serialVersionUID = 1L; 11 | private Pausable() {} 12 | private Pausable(String msg) {} 13 | } 14 | 15 | -------------------------------------------------------------------------------- /src/kilim/PauseReason.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; 8 | 9 | /** 10 | * @see Task#pause(PauseReason) 11 | */ 12 | public interface PauseReason { 13 | } 14 | -------------------------------------------------------------------------------- /src/kilim/State.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; 8 | 9 | /** 10 | * State is the super class for customized State objects generated 11 | * by ClassWeaver. For example, a customized state object may 12 | * look like this: 13 | *
14 |  * public final class kilim.S_O2I3 extends kilim.State{
15 |  *   public java.lang.Object f0, f1;
16 |  *   public int f2, f3, f4;
17 |  *   public kilim.S_O2I3();
18 |  * }
19 |  * 
20 | * This customized class contains slots for two objects and three 21 | * integers (its name is indicative of this aspect) and is used 22 | * as a canonical class to store any activation frame that needs 23 | * to store two objects and three ints. 24 | * 25 | */ 26 | 27 | public class State { 28 | public int pc; 29 | public Object self; 30 | } 31 | -------------------------------------------------------------------------------- /src/kilim/TaskDoneReason.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; 8 | 9 | public class TaskDoneReason implements PauseReason { 10 | Object exitObj; 11 | TaskDoneReason(Object o) {exitObj = o;} 12 | 13 | public String toString() { 14 | return "Done. Exit msg = " + exitObj; 15 | } 16 | } -------------------------------------------------------------------------------- /src/kilim/WeavingClassLoader.java: -------------------------------------------------------------------------------- 1 | package kilim; 2 | 3 | import java.io.DataInputStream; 4 | import java.io.File; 5 | import java.io.IOException; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | import kilim.analysis.ClassInfo; 10 | import kilim.analysis.FileLister; 11 | import kilim.tools.Weaver; 12 | 13 | /** 14 | * Classloader that loads classes from the classpath spec given by the system property 15 | * "kilim.class.path" and weaves them dynamically. 16 | */ 17 | public class WeavingClassLoader extends KilimClassLoader { 18 | public static final String KILIM_CLASSPATH = "kilim.class.path"; 19 | /** 20 | * List of paths in kilim.class.path 21 | */ 22 | ArrayList fileContainers; 23 | /** 24 | * Weaver instance. There is a mutually recursive dependency between the weaver and 25 | * this class loader. See {@link #findClass(String)} 26 | */ 27 | Weaver weaver; 28 | 29 | public WeavingClassLoader(ClassLoader parent) { 30 | super(parent); 31 | String classPath = System.getProperty(KILIM_CLASSPATH, ""); 32 | String[] classPaths = classPath.split(":"); 33 | fileContainers = new ArrayList(classPaths.length); 34 | for (String name : classPaths) { 35 | name = name.trim(); 36 | if (name.equals("")) 37 | continue; 38 | try { 39 | fileContainers.add(new FileLister(name)); 40 | } catch (IOException ioe) { 41 | // System.err.println( "'" + name + "' does not exist. See property " + 42 | // KILIM_CLASSPATH); 43 | } 44 | } 45 | weaver = new Weaver(this); // mutually recursive dependency. 46 | } 47 | 48 | 49 | /** 50 | * Check if class file exists in kilim.class.path. 51 | */ 52 | @Override 53 | protected Class findClass(String name) throws ClassNotFoundException { 54 | Class ret = null; 55 | for (FileLister container : fileContainers) { 56 | try { 57 | String classFileName = name.replace('.', File.separatorChar) + ".class"; 58 | FileLister.Entry fe = container.open(classFileName); 59 | if (fe == null) continue; 60 | byte[] code = readFully(fe); 61 | List cis = weaver.weave(new ClassInfo(name, code)); 62 | 63 | for (ClassInfo ci : cis) { 64 | if (findLoadedClass(ci.className) != null) 65 | continue; 66 | Class c = super.defineClass(ci.className, ci.bytes, 0, ci.bytes.length); 67 | if (ci.className.equals(name)) { 68 | ret = c; 69 | } else { 70 | // extra classes produced by the weaver. resolve them right away 71 | // That way, when the given class name is resolved, it'll find its 72 | // kilim related state object classes right away. 73 | if (ci.className.startsWith("kilim.S")) { 74 | super.resolveClass(c); 75 | } 76 | } 77 | } 78 | if (ret == null) { 79 | // code exists, but didn't need to be woven 80 | ret = super.defineClass(name, code, 0, code.length); 81 | } 82 | } catch (IOException ignore) { 83 | System.err.println(ignore.getMessage()); 84 | } 85 | } 86 | if (ret == null) { 87 | throw new ClassNotFoundException(name); 88 | } 89 | return ret; 90 | } 91 | 92 | private static byte[] readFully(FileLister.Entry fe) throws IOException { 93 | DataInputStream in = new DataInputStream(fe.getInputStream()); 94 | byte[] contents = new byte[(int)fe.getSize()]; 95 | in.readFully(contents); 96 | in.close(); 97 | return contents; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/kilim/YieldReason.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; 8 | 9 | public class YieldReason implements PauseReason { 10 | @Override 11 | public String toString() { 12 | return "yield"; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/kilim/analysis/AsmDetector.java: -------------------------------------------------------------------------------- 1 | package kilim.analysis; 2 | 3 | import java.util.LinkedList; 4 | import java.io.IOException; 5 | import java.util.HashMap; 6 | 7 | import kilim.mirrors.Detector; 8 | 9 | import org.objectweb.asm.ClassReader; 10 | import org.objectweb.asm.tree.ClassNode; 11 | import org.objectweb.asm.tree.MethodNode; 12 | 13 | /** 14 | * This class is called by Detector to parse signatures of classes 15 | * that may have verification errors. It uses asm to open the file instead 16 | * of trying to classload it. 17 | */ 18 | public class AsmDetector { 19 | static HashMap classCacheMap= new HashMap(); 20 | public static int getPausableStatus(String className, String methodName, 21 | String desc, Detector detector) 22 | { 23 | try { 24 | ClassCache classCache = classCacheMap.get(className); 25 | if (classCache == null) { 26 | ClassReader cr = new ClassReader(className); 27 | ClassNode cn = new ClassNode(); 28 | cr.accept(cn, /*flags*/ 0); 29 | classCache = cache(className, cn); 30 | } 31 | int status = classCache.getPausableStatus(methodName, desc); 32 | if (status == Detector.METHOD_NOT_FOUND_OR_PAUSABLE) { 33 | // check super classes 34 | for (String superName: classCache.superClasses) { 35 | status = detector.getPausableStatus(superName, methodName, desc); 36 | if (status != Detector.METHOD_NOT_FOUND_OR_PAUSABLE) 37 | break; 38 | } 39 | } 40 | return status; 41 | } catch (IOException ioe) { 42 | System.err.println("***Error reading " + className + ": " + ioe.getMessage()); 43 | return Detector.METHOD_NOT_FOUND_OR_PAUSABLE; 44 | } 45 | } 46 | private static ClassCache cache(String className, ClassNode cn) { 47 | ClassCache classCache = new ClassCache(); 48 | classCache.className = className; 49 | classCacheMap.put(className, classCache); 50 | LOOP: 51 | for (Object m: cn.methods) { 52 | MethodNode mn = (MethodNode)m; 53 | for (Object exception: mn.exceptions) { 54 | if ("kilim/Pausable".equals(exception)) { 55 | classCache.pausableMethods.add(mn.name + mn.desc); 56 | continue LOOP; 57 | } 58 | } 59 | classCache.otherMethods.add(mn.name + mn.desc); 60 | } 61 | classCache.addSuper(cn.superName); 62 | for (Object interfaceName: cn.interfaces) { 63 | classCache.addSuper((String)interfaceName); 64 | } 65 | // System.out.println(classCache); 66 | return classCache; 67 | } 68 | public static void main(String[] args) { 69 | AsmDetector.getPausableStatus("com/sleepycat/je/Database", "putInternal", "Lcom/sleepycat/je/Transaction;Lcom/sleepycat/je/DatabaseEntry;Lcom/sleepycat/je/DatabaseEntry;Lcom/sleepycat/je/dbi/PutMode;Lkilim/Fiber;)Lcom/sleepycat/je/OperationStatus;)V", Detector.DEFAULT); 70 | } 71 | 72 | static class ClassCache { 73 | String className; 74 | LinkedList pausableMethods = new LinkedList(); 75 | LinkedList otherMethods = new LinkedList(); 76 | LinkedList superClasses = new LinkedList(); 77 | public void addSuper(String superName) { 78 | if (superName.equals("java/lang/Object")) return; 79 | if (!superClasses.contains(superName)) {superClasses.add(superName);} 80 | } 81 | public int getPausableStatus(String methodName, String desc) { 82 | String md = methodName + desc; 83 | if (pausableMethods.contains(md)) { 84 | return Detector.PAUSABLE_METHOD_FOUND; 85 | } else if (otherMethods.contains(md)) { 86 | return Detector.METHOD_NOT_PAUSABLE; 87 | } else { 88 | return Detector.METHOD_NOT_FOUND_OR_PAUSABLE; 89 | } 90 | } 91 | @Override 92 | public String toString() { 93 | return className + "\nPausable Methods: " + pausableMethods + "\nOthers:" + otherMethods; 94 | } 95 | } 96 | } 97 | 98 | 99 | -------------------------------------------------------------------------------- /src/kilim/analysis/BBList.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.analysis; 7 | import java.util.ArrayList; 8 | 9 | /** 10 | * Just a convenient alias for ArrayList 11 | */ 12 | public class BBList extends ArrayList 13 | { 14 | private static final long serialVersionUID = -1768924113292685739L; 15 | public BBList() {} 16 | public BBList(int size) { 17 | super(size); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/kilim/analysis/ClassFlow.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.analysis; 7 | 8 | import kilim.*; 9 | import kilim.mirrors.Detector; 10 | 11 | import java.io.IOException; 12 | import java.io.InputStream; 13 | import java.util.ArrayList; 14 | 15 | import org.objectweb.asm.ClassReader; 16 | import org.objectweb.asm.MethodVisitor; 17 | import org.objectweb.asm.Opcodes; 18 | import org.objectweb.asm.tree.ClassNode; 19 | import org.objectweb.asm.tree.FieldNode; 20 | 21 | /** 22 | * This class reads a .class file (or stream), wraps each method with a MethodFlow object and optionally analyzes it. 23 | * 24 | */ 25 | public class ClassFlow extends ClassNode { 26 | ArrayList methodFlows; 27 | ClassReader cr; 28 | String classDesc; 29 | /** 30 | * true if any of the methods contained in the class file is pausable. ClassWeaver uses it later to avoid weaving if 31 | * isPausable isn't true. 32 | */ 33 | private boolean isPausable; 34 | 35 | /** 36 | * true if the .class being read is already woven. 37 | */ 38 | public boolean isWoven = false; 39 | private Detector detector; 40 | 41 | public ClassFlow(InputStream is, Detector detector) throws IOException { 42 | super(Opcodes.ASM4); 43 | cr = new ClassReader(is); 44 | this.detector = detector; 45 | } 46 | 47 | public ClassFlow(String aClassName, Detector detector) throws IOException { 48 | super(Opcodes.ASM4); 49 | cr = new ClassReader(aClassName); 50 | this.detector = detector; 51 | } 52 | 53 | public ClassFlow(byte[] data, Detector detector) { 54 | super(Opcodes.ASM4); 55 | cr = new ClassReader(data); 56 | this.detector = detector; 57 | } 58 | 59 | 60 | @Override 61 | @SuppressWarnings( { "unchecked" }) 62 | public MethodVisitor visitMethod( 63 | final int access, 64 | final String name, 65 | final String desc, 66 | final String signature, 67 | final String[] exceptions) 68 | { 69 | MethodFlow mn = new MethodFlow( this, access, name, desc, signature, 70 | exceptions, detector); 71 | super.methods.add(mn); 72 | return mn; 73 | } 74 | 75 | public ArrayList getMethodFlows() { 76 | assert (methodFlows != null) : "ClassFlow.analyze not called"; 77 | return methodFlows; 78 | } 79 | 80 | public ArrayList analyze(boolean forceAnalysis) throws KilimException { 81 | // cr.accept(this, ClassReader.SKIP_DEBUG); 82 | 83 | Detector save = Detector.setDetector(detector); 84 | try { 85 | cr.accept(this, /*flags*/ClassReader.SKIP_FRAMES); 86 | for (Object o : this.fields) { 87 | FieldNode fn = (FieldNode) o; 88 | if (fn.name.equals(Constants.WOVEN_FIELD)) { 89 | isWoven = true; 90 | break; 91 | } 92 | } 93 | if (isWoven && !forceAnalysis) 94 | return new ArrayList(); // This is a hack. 95 | 96 | 97 | cr = null; // We don't need this any more. 98 | classDesc = TypeDesc.getInterned("L" + name + ';'); 99 | ArrayList flows = new ArrayList(methods.size()); 100 | String msg = ""; 101 | for (Object o : methods) { 102 | try { 103 | MethodFlow mf = (MethodFlow) o; 104 | if (mf.isBridge()) { 105 | MethodFlow mmf = getOrigWithSameSig(mf); 106 | if (mmf != null) 107 | mf.setPausable(mmf.isPausable()); 108 | } 109 | mf.verifyPausables(); 110 | if (mf.isPausable()) 111 | isPausable = true; 112 | if ((mf.needsWeaving() || forceAnalysis) && (!mf.isAbstract())) { 113 | mf.analyze(); 114 | } 115 | flows.add(mf); 116 | } catch (KilimException ke) { 117 | msg = msg + ke.getMessage() + "\n-------------------------------------------------\n"; 118 | } 119 | } 120 | if (msg.length() > 0) { 121 | throw new KilimException(msg); 122 | } 123 | methodFlows = flows; 124 | return flows; 125 | 126 | } finally { 127 | Detector.setDetector(save); 128 | } 129 | } 130 | 131 | private MethodFlow getOrigWithSameSig(MethodFlow bridgeMethod) { 132 | for (Object o : methods) { 133 | MethodFlow mf = (MethodFlow) o; 134 | if (mf == bridgeMethod) 135 | continue; 136 | if (mf.name.equals(bridgeMethod.name)) { 137 | String mfArgs = mf.desc.substring(0, mf.desc.indexOf(')')); 138 | String bmArgs = bridgeMethod.desc.substring(0, bridgeMethod.desc.indexOf(')')); 139 | if (mfArgs.equals(bmArgs)) 140 | return mf; 141 | } 142 | } 143 | return null; 144 | // throw new AssertionError("Bridge method found, but original method does not exist\nBridge method:" + 145 | // this.name + "::" + bridgeMethod.name + bridgeMethod.desc); 146 | } 147 | 148 | public String getClassDescriptor() { 149 | return classDesc; 150 | } 151 | 152 | public String getClassName() { 153 | return super.name.replace('/', '.'); 154 | } 155 | 156 | public boolean isPausable() { 157 | getMethodFlows(); // check analyze has been run. 158 | return isPausable; 159 | } 160 | 161 | boolean isInterface() { 162 | return (this.access & Opcodes.ACC_INTERFACE) != 0; 163 | } 164 | 165 | public Detector detector() { 166 | return detector; 167 | } 168 | 169 | /* 170 | * If this class is a functional interface, return the one "Single Abstract 171 | * Method". If there is more than one abstract method, return null. 172 | * SAM methods are given special treatment 173 | */ 174 | public MethodFlow getSAM() { 175 | if (!isInterface()) { 176 | return null; 177 | } 178 | MethodFlow sam = null; 179 | for (MethodFlow mf: methodFlows) { 180 | if (mf.isAbstract()) { 181 | if (sam != null) { 182 | return null; 183 | } 184 | sam = mf; 185 | } 186 | } 187 | return sam; 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/kilim/analysis/ClassInfo.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.analysis; 7 | 8 | public class ClassInfo { 9 | /** 10 | * fully qualified classname in a format suitable for Class.forName 11 | */ 12 | public String className; 13 | 14 | /** 15 | * bytecode for the class 16 | */ 17 | public byte[] bytes; 18 | 19 | public ClassInfo(String aClassName, byte[] aBytes) { 20 | className = aClassName.replace('/', '.'); 21 | // className = aClassName.replace('.', '/'); 22 | bytes = aBytes; 23 | } 24 | 25 | @Override 26 | public String toString() { 27 | return className; 28 | } 29 | 30 | @Override 31 | public int hashCode() { 32 | return className.hashCode(); 33 | } 34 | 35 | @Override 36 | public boolean equals(Object obj) { 37 | if (this == obj) { 38 | return true; 39 | } 40 | if ((obj instanceof ClassInfo) 41 | && ((ClassInfo)obj).className.equals(this.className)) { 42 | return true; 43 | } 44 | return false; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/kilim/analysis/ClassWriter.java: -------------------------------------------------------------------------------- 1 | package kilim.analysis; 2 | 3 | import kilim.mirrors.Detector; 4 | import kilim.mirrors.RuntimeClassMirrors; 5 | 6 | public class ClassWriter extends org.objectweb.asm.ClassWriter { 7 | private final Detector detector; 8 | 9 | public ClassWriter(final int flags, final ClassLoader classLoader) { 10 | super(flags); 11 | this.detector = new Detector(new RuntimeClassMirrors(classLoader)); 12 | } 13 | 14 | public ClassWriter(final int flags, final Detector detector) { 15 | super(flags); 16 | this.detector = detector; 17 | } 18 | 19 | protected String getCommonSuperClass(final String type1, final String type2) { 20 | try { 21 | return detector.commonSuperType(type1, type2); 22 | } catch (kilim.mirrors.ClassMirrorNotFoundException e) { 23 | return "java/lang/Object"; 24 | } 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/kilim/analysis/FileLister.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 java.io.BufferedInputStream; 9 | import java.io.File; 10 | import java.io.FileInputStream; 11 | import java.io.IOException; 12 | import java.io.InputStream; 13 | import java.lang.ref.WeakReference; 14 | import java.util.Enumeration; 15 | import java.util.Iterator; 16 | import java.util.Stack; 17 | import java.util.jar.JarEntry; 18 | import java.util.jar.JarFile; 19 | 20 | 21 | /** 22 | * Utility class to present a uniform iterator interface for file containers; presently 23 | * includes directories and jar files. 24 | */ 25 | 26 | public class FileLister implements Iterable { 27 | public static abstract class Entry { 28 | public abstract String getFileName(); 29 | public abstract long getSize(); 30 | public abstract InputStream getInputStream() throws IOException; 31 | }; 32 | 33 | /** 34 | * weak ref to a container to avoid hanging on to an open jar file. 35 | */ 36 | volatile WeakReference containerRef; 37 | String name; 38 | 39 | public FileLister(String dirOrJarName) throws IOException { 40 | name= dirOrJarName; 41 | } 42 | 43 | /** 44 | * @param relativeFileName 45 | * @return if the relativeFileName exists in the directory or jar represented by FileLister object 46 | * open it. If not return null. 47 | * @throws IOException 48 | */ 49 | public Entry open(String relativeFileName) throws IOException { 50 | return getContainer().open(relativeFileName); 51 | } 52 | 53 | // Lazily initialize the container. 54 | private FileContainer getContainer() throws IOException { 55 | FileContainer container = null; 56 | if (containerRef != null) { 57 | container = containerRef.get(); 58 | if (container != null) return container; 59 | } 60 | 61 | if (name.endsWith(".jar")) { 62 | container = openJar(this.name); 63 | } else { 64 | File f = new File(this.name); 65 | if (f.exists() && f.isDirectory()) { 66 | container = new DirIterator(f); 67 | } else { 68 | throw new IOException("Expected jar file or directory name"); 69 | } 70 | } 71 | containerRef = new WeakReference(container); 72 | return container; 73 | } 74 | 75 | private FileContainer openJar(String jarFile) throws IOException { 76 | return new JarIterator(new JarFile(jarFile)); 77 | } 78 | 79 | public Iterator iterator() { 80 | try { 81 | return getContainer(); 82 | } catch (IOException ignore) {} 83 | return null; 84 | } 85 | } 86 | 87 | abstract class FileContainer implements Iterator { 88 | abstract FileLister.Entry open(String relativeFileName) throws IOException; 89 | } 90 | 91 | /** 92 | * Preorder traversal of a directory. Returns everything including directory 93 | * names. 94 | */ 95 | class DirIterator extends FileContainer { 96 | final File root; 97 | private static class DirEntry extends FileLister.Entry { 98 | final File file; 99 | DirEntry(File f) {file = f;} 100 | 101 | @Override 102 | public long getSize() { 103 | return file.length(); 104 | } 105 | @Override 106 | public String getFileName() { 107 | try { 108 | return file.getCanonicalPath(); 109 | } catch (IOException ignore) {} 110 | return null; 111 | } 112 | 113 | @Override 114 | public InputStream getInputStream() throws IOException { 115 | return new BufferedInputStream(new FileInputStream(file)); 116 | } 117 | } 118 | 119 | Stack stack = new Stack(); 120 | 121 | 122 | DirIterator(File f) { 123 | root = f; 124 | stack.push(f); 125 | } 126 | 127 | public boolean hasNext() { 128 | return !stack.isEmpty(); 129 | } 130 | 131 | public FileLister.Entry next() { 132 | File ret = stack.pop(); 133 | if (ret.isDirectory()) { 134 | // prepare for next round 135 | File[] files = ret.listFiles(); 136 | // first add all directories to stack, then the files, so that 137 | // all files in a directory are processed continuously 138 | for (int i = files.length - 1; i >= 0; i--) { 139 | File ff = files[i]; 140 | if (ff.isDirectory()) { 141 | stack.push(ff); 142 | } 143 | } 144 | for (int i = files.length - 1; i >= 0; i--) { 145 | File ff = files[i]; 146 | if (!ff.isDirectory()) { 147 | stack.push(ff); 148 | } 149 | } 150 | } 151 | return new DirEntry(ret); 152 | } 153 | 154 | public void remove() { 155 | throw new RuntimeException("FileLister does not remove files"); 156 | } 157 | 158 | @Override 159 | FileLister.Entry open(String fileName) throws IOException { 160 | File ret = new File(root.getAbsolutePath() + File.separatorChar + fileName); 161 | if (ret.exists() && ret.isFile()) { 162 | return new DirEntry(ret); 163 | } 164 | return null; 165 | } 166 | } 167 | 168 | class JarIterator extends FileContainer { 169 | Enumeration jarEnum; 170 | JarFile jarFile; 171 | String nextName; 172 | 173 | private class JEntry extends FileLister.Entry { 174 | private final JarEntry jarEntry; 175 | JEntry(JarEntry j) {jarEntry = j;} 176 | 177 | @Override 178 | public String getFileName() { 179 | return jarEntry.getName(); 180 | } 181 | 182 | @Override 183 | public InputStream getInputStream() throws IOException { 184 | return jarFile.getInputStream(jarEntry); 185 | } 186 | 187 | @Override 188 | public long getSize() { 189 | return jarEntry.getSize(); 190 | } 191 | } 192 | 193 | JarIterator(JarFile f) { 194 | jarFile = f; 195 | jarEnum = f.entries(); 196 | } 197 | 198 | public boolean hasNext() { 199 | return jarEnum.hasMoreElements(); 200 | } 201 | 202 | public FileLister.Entry next() { 203 | return new JEntry(jarEnum.nextElement()); 204 | } 205 | 206 | public void remove() { 207 | throw new RuntimeException("FileLister does not remove files"); 208 | } 209 | 210 | @Override 211 | FileLister.Entry open(String relativeFileName) throws IOException { 212 | JarEntry e = jarFile.getJarEntry(relativeFileName); 213 | return e == null ? null : new JEntry(e); 214 | } 215 | } -------------------------------------------------------------------------------- /src/kilim/analysis/Frame.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_DOUBLE; 9 | import static kilim.Constants.D_FLOAT; 10 | import static kilim.Constants.D_LONG; 11 | import static kilim.Constants.D_OBJECT; 12 | import static org.objectweb.asm.Opcodes.ACC_STATIC; 13 | import static org.objectweb.asm.Opcodes.ACC_SYNCHRONIZED; 14 | import static org.objectweb.asm.Opcodes.ALOAD; 15 | import static org.objectweb.asm.Opcodes.DLOAD; 16 | import static org.objectweb.asm.Opcodes.FLOAD; 17 | import static org.objectweb.asm.Opcodes.ILOAD; 18 | import static org.objectweb.asm.Opcodes.LLOAD; 19 | 20 | import org.objectweb.asm.tree.MethodNode; 21 | 22 | 23 | /** 24 | * An activation frame. 25 | 26 | * 27 | */ 28 | public class Frame { 29 | Value[] locals; 30 | Value[] stack; 31 | int numMonitorsActive = 0; 32 | int stacklen = 0; 33 | 34 | private Frame(int nLocals, int nStack, boolean init) { 35 | this.locals = new Value[nLocals]; 36 | if (init) { 37 | for (int i = 0; i < nLocals; i++) { 38 | locals[i] = Value.V_UNDEFINED; 39 | } 40 | } 41 | this.stack = new Value[nStack]; 42 | } 43 | 44 | public Frame(int nLocals, int nStack) { 45 | this(nLocals, nStack, true); 46 | } 47 | 48 | /** 49 | * Merge the local variables and stack from the incoming frame 50 | * into the current frame. 51 | * @param inframe -- incoming frame 52 | * @param localsOnly -- true for exception handlers, because the 53 | * stack is cleared. 54 | * @param usage -- Only those locals are merged that are deemed 55 | * live (@see Usage#isLiveIn(int)) 56 | * @return this, if the merge didn't change anything 57 | * or a new Frame if the operation changed a slot on the stack 58 | * or a local variable 59 | */ 60 | public Frame merge(Frame inframe, boolean localsOnly, Usage usage) { 61 | int slen = stacklen; 62 | 63 | Value[] nst = null; // new stack. allocated if needed 64 | 65 | if (!localsOnly) { 66 | Value[] st = stack; 67 | Value[] ist = inframe.stack; 68 | for (int i = 0; i < slen; i++) { 69 | Value va = st[i]; 70 | Value vb = ist[i]; 71 | if (va == vb || va.equals(vb)) continue; 72 | Value newval = va.merge(vb); 73 | if (newval != va) { 74 | if (nst == null) nst = dupArray(st); 75 | nst[i] = newval; 76 | } 77 | } 78 | } 79 | 80 | Value[] lo = locals; 81 | Value[] ilo = inframe.locals; 82 | Value[] nlo = null; // new locals array. allocated if needed 83 | for (int i = 0; i < lo.length; i++) { 84 | if (!usage.isLiveIn(i)) continue; 85 | Value va = lo[i]; 86 | Value vb = ilo[i]; 87 | if (va == vb || va.equals(vb)) continue; 88 | Value newval = va.merge(vb); 89 | if (newval != va) { 90 | if (nlo == null) nlo = dupArray(lo); 91 | nlo[i] = newval; 92 | } 93 | } 94 | if (nst == null && nlo == null) { 95 | return this; 96 | } else { 97 | // One or both of locals and stacks have new values 98 | if (nst == null) nst = dupArray(stack); 99 | if (nlo == null) nlo = dupArray(locals); 100 | return new Frame(nlo, nst, slen, numMonitorsActive); 101 | } 102 | } 103 | 104 | public static Value[] dupArray(Value[] a) { 105 | Value[] ret = new Value[a.length]; 106 | System.arraycopy(a, 0, ret, 0, a.length); 107 | return ret; 108 | } 109 | 110 | private Frame(Value[] alocals, Value[] astack, int astacklen, int aNumMonitorsActive) { 111 | this.locals = alocals; 112 | this.stack = astack; 113 | this.stacklen = astacklen; 114 | this.numMonitorsActive = aNumMonitorsActive; 115 | } 116 | 117 | public Frame dup() { 118 | return new Frame(dupArray(locals), dupArray(stack), stacklen, numMonitorsActive); 119 | } 120 | 121 | public Frame(String classDesc, MethodNode method) { 122 | this(method.maxLocals, method.maxStack, false); 123 | String[] argTypeDescs = TypeDesc.getArgumentTypes(method.desc); 124 | for (int i = 0; i < method.maxLocals; i++) { 125 | setLocal(i, Value.V_UNDEFINED); 126 | } 127 | int local = 0; 128 | int paramPos = 100000; 129 | if ((method.access & ACC_STATIC) == 0) { 130 | // 0th local is "this" 131 | setLocal(local++, Value.make(paramPos++,classDesc)); 132 | } 133 | for (int i = 0; i < argTypeDescs.length; i++) { 134 | local += setLocal(local, Value.make(paramPos++, argTypeDescs[i])); 135 | } 136 | if ((method.access & ACC_SYNCHRONIZED) != 0) { 137 | numMonitorsActive = 1; 138 | } 139 | } 140 | 141 | private boolean checkType(String desc) { 142 | if (desc.equals("Ljava/lang/Object;") && desc != D_OBJECT) return false; 143 | switch(desc.charAt(0)) { 144 | case 'L': case 'B': case 'C': case 'D': case 'F': case 'I': 145 | case 'J': case 'S': case 'Z': case 'N': case '[': case 'A': 146 | case 'U': 147 | return true; 148 | default: 149 | return false; 150 | } 151 | } 152 | 153 | public int setLocal(int local, Value v) { 154 | assert checkType(v.getTypeDesc()) : "Invalid type: " + v.getTypeDesc(); 155 | locals[local] = v; 156 | if (v.isCategory2()) { 157 | locals[local+1] = v; 158 | return 2; 159 | } 160 | return 1; 161 | } 162 | 163 | public Value getLocal(int local, int opcode) { 164 | Value v = locals[local]; 165 | String desc = v.getTypeDesc(); 166 | String expected = null; 167 | switch(opcode) { 168 | case ILOAD: { 169 | if (TypeDesc.isIntType(desc)) { 170 | return v; 171 | } else { 172 | expected = "int"; 173 | } 174 | break; 175 | } 176 | case LLOAD: { 177 | if (desc == D_LONG) { 178 | return v; 179 | } else { 180 | expected = "long"; 181 | } 182 | break; 183 | } 184 | case DLOAD: { 185 | if (desc == D_DOUBLE) { 186 | return v; 187 | } else { 188 | expected = "double"; 189 | } 190 | break; 191 | } 192 | case FLOAD: { 193 | if (desc == D_FLOAT) { 194 | return v; 195 | } else { 196 | expected = "float"; 197 | } 198 | break; 199 | } 200 | case ALOAD: { 201 | if (TypeDesc.isRefType(desc)) { 202 | return v; 203 | } else { 204 | expected = "ref"; 205 | } 206 | } 207 | } 208 | throw new AssertionError("Expected " + expected + " in local# " + local + ", got " + desc); 209 | } 210 | 211 | public Value getLocal(int local) { 212 | return locals[local]; 213 | } 214 | 215 | public Value getStack(int pos) { 216 | // for testing 217 | return stack[pos]; 218 | } 219 | 220 | public Value push(Value v) { 221 | assert (v != Value.V_UNDEFINED) : "UNDEFINED type pushed"; 222 | assert checkType(v.getTypeDesc()) : "Invalid type: " + v.getTypeDesc(); 223 | stack[stacklen++] = v; 224 | return v; 225 | } 226 | 227 | public Value pop() { 228 | try { 229 | return stack[--stacklen]; 230 | } catch (ArrayIndexOutOfBoundsException e) { 231 | throw new RuntimeException("Verify error. Expected word in stack, but stack is empty"); 232 | } 233 | } 234 | 235 | public Value popWord() { 236 | Value v = pop(); 237 | assert v.isCategory1() : "double word present where single expected"; 238 | return v; 239 | } 240 | 241 | public void popn(int n) { 242 | stacklen -= n; 243 | } 244 | 245 | void clearStack() { 246 | stacklen = 0; 247 | } 248 | 249 | @Override 250 | public boolean equals(Object other) { 251 | Frame that = (Frame)other; 252 | for (int i = 0; i < locals.length; i++) { 253 | if (!locals[i].equals(that.locals[i])) return false; 254 | } 255 | for (int i = 0; i < stacklen; i++) { 256 | if (!stack[i].equals(that.stack[i])) return false; 257 | } 258 | return true; 259 | } 260 | 261 | @Override 262 | public int hashCode() { 263 | int hash = 0; 264 | for (int i = 0; i < this.locals.length;i++) hash ^= this.locals[i].hashCode(); 265 | for (int i = 0; i < this.stacklen;i++) hash ^= this.locals[i].hashCode(); 266 | return hash; 267 | } 268 | 269 | @Override 270 | public String toString() { 271 | StringBuffer sb = new StringBuffer(100); 272 | int numDefined = 0; 273 | sb.append("): "); 274 | for (int i = 0; i < this.locals.length;i++) { 275 | Value v = locals[i]; 276 | if (v != Value.V_UNDEFINED) { 277 | numDefined++; 278 | sb.append(i).append(':').append(this.locals[i]).append(" "); 279 | } 280 | } 281 | sb.insert(0, numDefined); 282 | sb.insert(0, "Locals("); 283 | sb.append("\n").append("Stack(").append(stacklen).append("): "); 284 | for (int i = 0; i < this.stacklen;i++) { 285 | sb.append(this.stack[i]).append(" "); 286 | } 287 | return sb.toString(); 288 | } 289 | 290 | public int getMaxLocals() { 291 | return locals.length; 292 | } 293 | 294 | public int getStackLen() { 295 | return stacklen; 296 | } 297 | 298 | } 299 | -------------------------------------------------------------------------------- /src/kilim/analysis/Handler.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.THROWABLE_CLASS; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Collections; 12 | 13 | /** 14 | * Representation for a catch handler. 15 | */ 16 | public class Handler implements Comparable { 17 | /** 18 | * Source offset in method's instruction list 19 | */ 20 | public int from; 21 | 22 | /** 23 | * End offset in method's instruction list 24 | */ 25 | public int to; 26 | 27 | /** 28 | * Exception type 29 | */ 30 | public String type; 31 | 32 | /** 33 | * catch handler's entry point 34 | */ 35 | public BasicBlock catchBB; 36 | 37 | public Handler(int aFrom, int aTo, String aType, BasicBlock aCatchBB) { 38 | from = aFrom; 39 | to = aTo; 40 | if (aType == null) { 41 | // try/finally is compiled with a covering catch handler with 42 | // type null. It is the same as catching Throwable. 43 | aType = THROWABLE_CLASS; 44 | } 45 | type = aType; 46 | catchBB = aCatchBB; 47 | } 48 | 49 | public int compareTo(Handler h) { 50 | int c = this.type.compareTo(h.type); 51 | if (c != 0) return c; 52 | 53 | c = this.catchBB.compareTo(h.catchBB); 54 | if (c != 0) return c; 55 | 56 | return from < h.from ? -1 : (from == h.from) ? 0 : 1; 57 | } 58 | 59 | public static ArrayList consolidate( ArrayList list) { 60 | Collections.sort(list); 61 | ArrayList newList = new ArrayList(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 | --------------------------------------------------------------------------------