├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src ├── main └── java │ └── de │ └── matthiasmann │ └── continuations │ ├── CoIterator.java │ ├── Coroutine.java │ ├── CoroutineProto.java │ ├── Stack.java │ ├── SuspendExecution.java │ ├── Util.java │ ├── instrument │ ├── AlreadyInstrumented.java │ ├── CheckInstrumentationVisitor.java │ ├── DBClassWriter.java │ ├── ExtractSuperClass.java │ ├── InstrumentClass.java │ ├── InstrumentMethod.java │ ├── InstrumentationTask.java │ ├── JavaAgent.java │ ├── Log.java │ ├── LogLevel.java │ ├── MethodDatabase.java │ ├── NewValue.java │ ├── OmittedInstruction.java │ ├── TypeAnalyzer.java │ ├── TypeInterpreter.java │ ├── UnableToInstrumentException.java │ └── package-info.java │ └── package-info.java └── test └── java └── de └── matthiasmann └── continuations ├── ArrayIT.java ├── BlockingIT.java ├── CatchIT.java ├── CoIteratorIT.java ├── DoubleIT.java ├── FinallyIT.java ├── InheritIT.java ├── InitialSizeIT.java ├── InterfaceIT.java ├── Merge2IT.java ├── Merge3IT.java ├── MergeIT.java ├── NullIT.java ├── SerializeIT.java ├── SomeInterface.java ├── SuspendConstructorArgumentIT.java ├── SuspendIT.java ├── ThrowIT.java └── UninitializedIT.java /.gitignore: -------------------------------------------------------------------------------- 1 | .settings/ 2 | .classpath 3 | .project 4 | target/ 5 | nbproject/ 6 | build.xml 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008-2013, Matthias Mann 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * Neither the name of Matthias Mann nor the names of its 13 | contributors may be used to endorse or promote products derived from 14 | this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Continuations library by Matthias Mann from . Original documentation follows. 2 | 3 | # Continuations Library by Matthias Mann 4 | 5 | This small library allows you to write your game AI or animation code in a simple sequential way, blocking on commands like `walkTo()` and all this without blocking your AI thread or the need for additional threads. 6 | 7 | Some of you might be familiar with the features of scripting languages like [Lua](http://www.lua.org/) or [AngelScript](http://www.angelcode.com/angelscript/). One of these features are Coroutines. These allow a control transfer that is beyond a simple `if`/`else` or `goto`. Somewhere you define the entry point - the coroutine - and while executing inside the coroutine you can suspend your code and return to the coroutine call. This is comparable to stack unwinding of exceptions. But coroutines offer you the unique feature to return to the state where you suspended execution. This allows you to write code in a simple sequential way that would otherwise require either its own thread or a hand written state machine. 8 | 9 | But I'm writing this for the Java rubric - and this feature is not part of your standard Java runtime today. This library offers you a very easy and still power full way to use these features in Java without affecting your code by intrusive requirements. 10 | 11 | Lets start with what you get: 12 | 13 | * Write simple sequential code - you no longer need to create state machines by hand 14 | * No `Thread`s are created or needed - no multi thread synchronization issues 15 | * No garbage creation from code execution 16 | * Very small runtime overhead 17 | * Only suspendable method calls are changed - all calls into your standard library (like `java.util.*` etc) are not affected at all. 18 | * Full serialization support 19 | * You can store the execution state of coroutines as part of your game state in your save game without any additional code. This of course requires that your classes and data types which you use in your coroutines are serializable. 20 | * Full support for exception handling and finally blocks 21 | * Offline preprocessing does not slow down you application load time 22 | * Of course runtime instrumentation is also possible. 23 | * Very small runtime library - less then 10 KByte (uncompressed JAR) 24 | * BSD License 25 | 26 | With all these great features - you may be asking for the drawbacks. Well there are of course a few drawbacks: 27 | 28 | * Constructors and static initializers can't be suspended 29 | * Suspendable methods can't be synchronized or have synchronized blocks 30 | * You need to download [ASM3 library](https://asm.ow2.io/) to run the instrumentation task 31 | 32 | You can't call suspendable method with reflection 33 | 34 | The synchronization issue can be worked around by putting code which requires the use of synchronization into its own method. 35 | 36 | Lets look at an example - an iterator. Writing iterators can be quite complex - especially if the implementation is more then just a table walk. This library comes with a utility class that allows you to write iterators in a simple sequential way - also known as producers. It is based on a JUnit test which is also distributed with the source code of this library. 37 | 38 | ```java 39 | public class TestIterator extends CoIterator implements Serializable { 40 | @Override 41 | protected void run() throws SuspendExecution { 42 | produce("A"); 43 | produce("B"); 44 | for(int i = 0; i < 4; i++) { 45 | produce("C" + i); 46 | } 47 | produce("D"); 48 | produce("E"); 49 | } 50 | } 51 | ``` 52 | 53 | This class extends the class `CoIterator` which provides the `produce` method and implements all methods of the `Iterator` interface. 54 | The `run` method is an abstract method from `CoIterator` which is the body of our iterator. This method is declared to throw the special exception `SuspendExecution`. This exception must never be caught by any user code - it must be always propagated. But don't fear - you can still use the infamous `catch(Throwable)` if you like. This exception is the marker for suspendable methods - it tells the preprocessor that this method calls others methods which might suspend execution. In our example each call to `produce` will suspend execution until the consumer (the caller of `next`) has consumed the produced value. Let's take a look at code that uses our `Iterator`: 55 | 56 | ```java 57 | Iterator iter1 = new TestIterator(); 58 | while(iter.hasNext()) { 59 | System.out.println(iter.next()); 60 | } 61 | ``` 62 | 63 | Which will produce the following output: 64 | 65 | ``` 66 | A 67 | B 68 | C0 69 | C1 70 | C2 71 | C3 72 | D 73 | E 74 | ``` 75 | 76 | This looks very familiar - it's your everyday iterator usage. Of course it also works with an enhanced `for` loop if the `Iterator` is returned from a class which implements `Iterable`. 77 | 78 | But you can also write this with standard Java code without support from this library: 79 | 80 | ```java 81 | public class TestIterator implements Iterator, Serializable { 82 | private int state; 83 | private int i; 84 | public String next() { 85 | switch(state) { 86 | case 0: state=1; return "A"; 87 | case 1: state=2; i=0; return "B"; 88 | case 2: 89 | if(i == 3) state = 3; 90 | return "C" + (i++); 91 | case 3: state=4; return "D"; 92 | case 4: state=5; return "E"; 93 | default: throw new NoSuchElementException(); 94 | } 95 | } 96 | public boolean hasNext() { 97 | return state < 5; 98 | } 99 | public void remove() { 100 | throw new UnsupportedOperationException("Not supported"); 101 | } 102 | } 103 | ``` 104 | 105 | That is much more complex - and if your code gets more complicated it will be very hard to create a state machine for it. But with the help of this library you can spend your time to write your application. 106 | 107 | An important point is that the underlying `Coroutine` is only executed when `hasNext()` or `next()` of the `Iterator` is executed. `hasNext()` will return `false` if the `run()` method finishes its execution. It also means that if you don't consume all values that the `Iterator` returns that the `run()` method of your `CoIterator` subclass never get the chance to finish its execution - which includes any not yet executed finally blocks. 108 | 109 | If you look closely at our `TestIterator` class you will see that it also implements `Serializable` which means that you can store the iterator in a file. See the JUnit test for the complete example. 110 | 111 | To use the Ant task you need the full JAR and the full ASM3 JAR. It also works with ASM 3.1. All dependencies of your application needs to be in the class path of the instrumentation Ant task - otherwise it can't analyze the method calls. You can safely run this task over already instrumented class files - it will detect already transformed classes and skip them. This example Ant snippet assumes the Netbeans build properties: 112 | 113 | ```xml 114 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | ``` 124 | 125 | The embedded fileset is used to specify the list of classes that should be instrumented. The task also has a few parameters that can be used to alter its behavior: 126 | 127 | |Attribute|Description|Required|Default| 128 | |---------|-----------|--------|-------| 129 | |`verbose`|Outputs informations about each processing step|No|`false`| 130 | |`check`|Includes the ASM verifier in the transformation chain. Mostly a debugging option.|No|`false`| 131 | |`debug`|Outputs internal and detailed information about the processing of the class files.|No|`false`| 132 | 133 | Enough with the talk for now - here is the link to the library: 134 | 135 | * Javadoc (multiple HTML pages) 136 | * Binaries, Source code and zipped Javadoc 137 | * ASM3 (for the 2010 version), ASM4 (for the 2012 and newer version) library 138 | 139 | So have fun and let me know if you find it useful -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | de.matthiasmann.continuations 6 | continuations 7 | 1.0-SNAPSHOT 8 | jar 9 | continuations 10 | http://www.matthiasmann.de/content/view/24/26/ 11 | 12 | 13 | UTF-8 14 | UTF-8 15 | 16 | 17 | 18 | 19 | 20 | org.apache.maven.plugins 21 | maven-compiler-plugin 22 | 3.6.0 23 | 24 | 1.8 25 | 1.8 26 | 27 | 28 | 29 | org.apache.maven.plugins 30 | maven-jar-plugin 31 | 3.0.1 32 | 33 | 34 | 35 | de.matthiasmann.continuations.instrument.JavaAgent 36 | 37 | 38 | 39 | 40 | 41 | org.apache.maven.plugins 42 | maven-failsafe-plugin 43 | 2.19.1 44 | 45 | -javaagent:${project.build.directory}/${project.build.finalName}.jar -ea 46 | 47 | 48 | 49 | 50 | integration-test 51 | verify 52 | 53 | 54 | 55 | 56 | 57 | org.apache.maven.plugins 58 | maven-antrun-plugin 59 | 1.8 60 | 61 | 62 | instrument-classes 63 | compile 64 | 65 | 66 | 68 | 71 | 72 | 75 | 76 | 77 | 78 | 79 | run 80 | 81 | 82 | 83 | 84 | instrument-test-classes 85 | test-compile 86 | 87 | 88 | 90 | 93 | 94 | 97 | 98 | 99 | 100 | 101 | run 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | junit 112 | junit 113 | 4.13.1 114 | test 115 | 116 | 117 | org.ow2.asm 118 | asm-all 119 | 5.1 120 | 121 | 122 | org.apache.ant 123 | ant 124 | 1.10.11 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /src/main/java/de/matthiasmann/continuations/CoIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2013, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations; 30 | 31 | import java.io.Serializable; 32 | import java.util.Iterator; 33 | import java.util.NoSuchElementException; 34 | 35 | /** 36 | * A Coroutine based iterator 37 | * 38 | * @param the element class that the Iterator returns 39 | * @author Matthias Mann 40 | */ 41 | public abstract class CoIterator implements Iterator, Serializable { 42 | 43 | private static final long serialVersionUID = 351278561539L; 44 | 45 | private final Coroutine co; 46 | 47 | private E element; 48 | private boolean hasElement; 49 | 50 | protected CoIterator() { 51 | co = new Coroutine(new DelegateExecute()); 52 | } 53 | 54 | @Override 55 | public boolean hasNext() { 56 | while(!hasElement && co.getState() != Coroutine.State.FINISHED) { 57 | co.run(); 58 | } 59 | return hasElement; 60 | } 61 | 62 | @Override 63 | public E next() { 64 | if(!hasNext()) { 65 | throw new NoSuchElementException(); 66 | } 67 | E result = element; 68 | hasElement = false; 69 | element = null; 70 | return result; 71 | } 72 | 73 | /** 74 | * Always throws UnsupportedOperationException. 75 | * @throws java.lang.UnsupportedOperationException always 76 | */ 77 | @Override 78 | public void remove() throws UnsupportedOperationException { 79 | throw new UnsupportedOperationException("Not supported"); 80 | } 81 | 82 | /** 83 | * Produces the next value to be returned by the {@link #next} method. 84 | * 85 | * @param element The value that should be returned by {@link #next} 86 | * @throws de.matthiasmann.continuations.SuspendExecution This method will suspend the execution 87 | */ 88 | protected void produce(E element) throws SuspendExecution { 89 | if(hasElement) { 90 | throw new IllegalStateException("hasElement = true"); 91 | } 92 | this.element = element; 93 | hasElement = true; 94 | Coroutine.yield(); 95 | } 96 | 97 | /** 98 | *

This is the body of the Iterator. This method is executed as a 99 | * {@link Coroutine} to {@link #produce} the values of the Iterator.

100 | * 101 | *

Note that this method is suspended each time it calls produce. And if 102 | * the consumer does not consume all values of the Iterator then this 103 | * method does not get the chance to finish it's execution. This also 104 | * includes the finally blocks.

105 | * 106 | *

This method must only suspend by calling produce. Any other reason 107 | * for suspension will cause a busy loop in the Iterator.

108 | * 109 | * @throws de.matthiasmann.continuations.SuspendExecution 110 | */ 111 | protected abstract void run() throws SuspendExecution; 112 | 113 | private class DelegateExecute implements CoroutineProto, Serializable { 114 | private static final long serialVersionUID = 12784529515412L; 115 | 116 | @Override 117 | public void coExecute() throws SuspendExecution { 118 | CoIterator.this.run(); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/de/matthiasmann/continuations/Coroutine.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations; 30 | 31 | import java.io.IOException; 32 | import java.io.Serializable; 33 | import java.lang.annotation.Annotation; 34 | 35 | /** 36 | *

A Coroutine is used to run a CoroutineProto.

37 | *

It also provides a function to suspend a running Coroutine.

38 | * 39 | *

A Coroutine can be serialized if it's not running and all involved 40 | * classes and data types are also {@link Serializable}.

41 | * 42 | * @author Matthias Mann 43 | */ 44 | public class Coroutine implements Runnable, Serializable { 45 | 46 | /** 47 | * Default stack size for the data stack. 48 | * @see #Coroutine(de.matthiasmann.continuations.CoroutineProto, int) 49 | */ 50 | public static final int DEFAULT_STACK_SIZE = 16; 51 | 52 | private static final long serialVersionUID = 2783452871536981L; 53 | 54 | public enum State { 55 | /** The Coroutine has not yet been executed */ 56 | NEW, 57 | /** The Coroutine is currently executing */ 58 | RUNNING, 59 | /** The Coroutine has suspended it's execution */ 60 | SUSPENDED, 61 | /** The Coroutine has finished it's run method 62 | * @see CoroutineProto#coExecute() 63 | */ 64 | FINISHED 65 | }; 66 | 67 | private final CoroutineProto proto; 68 | private final Stack stack; 69 | private State state; 70 | 71 | /** 72 | * Suspend the currently running Coroutine on the calling thread. 73 | * 74 | * @throws de.matthiasmann.continuations.SuspendExecution This exception is used for control transfer - don't catch it ! 75 | * @throws java.lang.IllegalStateException If not called from a Coroutine 76 | */ 77 | public static void yield() throws SuspendExecution, IllegalStateException { 78 | throw new Error("Calling function not instrumented"); 79 | } 80 | 81 | /** 82 | * Creates a new Coroutine from the given CoroutineProto. A CoroutineProto 83 | * can be used in several Coroutines at the same time - but then the normal 84 | * multi threading rules apply to the member state. 85 | * 86 | * @param proto the CoroutineProto for the Coroutine. 87 | */ 88 | public Coroutine(CoroutineProto proto) { 89 | this(proto, DEFAULT_STACK_SIZE); 90 | } 91 | 92 | /** 93 | * Creates a new Coroutine from the given CoroutineProto. A CoroutineProto 94 | * can be used in several Coroutines at the same time - but then the normal 95 | * multi threading rules apply to the member state. 96 | * 97 | * @param proto the CoroutineProto for the Coroutine. 98 | * @param stackSize the initial stack size for the data stack 99 | * @throws NullPointerException when proto is null 100 | * @throws IllegalArgumentException when stackSize is <= 0 101 | */ 102 | public Coroutine(CoroutineProto proto, int stackSize) { 103 | this.proto = proto; 104 | this.stack = new Stack(this, stackSize); 105 | this.state = State.NEW; 106 | 107 | if(proto == null) { 108 | throw new NullPointerException("proto"); 109 | } 110 | assert isInstrumented(proto) : "Not instrumented"; 111 | } 112 | 113 | /** 114 | * Returns the active Coroutine on this thread or NULL if no coroutine is running. 115 | * @return the active Coroutine on this thread or NULL if no coroutine is running. 116 | */ 117 | public static Coroutine getActiveCoroutine() { 118 | Stack s = Stack.getStack(); 119 | if(s != null) { 120 | return s.co; 121 | } 122 | return null; 123 | } 124 | 125 | /** 126 | * Returns the CoroutineProto that is used for this Coroutine 127 | * @return The CoroutineProto that is used for this Coroutine 128 | */ 129 | public CoroutineProto getProto() { 130 | return proto; 131 | } 132 | 133 | /** 134 | *

Returns the current state of this Coroutine. May be called by the Coroutine 135 | * itself but should not be called by another thread.

136 | * 137 | *

The Coroutine starts in the state NEW then changes to RUNNING. From 138 | * RUNNING it may change to FINISHED or SUSPENDED. SUSPENDED can only change 139 | * to RUNNING by calling run() again.

140 | * 141 | * @return The current state of this Coroutine 142 | * @see #run() 143 | */ 144 | public State getState() { 145 | return state; 146 | } 147 | 148 | /** 149 | * Runs the Coroutine until it is finished or suspended. This method must only 150 | * be called when the Coroutine is in the states NEW or SUSPENDED. It is not 151 | * multi threading safe. 152 | * 153 | * @throws java.lang.IllegalStateException if the Coroutine is currently running or already finished. 154 | */ 155 | @Override 156 | public void run() throws IllegalStateException { 157 | if(state != State.NEW && state != State.SUSPENDED) { 158 | throw new IllegalStateException("Not new or suspended"); 159 | } 160 | State result = State.FINISHED; 161 | Stack oldStack = Stack.getStack(); 162 | try { 163 | state = State.RUNNING; 164 | Stack.setStack(stack); 165 | try { 166 | proto.coExecute(); 167 | } catch (SuspendExecution ex) { 168 | assert ex == SuspendExecution.instance; 169 | result = State.SUSPENDED; 170 | //stack.dump(); 171 | stack.resumeStack(); 172 | } 173 | } finally { 174 | Stack.setStack(oldStack); 175 | state = result; 176 | } 177 | } 178 | 179 | private void writeObject(java.io.ObjectOutputStream out) throws IOException { 180 | if(state == State.RUNNING) { 181 | throw new IllegalStateException("trying to serialize a running coroutine"); 182 | } 183 | out.defaultWriteObject(); 184 | } 185 | 186 | @SuppressWarnings("unchecked") 187 | private boolean isInstrumented(CoroutineProto proto) { 188 | try { 189 | Class clz = Class.forName("de.matthiasmann.continuations.instrument.AlreadyInstrumented"); 190 | return proto.getClass().isAnnotationPresent((Class) clz); 191 | } catch (ClassNotFoundException ex) { 192 | return true; // can't check 193 | } catch (Throwable ex) { 194 | return true; // it's just a check - make sure we don't fail if something goes wrong 195 | } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/main/java/de/matthiasmann/continuations/CoroutineProto.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations; 30 | 31 | /** 32 | * A class that implements this interface can be run as a Coroutine. 33 | * 34 | * @see Coroutine 35 | * @author Matthias Mann 36 | */ 37 | public interface CoroutineProto { 38 | 39 | /** 40 | * Entry point for Coroutine execution. 41 | * 42 | * This method should never be called directly. 43 | * 44 | * @see Coroutine#Coroutine(de.matthiasmann.continuations.CoroutineProto) 45 | * @see Coroutine#run() 46 | * @see SuspendExecution 47 | * @throws de.matthiasmann.continuations.SuspendExecution This exception should never be cought 48 | */ 49 | public void coExecute() throws SuspendExecution; 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/de/matthiasmann/continuations/Stack.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations; 30 | 31 | import java.io.Serializable; 32 | 33 | /** 34 | * Internal Class - DO NOT USE ! 35 | * 36 | * Needs to be public so that instrumented code can access it. 37 | * ANY CHANGE IN THIS CLASS NEEDS TO BE SYNCHRONIZED WITH {@link de.matthiasmann.continuations.instrument.InstrumentMethod} 38 | * 39 | * @author Matthias Mann 40 | */ 41 | public final class Stack implements Serializable { 42 | 43 | private static final long serialVersionUID = 12786283751253L; 44 | 45 | private static final ThreadLocal tls = new ThreadLocal<>(); 46 | 47 | /** sadly this need to be here */ 48 | public static SuspendExecution exception_instance_not_for_user_code = SuspendExecution.instance; 49 | 50 | final Coroutine co; 51 | 52 | private int methodTOS = -1; 53 | private int[] method; 54 | 55 | private long[] dataLong; 56 | private Object[] dataObject; 57 | 58 | transient int curMethodSP; 59 | 60 | Stack(Coroutine co, int stackSize) { 61 | if(stackSize <= 0) { 62 | throw new IllegalArgumentException("stackSize"); 63 | } 64 | this.co = co; 65 | this.method = new int[8]; 66 | this.dataLong = new long[stackSize]; 67 | this.dataObject = new Object[stackSize]; 68 | } 69 | 70 | public static Stack getStack() { 71 | return tls.get(); 72 | } 73 | 74 | static void setStack(Stack s) { 75 | tls.set(s); 76 | } 77 | 78 | /** 79 | * Called before a method is called. 80 | * @param entry the entry point in the method for resume 81 | * @param numSlots the number of required stack slots for storing the state 82 | */ 83 | public final void pushMethodAndReserveSpace(int entry, int numSlots) { 84 | final int methodIdx = methodTOS; 85 | 86 | if(method.length - methodIdx < 2) { 87 | growMethodStack(); 88 | } 89 | 90 | curMethodSP = method[methodIdx-1]; 91 | int dataTOS = curMethodSP + numSlots; 92 | 93 | method[methodIdx] = entry; 94 | method[methodIdx+1] = dataTOS; 95 | 96 | //System.out.println("entry="+entry+" size="+size+" sp="+curMethodSP+" tos="+dataTOS+" nr="+methodIdx); 97 | 98 | if(dataTOS > dataObject.length) { 99 | growDataStack(dataTOS); 100 | } 101 | } 102 | 103 | /** 104 | * Called at the end of a method. 105 | * Undoes the effects of nextMethodEntry() and clears the dataObject[] array 106 | * to allow the values to be GCed. 107 | */ 108 | public final void popMethod() { 109 | int idx = methodTOS; 110 | method[idx] = 0; 111 | int oldSP = curMethodSP; 112 | int newSP = method[idx-1]; 113 | curMethodSP = newSP; 114 | methodTOS = idx-2; 115 | for(int i=newSP ; iAn exception used to initiate the control transfer.

33 | *

It does not support stack traces.

34 | * 35 | *

Methods which are declared to throw this exception are "suspendable". This 36 | * exception must always be propagated and never be caught.

37 | * 38 | *

Generic try/catch handlers are allowed:
39 | * {@code try{ doSomething(); } catch(Throwable ex) { handleException(ex); } }

40 | * 41 | *

The instrumentation ANT task will enhance the bytecode of these methods to 42 | * support suspension and continuation of their execution.

43 | * 44 | * @author Matthias Mann 45 | */ 46 | public final class SuspendExecution extends Exception { 47 | 48 | static final SuspendExecution instance = new SuspendExecution(); 49 | 50 | private SuspendExecution() { 51 | } 52 | 53 | @Override 54 | public Throwable fillInStackTrace() { 55 | return this; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/de/matthiasmann/continuations/Util.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations; 30 | 31 | /** 32 | * 33 | * @author Matthias Mann 34 | */ 35 | public class Util { 36 | 37 | public static int[] copyOf(int[] src, int size) { 38 | int[] dst = new int[size]; 39 | System.arraycopy(src, 0, dst, 0, Math.min(src.length, size)); 40 | return dst; 41 | } 42 | 43 | public static long[] copyOf(long[] src, int size) { 44 | long[] dst = new long[size]; 45 | System.arraycopy(src, 0, dst, 0, Math.min(src.length, size)); 46 | return dst; 47 | } 48 | 49 | public static Object[] copyOf(Object[] src, int size) { 50 | Object[] dst = new Object[size]; 51 | System.arraycopy(src, 0, dst, 0, Math.min(src.length, size)); 52 | return dst; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/de/matthiasmann/continuations/instrument/AlreadyInstrumented.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations.instrument; 30 | 31 | import java.lang.annotation.ElementType; 32 | import java.lang.annotation.Retention; 33 | import java.lang.annotation.RetentionPolicy; 34 | import java.lang.annotation.Target; 35 | 36 | /** 37 | * An annotation used to mark a class as instrumented. 38 | * It must never be used in Java source code. 39 | * 40 | * @author Matthias Mann 41 | */ 42 | @Target(ElementType.TYPE) 43 | @Retention(RetentionPolicy.RUNTIME) 44 | public @interface AlreadyInstrumented { 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/de/matthiasmann/continuations/instrument/CheckInstrumentationVisitor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2013, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations.instrument; 30 | 31 | import de.matthiasmann.continuations.SuspendExecution; 32 | import de.matthiasmann.continuations.instrument.MethodDatabase.ClassEntry; 33 | import org.objectweb.asm.AnnotationVisitor; 34 | import org.objectweb.asm.ClassVisitor; 35 | import org.objectweb.asm.MethodVisitor; 36 | import org.objectweb.asm.Opcodes; 37 | import org.objectweb.asm.Type; 38 | 39 | /** 40 | * Check if a class contains suspendable methods. 41 | * Basicly this class checks if a method is declared to throw {@link SuspendExecution}. 42 | * 43 | * @author Matthias Mann 44 | */ 45 | public class CheckInstrumentationVisitor extends ClassVisitor { 46 | 47 | static final String EXCEPTION_NAME = Type.getInternalName(SuspendExecution.class); 48 | static final String EXCEPTION_DESC = Type.getDescriptor(SuspendExecution.class); 49 | 50 | private String className; 51 | private ClassEntry classEntry; 52 | private boolean hasSuspendable; 53 | private boolean alreadyInstrumented; 54 | 55 | public CheckInstrumentationVisitor() { 56 | super(Opcodes.ASM5); 57 | } 58 | 59 | public boolean needsInstrumentation() { 60 | return hasSuspendable; 61 | } 62 | 63 | ClassEntry getClassEntry() { 64 | return classEntry; 65 | } 66 | 67 | public String getName() { 68 | return className; 69 | } 70 | 71 | public boolean isAlreadyInstrumented() { 72 | return alreadyInstrumented; 73 | } 74 | 75 | @Override 76 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 77 | this.className = name; 78 | this.classEntry = new ClassEntry(superName); 79 | } 80 | 81 | @Override 82 | public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 83 | if(desc.equals(InstrumentClass.ALREADY_INSTRUMENTED_NAME)) { 84 | alreadyInstrumented = true; 85 | } 86 | return null; 87 | } 88 | 89 | @Override 90 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 91 | boolean suspendable = checkExceptions(exceptions); 92 | if(suspendable) { 93 | hasSuspendable = true; 94 | // synchronized methods can't be made suspendable 95 | if((access & Opcodes.ACC_SYNCHRONIZED) == Opcodes.ACC_SYNCHRONIZED) { 96 | throw new UnableToInstrumentException("synchronized method", className, name, desc); 97 | } 98 | } 99 | classEntry.set(name, desc, suspendable); 100 | return null; 101 | } 102 | 103 | public static boolean checkExceptions(String[] exceptions) { 104 | if(exceptions != null) { 105 | for(String ex : exceptions) { 106 | if(ex.equals(EXCEPTION_NAME)) { 107 | return true; 108 | } 109 | } 110 | } 111 | return false; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/de/matthiasmann/continuations/instrument/DBClassWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2013, Matthias Mann 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Matthias Mann nor the names of its contributors may 15 | * be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 22 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 23 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 26 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | package de.matthiasmann.continuations.instrument; 31 | 32 | import org.objectweb.asm.ClassReader; 33 | import org.objectweb.asm.ClassWriter; 34 | 35 | /** 36 | * 37 | * @author Matthias Mann 38 | */ 39 | public class DBClassWriter extends ClassWriter { 40 | 41 | private final MethodDatabase db; 42 | 43 | public DBClassWriter(MethodDatabase db, ClassReader classReader) { 44 | super(classReader, COMPUTE_FRAMES | COMPUTE_MAXS); 45 | this.db = db; 46 | } 47 | 48 | @Override 49 | protected String getCommonSuperClass(String type1, String type2) { 50 | return db.getCommonSuperClass(type1, type2); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/de/matthiasmann/continuations/instrument/ExtractSuperClass.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2012, Matthias Mann 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Matthias Mann nor the names of its contributors may 15 | * be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 22 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 23 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 26 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | package de.matthiasmann.continuations.instrument; 31 | 32 | import org.objectweb.asm.ClassVisitor; 33 | import org.objectweb.asm.Opcodes; 34 | 35 | /** 36 | * 37 | * @author Matthias Mann 38 | */ 39 | public class ExtractSuperClass extends ClassVisitor { 40 | 41 | String superClass; 42 | 43 | public ExtractSuperClass() { 44 | super(Opcodes.ASM5); 45 | } 46 | 47 | @Override 48 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 49 | this.superClass = superName; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/de/matthiasmann/continuations/instrument/InstrumentClass.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2013, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations.instrument; 30 | 31 | import java.util.ArrayList; 32 | import java.util.List; 33 | 34 | import org.objectweb.asm.AnnotationVisitor; 35 | import org.objectweb.asm.ClassVisitor; 36 | import org.objectweb.asm.MethodVisitor; 37 | import org.objectweb.asm.Opcodes; 38 | import org.objectweb.asm.Type; 39 | import org.objectweb.asm.tree.MethodNode; 40 | import org.objectweb.asm.tree.analysis.AnalyzerException; 41 | 42 | import de.matthiasmann.continuations.Coroutine; 43 | import de.matthiasmann.continuations.instrument.MethodDatabase.ClassEntry; 44 | 45 | /** 46 | * Instrument a class by instrumenting all suspendable methods and copying the others. 47 | * 48 | * @author Matthias Mann 49 | */ 50 | public class InstrumentClass extends ClassVisitor { 51 | 52 | static final String COROUTINE_NAME = Type.getInternalName(Coroutine.class); 53 | static final String ALREADY_INSTRUMENTED_NAME = Type.getDescriptor(AlreadyInstrumented.class); 54 | 55 | private final MethodDatabase db; 56 | private final boolean forceInstrumentation; 57 | private String className; 58 | private ClassEntry classEntry; 59 | private boolean alreadyInstrumented; 60 | private ArrayList methods; 61 | 62 | public InstrumentClass(ClassVisitor cv, MethodDatabase db, boolean forceInstrumentation) { 63 | super(Opcodes.ASM5, cv); 64 | 65 | this.db = db; 66 | this.forceInstrumentation = forceInstrumentation; 67 | } 68 | 69 | @Override 70 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 71 | this.className = name; 72 | this.classEntry = new ClassEntry(superName); 73 | 74 | // need atleast 1.5 for annotations to work 75 | if(version < Opcodes.V1_5) { 76 | version = Opcodes.V1_5; 77 | } 78 | 79 | super.visit(version, access, name, signature, superName, interfaces); 80 | } 81 | 82 | @Override 83 | public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 84 | if(desc.equals(InstrumentClass.ALREADY_INSTRUMENTED_NAME)) { 85 | alreadyInstrumented = true; 86 | } 87 | return super.visitAnnotation(desc, visible); 88 | } 89 | 90 | @Override 91 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 92 | boolean suspendable = CheckInstrumentationVisitor.checkExceptions(exceptions); 93 | classEntry.set(name, desc, suspendable); 94 | 95 | if(suspendable && checkAccess(access) && !(className.equals(COROUTINE_NAME) && name.equals("yield"))) { 96 | if(db.isDebug()) { 97 | db.log(LogLevel.INFO, "Instrumenting method %s#%s", className, name); 98 | } 99 | 100 | if(methods == null) { 101 | methods = new ArrayList<>(); 102 | } 103 | 104 | MethodNode mn = new MethodNode(access, name, desc, signature, exceptions); 105 | methods.add(mn); 106 | return mn; 107 | } 108 | return super.visitMethod(access, name, desc, signature, exceptions); 109 | } 110 | 111 | @Override 112 | @SuppressWarnings("CallToThreadDumpStack") 113 | public void visitEnd() { 114 | db.recordSuspendableMethods(className, classEntry); 115 | 116 | if(methods != null) { 117 | if(alreadyInstrumented && !forceInstrumentation) { 118 | for(MethodNode mn : methods) { 119 | mn.accept(makeOutMV(mn)); 120 | } 121 | } else { 122 | if(!alreadyInstrumented) { 123 | super.visitAnnotation(ALREADY_INSTRUMENTED_NAME, true); 124 | } 125 | 126 | for(MethodNode mn : methods) { 127 | MethodVisitor outMV = makeOutMV(mn); 128 | try { 129 | InstrumentMethod im = new InstrumentMethod(db, className, mn); 130 | if(im.collectCodeBlocks()) { 131 | if(mn.name.charAt(0) == '<') { 132 | throw new UnableToInstrumentException("special method", className, mn.name, mn.desc); 133 | } 134 | im.accept(outMV); 135 | } else { 136 | mn.accept(outMV); 137 | } 138 | } catch(AnalyzerException ex) { 139 | ex.printStackTrace(); 140 | throw new InternalError(ex.getMessage()); 141 | } 142 | } 143 | } 144 | } 145 | super.visitEnd(); 146 | } 147 | 148 | private MethodVisitor makeOutMV(MethodNode mn) { 149 | return super.visitMethod(mn.access, mn.name, mn.desc, mn.signature, toStringArray(mn.exceptions)); 150 | } 151 | 152 | private static boolean checkAccess(int access) { 153 | return (access & (Opcodes.ACC_ABSTRACT | Opcodes.ACC_NATIVE)) == 0; 154 | } 155 | 156 | private static String[] toStringArray(List l) { 157 | if(l.isEmpty()) { 158 | return null; 159 | } 160 | return l.toArray(new String[l.size()]); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/main/java/de/matthiasmann/continuations/instrument/InstrumentMethod.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2013, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations.instrument; 30 | 31 | import java.util.List; 32 | 33 | import org.objectweb.asm.Label; 34 | import org.objectweb.asm.MethodVisitor; 35 | import org.objectweb.asm.Opcodes; 36 | import org.objectweb.asm.Type; 37 | import org.objectweb.asm.tree.AbstractInsnNode; 38 | import org.objectweb.asm.tree.AnnotationNode; 39 | import org.objectweb.asm.tree.InsnList; 40 | import org.objectweb.asm.tree.LabelNode; 41 | import org.objectweb.asm.tree.LocalVariableNode; 42 | import org.objectweb.asm.tree.MethodInsnNode; 43 | import org.objectweb.asm.tree.MethodNode; 44 | import org.objectweb.asm.tree.TryCatchBlockNode; 45 | import org.objectweb.asm.tree.analysis.Analyzer; 46 | import org.objectweb.asm.tree.analysis.AnalyzerException; 47 | import org.objectweb.asm.tree.analysis.BasicValue; 48 | import org.objectweb.asm.tree.analysis.Frame; 49 | import org.objectweb.asm.tree.analysis.Value; 50 | 51 | import de.matthiasmann.continuations.Stack; 52 | import de.matthiasmann.continuations.SuspendExecution; 53 | 54 | /** 55 | * Instrument a method to allow suspension 56 | * 57 | * @author Matthias Mann 58 | */ 59 | public class InstrumentMethod { 60 | 61 | private static final String STACK_NAME = Type.getInternalName(Stack.class); 62 | 63 | private final MethodDatabase db; 64 | private final String className; 65 | private final MethodNode mn; 66 | private final Frame[] frames; 67 | private final int lvarStack; 68 | private final int firstLocal; 69 | 70 | private FrameInfo[] codeBlocks = new FrameInfo[32]; 71 | private int numCodeBlocks; 72 | private int additionalLocals; 73 | 74 | private boolean warnedAboutMonitors; 75 | private int warnedAboutBlocking; 76 | 77 | private static final BlockingMethod BLOCKING_METHODS[] = { 78 | new BlockingMethod("java/lang/Thread", "sleep", "(J)V", "(JI)V"), 79 | new BlockingMethod("java/lang/Thread", "join", "()V", "(J)V", "(JI)V"), 80 | new BlockingMethod("java/lang/Object", "wait", "()V", "(J)V", "(JI)V"), 81 | new BlockingMethod("java/util/concurrent/locks/Lock", "lock", "()V"), 82 | new BlockingMethod("java/util/concurrent/locks/Lock", "lockInterruptibly", "()V"), 83 | }; 84 | 85 | public InstrumentMethod(MethodDatabase db, String className, MethodNode mn) throws AnalyzerException { 86 | this.db = db; 87 | this.className = className; 88 | this.mn = mn; 89 | 90 | try { 91 | Analyzer a = new TypeAnalyzer(db); 92 | this.frames = a.analyze(className, mn); 93 | this.lvarStack = mn.maxLocals; 94 | this.firstLocal = ((mn.access & Opcodes.ACC_STATIC) == Opcodes.ACC_STATIC) ? 0 : 1; 95 | } catch (UnsupportedOperationException ex) { 96 | throw new AnalyzerException(null, ex.getMessage(), ex); 97 | } 98 | } 99 | 100 | public boolean collectCodeBlocks() { 101 | int numIns = mn.instructions.size(); 102 | 103 | codeBlocks[0] = FrameInfo.FIRST; 104 | for(int i=0 ; i= 0) { 119 | int mask = 1 << blockingId; 120 | if(!db.isAllowBlocking()) { 121 | throw new UnableToInstrumentException("blocking call to " + 122 | min.owner + "#" + min.name + min.desc, className, mn.name, mn.desc); 123 | } else if((warnedAboutBlocking & mask) == 0) { 124 | warnedAboutBlocking |= mask; 125 | db.log(LogLevel.WARNING, "Method %s#%s%s contains potentially blocking call to " + 126 | min.owner + "#" + min.name + min.desc, className, mn.name, mn.desc); 127 | } 128 | } 129 | } 130 | } 131 | } 132 | } 133 | addCodeBlock(null, numIns); 134 | 135 | return numCodeBlocks > 1; 136 | } 137 | 138 | private static int isBlockingCall(MethodInsnNode ins) { 139 | for(int i=0,n=BLOCKING_METHODS.length ; i= fi.endInstruction) { 282 | //System.out.println("i="+i+" start="+start+" end="+end+" split="+splitIdx+ 283 | // " start="+mn.instructions.get(start)+" end="+mn.instructions.get(end)); 284 | 285 | // need to split try/catch around the suspendable call 286 | if(start == fi.endInstruction) { 287 | tcb.start = fi.createAfterLabel(); 288 | } else { 289 | if(end > fi.endInstruction) { 290 | TryCatchBlockNode tcb2 = new TryCatchBlockNode( 291 | fi.createAfterLabel(), 292 | tcb.end, tcb.handler, tcb.type); 293 | mn.tryCatchBlocks.add(i+1, tcb2); 294 | } 295 | 296 | tcb.end = fi.createBeforeLabel(); 297 | } 298 | } 299 | } 300 | } 301 | 302 | private void dumpCodeBlock(MethodVisitor mv, int idx, int skip) { 303 | int start = codeBlocks[idx].endInstruction; 304 | int end = codeBlocks[idx+1].endInstruction; 305 | 306 | for(int i=start+skip ; i".equals(min.name)) { 331 | int argSize = TypeAnalyzer.getNumArguments(min.desc); 332 | Frame frame = frames[i]; 333 | int stackIndex = frame.getStackSize() - argSize - 1; 334 | Value thisValue = frame.getStack(stackIndex); 335 | if(stackIndex >= 1 && 336 | isNewValue(thisValue, true) && 337 | isNewValue(frame.getStack(stackIndex-1), false)) { 338 | NewValue newValue = (NewValue)thisValue; 339 | if(newValue.omitted) { 340 | emitNewAndDup(mv, frame, stackIndex, min); 341 | } 342 | } else { 343 | db.log(LogLevel.WARNING, "Expected to find a NewValue on stack index %d: %s", stackIndex, frame); 344 | } 345 | } 346 | break; 347 | } 348 | ins.accept(mv); 349 | } 350 | } 351 | 352 | private static void dumpParameterAnnotations(MethodVisitor mv, List[] parameterAnnotations, boolean visible) { 353 | for(int i=0 ; i= -1 && value <= 5) { 364 | mv.visitInsn(Opcodes.ICONST_0 + value); 365 | } else if((byte)value == value) { 366 | mv.visitIntInsn(Opcodes.BIPUSH, value); 367 | } else if((short)value == value) { 368 | mv.visitIntInsn(Opcodes.SIPUSH, value); 369 | } else { 370 | mv.visitLdcInsn(value); 371 | } 372 | } 373 | 374 | private void emitNewAndDup(MethodVisitor mv, Frame frame, int stackIndex, MethodInsnNode min) { 375 | int arguments = frame.getStackSize() - stackIndex - 1; 376 | int neededLocals = 0; 377 | for(int i=arguments ; i>=1 ; i--) { 378 | BasicValue v = (BasicValue)frame.getStack(stackIndex+i); 379 | mv.visitVarInsn(v.getType().getOpcode(Opcodes.ISTORE), lvarStack+1+neededLocals); 380 | neededLocals += v.getSize(); 381 | } 382 | db.log(LogLevel.DEBUG, "Inserting NEW & DUP for constructor call %s%s with %d arguments (%d locals)", min.owner, min.desc, arguments, neededLocals); 383 | if(additionalLocals < neededLocals) { 384 | additionalLocals = neededLocals; 385 | } 386 | ((NewValue)frame.getStack(stackIndex-1)).insn.accept(mv); 387 | ((NewValue)frame.getStack(stackIndex )).insn.accept(mv); 388 | for(int i=1 ; i<=arguments ; i++) { 389 | BasicValue v = (BasicValue)frame.getStack(stackIndex+i); 390 | neededLocals -= v.getSize(); 391 | mv.visitVarInsn(v.getType().getOpcode(Opcodes.ILOAD), lvarStack+1+neededLocals); 392 | } 393 | } 394 | 395 | private void emitPopMethod(MethodVisitor mv) { 396 | mv.visitVarInsn(Opcodes.ALOAD, lvarStack); 397 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, STACK_NAME, "popMethod", "()V"); 398 | } 399 | 400 | private void emitStoreState(MethodVisitor mv, int idx, FrameInfo fi) { 401 | Frame f = frames[fi.endInstruction]; 402 | 403 | if(fi.lBefore != null) { 404 | fi.lBefore.accept(mv); 405 | } 406 | 407 | mv.visitVarInsn(Opcodes.ALOAD,lvarStack); 408 | emitConst(mv, idx); 409 | emitConst(mv, fi.numSlots); 410 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, STACK_NAME, "pushMethodAndReserveSpace", "(II)V"); 411 | 412 | for(int i=f.getStackSize() ; i-->0 ;) { 413 | BasicValue v = (BasicValue) f.getStack(i); 414 | if(!isOmitted(v)) { 415 | if(!isNullType(v)) { 416 | int slotIdx = fi.stackSlotIndices[i]; 417 | assert slotIdx >= 0 && slotIdx < fi.numSlots; 418 | emitStoreValue(mv, v, lvarStack, slotIdx); 419 | } else { 420 | db.log(LogLevel.DEBUG, "NULL stack entry: type=%s size=%d", v.getType(), v.getSize()); 421 | mv.visitInsn(Opcodes.POP); 422 | } 423 | } 424 | } 425 | 426 | for(int i=firstLocal ; i= 0 && slotIdx < fi.numSlots; 432 | emitStoreValue(mv, v, lvarStack, slotIdx); 433 | } 434 | } 435 | } 436 | 437 | private void emitRestoreState(MethodVisitor mv, int idx, FrameInfo fi) { 438 | Frame f = frames[fi.endInstruction]; 439 | 440 | for(int i=firstLocal ; i= 0 && slotIdx < fi.numSlots; 445 | emitRestoreValue(mv, v, lvarStack, slotIdx); 446 | mv.visitVarInsn(v.getType().getOpcode(Opcodes.ISTORE), i); 447 | } else if(v != BasicValue.UNINITIALIZED_VALUE) { 448 | mv.visitInsn(Opcodes.ACONST_NULL); 449 | mv.visitVarInsn(Opcodes.ASTORE, i); 450 | } 451 | } 452 | 453 | for(int i=0 ; i= 0 && slotIdx < fi.numSlots; 459 | emitRestoreValue(mv, v, lvarStack, slotIdx); 460 | } else { 461 | mv.visitInsn(Opcodes.ACONST_NULL); 462 | } 463 | } 464 | } 465 | 466 | if(fi.lAfter != null) { 467 | fi.lAfter.accept(mv); 468 | } 469 | } 470 | 471 | private void emitStoreValue(MethodVisitor mv, BasicValue v, int lvarStack, int idx) throws InternalError, IndexOutOfBoundsException { 472 | String desc; 473 | 474 | switch(v.getType().getSort()) { 475 | case Type.OBJECT: 476 | case Type.ARRAY: 477 | desc = "(Ljava/lang/Object;L"+STACK_NAME+";I)V"; 478 | break; 479 | case Type.BOOLEAN: 480 | case Type.BYTE: 481 | case Type.SHORT: 482 | case Type.CHAR: 483 | case Type.INT: 484 | desc = "(IL"+STACK_NAME+";I)V"; 485 | break; 486 | case Type.FLOAT: 487 | desc = "(FL"+STACK_NAME+";I)V"; 488 | break; 489 | case Type.LONG: 490 | desc = "(JL"+STACK_NAME+";I)V"; 491 | break; 492 | case Type.DOUBLE: 493 | desc = "(DL"+STACK_NAME+";I)V"; 494 | break; 495 | default: 496 | throw new InternalError("Unexpected type: " + v.getType()); 497 | } 498 | 499 | mv.visitVarInsn(Opcodes.ALOAD, lvarStack); 500 | emitConst(mv, idx); 501 | mv.visitMethodInsn(Opcodes.INVOKESTATIC, STACK_NAME, "push", desc); 502 | } 503 | 504 | private void emitRestoreValue(MethodVisitor mv, BasicValue v, int lvarStack, int idx) { 505 | mv.visitVarInsn(Opcodes.ALOAD, lvarStack); 506 | emitConst(mv, idx); 507 | 508 | switch(v.getType().getSort()) { 509 | case Type.OBJECT: 510 | String internalName = v.getType().getInternalName(); 511 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, STACK_NAME, "getObject", "(I)Ljava/lang/Object;"); 512 | if(!internalName.equals("java/lang/Object")) { // don't cast to Object ;) 513 | mv.visitTypeInsn(Opcodes.CHECKCAST, internalName); 514 | } 515 | break; 516 | case Type.ARRAY: 517 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, STACK_NAME, "getObject", "(I)Ljava/lang/Object;"); 518 | mv.visitTypeInsn(Opcodes.CHECKCAST, v.getType().getDescriptor()); 519 | break; 520 | case Type.BYTE: 521 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, STACK_NAME, "getInt", "(I)I"); 522 | mv.visitInsn(Opcodes.I2B); 523 | break; 524 | case Type.SHORT: 525 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, STACK_NAME, "getInt", "(I)I"); 526 | mv.visitInsn(Opcodes.I2S); 527 | break; 528 | case Type.CHAR: 529 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, STACK_NAME, "getInt", "(I)I"); 530 | mv.visitInsn(Opcodes.I2C); 531 | break; 532 | case Type.BOOLEAN: 533 | case Type.INT: 534 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, STACK_NAME, "getInt", "(I)I"); 535 | break; 536 | case Type.FLOAT: 537 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, STACK_NAME, "getFloat", "(I)F"); 538 | break; 539 | case Type.LONG: 540 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, STACK_NAME, "getLong", "(I)J"); 541 | break; 542 | case Type.DOUBLE: 543 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, STACK_NAME, "getDouble", "(I)D"); 544 | break; 545 | default: 546 | throw new InternalError("Unexpected type: " + v.getType()); 547 | } 548 | } 549 | 550 | static boolean isNullType(BasicValue v) { 551 | return (v == BasicValue.UNINITIALIZED_VALUE) || 552 | (v.isReference() && v.getType().getInternalName().equals("null")); 553 | } 554 | 555 | static boolean isOmitted(BasicValue v) { 556 | if(v instanceof NewValue) { 557 | return ((NewValue)v).omitted; 558 | } 559 | return false; 560 | } 561 | 562 | static boolean isNewValue(Value v, boolean dupped) { 563 | if(v instanceof NewValue) { 564 | return ((NewValue)v).isDupped == dupped; 565 | } 566 | return false; 567 | } 568 | 569 | static class BlockLabelNode extends LabelNode { 570 | final int idx; 571 | 572 | BlockLabelNode(int idx) { 573 | this.idx = idx; 574 | } 575 | } 576 | 577 | static class FrameInfo { 578 | static final FrameInfo FIRST = new FrameInfo(null, 0, 0, null, null); 579 | 580 | final int endInstruction; 581 | final int numSlots; 582 | final int numObjSlots; 583 | final int[] localSlotIndices; 584 | final int[] stackSlotIndices; 585 | 586 | BlockLabelNode lBefore; 587 | BlockLabelNode lAfter; 588 | 589 | FrameInfo(Frame f, int firstLocal, int endInstruction, InsnList insnList, MethodDatabase db) { 590 | this.endInstruction = endInstruction; 591 | 592 | int idxObj = 0; 593 | int idxPrim = 0; 594 | 595 | if(f != null) { 596 | stackSlotIndices = new int[f.getStackSize()]; 597 | for(int i=0 ; iInstrumentation ANT task

51 | * 52 | *

It requires one or more FileSet elements pointing to class files that should 53 | * be instrumented.

54 | *

Classes that are referenced from the instrumented classes are searched in 55 | * the classpath of the task. If a referenced class is not found a warning is 56 | * generated and the instrumentation will result in less efficent code.

57 | * 58 | *

The following options can be set:

    59 | *
  • check - default: false
    The resulting code is run through a verifier.
  • 60 | *
  • verbose - default: false
    The name of each processed class and all suspendable method calles is displayed.
  • 61 | *
  • debug - default: false
    Prints internal debugging information.
  • 62 | *
  • allowmonitors - default: false
    Allows the use of synchronized statements - this is DANGEROUS !
  • 63 | *
  • allowblocking - default: false
    Allows the use known blocking calls like Thread.sleep, Object.wait etc.
  • 64 | *

65 | * 66 | * @see ANT FileSet 67 | * @see SuspendExecution 68 | * @author Matthias Mann 69 | */ 70 | public class InstrumentationTask extends Task { 71 | 72 | private ArrayList filesets = new ArrayList<>(); 73 | private boolean check; 74 | private boolean verbose; 75 | private boolean allowMonitors; 76 | private boolean allowBlocking; 77 | private boolean debug; 78 | private boolean writeClasses = true; 79 | 80 | public void addFileSet(FileSet fs) { 81 | filesets.add(fs); 82 | } 83 | 84 | public void setCheck(boolean check) { 85 | this.check = check; 86 | } 87 | 88 | public void setVerbose(boolean verbose) { 89 | this.verbose = verbose; 90 | } 91 | 92 | public void setAllowMonitors(boolean allowMonitors) { 93 | this.allowMonitors = allowMonitors; 94 | } 95 | 96 | public void setAllowBlocking(boolean allowBlocking) { 97 | this.allowBlocking = allowBlocking; 98 | } 99 | 100 | public void setDebug(boolean debug) { 101 | this.debug = debug; 102 | } 103 | 104 | public void setWriteClasses(boolean writeClasses) { 105 | this.writeClasses = writeClasses; 106 | } 107 | 108 | @Override 109 | public void execute() throws BuildException { 110 | MethodDatabase db = new MethodDatabase(getClass().getClassLoader()); 111 | 112 | db.setVerbose(verbose); 113 | db.setDebug(debug); 114 | db.setAllowMonitors(allowMonitors); 115 | db.setAllowBlocking(allowBlocking); 116 | db.setLog(new Log() { 117 | @Override 118 | public void log(LogLevel level, String msg, Object... args) { 119 | int msgLevel; 120 | switch(level) { 121 | case DEBUG: msgLevel = Project.MSG_INFO; break; 122 | case INFO: msgLevel = Project.MSG_INFO; break; 123 | case WARNING: msgLevel = Project.MSG_WARN; break; 124 | default: throw new AssertionError("Unhandled log level: " + level); 125 | } 126 | InstrumentationTask.this.log(level+": "+String.format(msg, args), msgLevel); 127 | } 128 | @Override 129 | public void error(String msg, Exception ex) { 130 | InstrumentationTask.this.log("ERROR: "+msg, ex, Project.MSG_ERR); 131 | } 132 | }); 133 | 134 | try { 135 | for(FileSet fs : filesets) { 136 | DirectoryScanner ds = fs.getDirectoryScanner(getProject()); 137 | String[] includedFiles = ds.getIncludedFiles(); 138 | 139 | for(String filename : includedFiles) { 140 | if(filename.endsWith(".class")) { 141 | File file = new File(fs.getDir(), filename); 142 | if(file.isFile()) { 143 | db.checkClass(file); 144 | } else { 145 | log("File not found: " + filename); 146 | } 147 | } 148 | } 149 | } 150 | 151 | db.log(LogLevel.INFO, "Instrumenting " + db.getWorkList().size() + " classes"); 152 | 153 | for(File f : db.getWorkList()) { 154 | instrumentClass(db, f); 155 | } 156 | } catch (UnableToInstrumentException ex) { 157 | log(ex.getMessage()); 158 | throw new BuildException(ex.getMessage(), ex); 159 | } 160 | } 161 | 162 | private void instrumentClass(MethodDatabase db, File f) { 163 | db.log(LogLevel.INFO, "Instrumenting class %s", f); 164 | 165 | try { 166 | ClassReader r; 167 | 168 | FileInputStream fis = new FileInputStream(f); 169 | try { 170 | r = new ClassReader(fis); 171 | } finally { 172 | fis.close(); 173 | } 174 | 175 | ClassWriter cw = new DBClassWriter(db, r); 176 | ClassVisitor cv = check ? new CheckClassAdapter(cw) : cw; 177 | InstrumentClass ic = new InstrumentClass(cv, db, false); 178 | r.accept(ic, ClassReader.SKIP_FRAMES); 179 | 180 | byte[] newClass = cw.toByteArray(); 181 | 182 | if(writeClasses) { 183 | FileOutputStream fos = new FileOutputStream(f); 184 | try { 185 | fos.write(newClass); 186 | } finally { 187 | fos.close(); 188 | } 189 | } 190 | } catch (IOException ex) { 191 | throw new BuildException("Instrumenting file " + f, ex); 192 | } 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/main/java/de/matthiasmann/continuations/instrument/JavaAgent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2013, Matthias Mann 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Matthias Mann nor the names of its contributors may 15 | * be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 22 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 23 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 26 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | /* 32 | * Copyright (c) 2012, Enhanced Four 33 | * All rights reserved. 34 | * 35 | * Redistribution and use in source and binary forms, with or without 36 | * modification, are permitted provided that the following conditions are 37 | * met: 38 | * 39 | * * Redistributions of source code must retain the above copyright 40 | * notice, this list of conditions and the following disclaimer. 41 | * 42 | * * Redistributions in binary form must reproduce the above copyright 43 | * notice, this list of conditions and the following disclaimer in the 44 | * documentation and/or other materials provided with the distribution. 45 | * 46 | * * Neither the name of 'Enhanced Four' nor the names of its contributors 47 | * may be used to endorse or promote products derived from this software 48 | * without specific prior written permission. 49 | * 50 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 51 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 52 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 53 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 54 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 55 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 56 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 57 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 58 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 59 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 60 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 61 | */ 62 | package de.matthiasmann.continuations.instrument; 63 | 64 | import java.lang.instrument.ClassFileTransformer; 65 | import java.lang.instrument.IllegalClassFormatException; 66 | import java.lang.instrument.Instrumentation; 67 | import java.security.ProtectionDomain; 68 | 69 | import org.objectweb.asm.ClassReader; 70 | import org.objectweb.asm.ClassVisitor; 71 | import org.objectweb.asm.ClassWriter; 72 | import org.objectweb.asm.util.CheckClassAdapter; 73 | 74 | /* 75 | * Created on Nov 21, 2010 76 | * 77 | * @author Riven 78 | * @author Matthias Mann 79 | */ 80 | public class JavaAgent { 81 | public static void premain(String agentArguments, Instrumentation instrumentation) { 82 | MethodDatabase db = new MethodDatabase(Thread.currentThread().getContextClassLoader()); 83 | boolean checkArg = false; 84 | 85 | if(agentArguments != null) { 86 | for(char c : agentArguments.toCharArray()) { 87 | switch(c) { 88 | case 'v': 89 | db.setVerbose(true); 90 | break; 91 | 92 | case 'd': 93 | db.setDebug(true); 94 | break; 95 | 96 | case 'm': 97 | db.setAllowMonitors(true); 98 | break; 99 | 100 | case 'c': 101 | checkArg = true; 102 | break; 103 | 104 | case 'b': 105 | db.setAllowBlocking(true); 106 | break; 107 | 108 | default: 109 | throw new IllegalStateException("Usage: vdmc (verbose, debug, allow monitors, check class)"); 110 | } 111 | } 112 | } 113 | 114 | db.setLog(new Log() { 115 | public void log(LogLevel level, String msg, Object... args) { 116 | System.out.println("[Continuations] " + level + ": " + String.format(msg, args)); 117 | } 118 | 119 | public void error(String msg, Exception exc) { 120 | System.out.println("[Continuations] ERROR: " + msg); 121 | 122 | exc.printStackTrace(System.out); 123 | } 124 | }); 125 | 126 | instrumentation.addTransformer(new Transformer(db, checkArg)); 127 | } 128 | 129 | static byte[] instrumentClass(MethodDatabase db, byte[] data, boolean check) { 130 | ClassReader r = new ClassReader(data); 131 | ClassWriter cw = new DBClassWriter(db, r); 132 | ClassVisitor cv = check ? new CheckClassAdapter(cw) : cw; 133 | InstrumentClass ic = new InstrumentClass(cv, db, false); 134 | r.accept(ic, ClassReader.SKIP_FRAMES); 135 | return cw.toByteArray(); 136 | } 137 | 138 | private static class Transformer implements ClassFileTransformer { 139 | private final MethodDatabase db; 140 | private final boolean check; 141 | 142 | public Transformer(MethodDatabase db, boolean check) { 143 | this.db = db; 144 | this.check = check; 145 | } 146 | 147 | @Override 148 | public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, 149 | ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { 150 | if(MethodDatabase.isJavaCore(className)) { 151 | return null; 152 | } 153 | if(className.startsWith("org/objectweb/asm/")) { 154 | return null; 155 | } 156 | 157 | db.log(LogLevel.INFO, "TRANSFORM: %s", className); 158 | 159 | try { 160 | return instrumentClass(db, classfileBuffer, check); 161 | } catch(Exception ex) { 162 | db.error("Unable to instrument", ex); 163 | return null; 164 | } 165 | } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/main/java/de/matthiasmann/continuations/instrument/Log.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations.instrument; 30 | 31 | /** 32 | * Allow access to the ANT logging routines 33 | * 34 | * @author Matthias Mann 35 | */ 36 | public interface Log { 37 | 38 | public void log(LogLevel level, String msg, Object ... args); 39 | 40 | public void error(String msg, Exception ex); 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/de/matthiasmann/continuations/instrument/LogLevel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2013, Matthias Mann 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Matthias Mann nor the names of its contributors may 15 | * be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 22 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 23 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 26 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | package de.matthiasmann.continuations.instrument; 31 | 32 | /** 33 | * 34 | * @author Matthias Mann 35 | */ 36 | public enum LogLevel { 37 | DEBUG, 38 | INFO, 39 | WARNING 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/de/matthiasmann/continuations/instrument/MethodDatabase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2013, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations.instrument; 30 | 31 | import java.io.File; 32 | import java.io.FileInputStream; 33 | import java.io.IOException; 34 | import java.io.InputStream; 35 | import java.util.ArrayList; 36 | import java.util.HashMap; 37 | 38 | import org.objectweb.asm.ClassReader; 39 | 40 | /** 41 | *

Collects information about classes and their suspendable methods.

42 | *

Provides access to configuration parameters and to logging

43 | * 44 | * @author Matthias Mann 45 | */ 46 | public class MethodDatabase implements Log { 47 | 48 | private final ClassLoader cl; 49 | private final HashMap classes; 50 | private final HashMap superClasses; 51 | private final ArrayList workList; 52 | 53 | private Log log; 54 | private boolean verbose; 55 | private boolean debug; 56 | private boolean allowMonitors; 57 | private boolean allowBlocking; 58 | private int logLevelMask; 59 | 60 | public MethodDatabase(ClassLoader classloader) { 61 | if(classloader == null) { 62 | throw new NullPointerException("classloader"); 63 | } 64 | 65 | this.cl = classloader; 66 | 67 | classes = new HashMap<>(); 68 | superClasses = new HashMap<>(); 69 | workList = new ArrayList<>(); 70 | 71 | setLogLevelMask(); 72 | } 73 | 74 | public boolean isAllowMonitors() { 75 | return allowMonitors; 76 | } 77 | 78 | public void setAllowMonitors(boolean allowMonitors) { 79 | this.allowMonitors = allowMonitors; 80 | } 81 | 82 | public boolean isAllowBlocking() { 83 | return allowBlocking; 84 | } 85 | 86 | public void setAllowBlocking(boolean allowBlocking) { 87 | this.allowBlocking = allowBlocking; 88 | } 89 | 90 | public Log getLog() { 91 | return log; 92 | } 93 | 94 | public void setLog(Log log) { 95 | this.log = log; 96 | } 97 | 98 | public boolean isVerbose() { 99 | return verbose; 100 | } 101 | 102 | public void setVerbose(boolean verbose) { 103 | this.verbose = verbose; 104 | setLogLevelMask(); 105 | } 106 | 107 | public boolean isDebug() { 108 | return debug; 109 | } 110 | 111 | public void setDebug(boolean debug) { 112 | this.debug = debug; 113 | setLogLevelMask(); 114 | } 115 | 116 | private void setLogLevelMask() { 117 | logLevelMask = (1 << LogLevel.WARNING.ordinal()); 118 | if(verbose || debug) { 119 | logLevelMask |= (1 << LogLevel.INFO.ordinal()); 120 | } 121 | if(debug) { 122 | logLevelMask |= (1 << LogLevel.DEBUG.ordinal()); 123 | } 124 | } 125 | 126 | @Override 127 | public void log(LogLevel level, String msg, Object ... args) { 128 | if(log != null && (logLevelMask & (1 << level.ordinal())) != 0) { 129 | log.log(level, msg, args); 130 | } 131 | } 132 | 133 | @Override 134 | public void error(String msg, Exception ex) { 135 | if(log != null) { 136 | log.error(msg, ex); 137 | } 138 | } 139 | 140 | public void checkClass(File f) { 141 | try { 142 | FileInputStream fis = new FileInputStream(f); 143 | CheckInstrumentationVisitor civ = checkFileAndClose(fis, f.getPath()); 144 | 145 | if(civ != null) { 146 | recordSuspendableMethods(civ.getName(), civ.getClassEntry()); 147 | 148 | if(civ.needsInstrumentation()) { 149 | if(civ.isAlreadyInstrumented()) { 150 | log(LogLevel.INFO, "Found instrumented class: %s", f.getPath()); 151 | } else { 152 | log(LogLevel.INFO, "Found class: %s", f.getPath()); 153 | workList.add(f); 154 | } 155 | } 156 | } 157 | } catch (UnableToInstrumentException ex) { 158 | throw ex; 159 | } catch (Exception ex) { 160 | error(f.getPath(), ex); 161 | } 162 | } 163 | 164 | public boolean isMethodSuspendable(String className, String methodName, String methodDesc, boolean searchSuperClass) { 165 | if(methodName.charAt(0) == '<') { 166 | return false; // special methods are never suspendable 167 | } 168 | 169 | if(isJavaCore(className)) { 170 | return false; 171 | } 172 | 173 | String curClassName = className; 174 | do { 175 | ClassEntry entry = getClassEntry(curClassName); 176 | if(entry == null) { 177 | entry = CLASS_NOT_FOUND; 178 | 179 | if(cl != null) { 180 | log(LogLevel.INFO, "Trying to read class: %s", curClassName); 181 | 182 | CheckInstrumentationVisitor civ = checkClass(curClassName); 183 | if(civ == null) { 184 | log(LogLevel.WARNING, "Class not found assuming suspendable: %s", curClassName); 185 | } else { 186 | entry = civ.getClassEntry(); 187 | } 188 | } else { 189 | log(LogLevel.WARNING, "Can't check class - assuming suspendable: %s", curClassName); 190 | } 191 | 192 | recordSuspendableMethods(curClassName, entry); 193 | } 194 | 195 | if(entry == CLASS_NOT_FOUND) { 196 | return true; 197 | } 198 | 199 | Boolean suspendable = entry.check(methodName, methodDesc); 200 | if(suspendable != null) { 201 | return suspendable; 202 | } 203 | 204 | curClassName = entry.superName; 205 | } while(searchSuperClass && curClassName != null); 206 | 207 | log(LogLevel.WARNING, "Method not found in class - assuming suspendable: %s#%s%s", className, methodName, methodDesc); 208 | return true; 209 | } 210 | 211 | private synchronized ClassEntry getClassEntry(String className) { 212 | return classes.get(className); 213 | } 214 | 215 | void recordSuspendableMethods(String className, ClassEntry entry) { 216 | ClassEntry oldEntry; 217 | synchronized(this) { 218 | oldEntry = classes.put(className, entry); 219 | } 220 | if(oldEntry != null) { 221 | if(!oldEntry.equals(entry)) { 222 | log(LogLevel.WARNING, "Duplicate class entries with different data for class: %s", className); 223 | } 224 | } 225 | } 226 | 227 | public String getCommonSuperClass(String classA, String classB) { 228 | ArrayList listA = getSuperClasses(classA); 229 | ArrayList listB = getSuperClasses(classB); 230 | if(listA == null || listB == null) { 231 | return null; 232 | } 233 | int idx = 0; 234 | int num = Math.min(listA.size(), listB.size()); 235 | for(; idx 0) { 243 | return listA.get(idx-1); 244 | } 245 | return null; 246 | } 247 | 248 | public boolean isException(String className) { 249 | for(;;) { 250 | if("java/lang/Throwable".equals(className)) { 251 | return true; 252 | } 253 | if("java/lang/Object".equals(className)) { 254 | return false; 255 | } 256 | 257 | String superClass = getDirectSuperClass(className); 258 | if(superClass == null) { 259 | log(LogLevel.WARNING, "Can't determine super class of %s", className); 260 | return false; 261 | } 262 | className = superClass; 263 | } 264 | } 265 | 266 | public ArrayList getWorkList() { 267 | return workList; 268 | } 269 | 270 | /** 271 | *

Overwrite this function if Coroutines is used in a transformation chain.

272 | *

This method must create a new CheckInstrumentationVisitor and visit the 273 | * specified class with it.

274 | * @param className the class the needs to be analysed 275 | * @return a new CheckInstrumentationVisitor that has visited the specified 276 | * class or null if the class was not found 277 | */ 278 | protected CheckInstrumentationVisitor checkClass(String className) { 279 | InputStream is = cl.getResourceAsStream(className + ".class"); 280 | if(is != null) { 281 | return checkFileAndClose(is, className); 282 | } 283 | return null; 284 | } 285 | 286 | private CheckInstrumentationVisitor checkFileAndClose(InputStream is, String name) { 287 | try { 288 | try { 289 | ClassReader r = new ClassReader(is); 290 | 291 | CheckInstrumentationVisitor civ = new CheckInstrumentationVisitor(); 292 | r.accept(civ, ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES|ClassReader.SKIP_CODE); 293 | 294 | return civ; 295 | } finally { 296 | is.close(); 297 | } 298 | } catch (UnableToInstrumentException ex) { 299 | throw ex; 300 | } catch (Exception ex) { 301 | error(name, ex); 302 | } 303 | return null; 304 | } 305 | 306 | private String extractSuperClass(String className) { 307 | InputStream is = cl.getResourceAsStream(className + ".class"); 308 | if(is != null) { 309 | try { 310 | try { 311 | ClassReader r = new ClassReader(is); 312 | ExtractSuperClass esc = new ExtractSuperClass(); 313 | r.accept(esc, ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES); 314 | return esc.superClass; 315 | } finally { 316 | is.close(); 317 | } 318 | } catch(IOException ex) { 319 | error(className, ex); 320 | } 321 | } 322 | return null; 323 | } 324 | 325 | private ArrayList getSuperClasses(String className) { 326 | ArrayList result = new ArrayList<>(); 327 | for(;;) { 328 | result.add(0, className); 329 | if("java/lang/Object".equals(className)) { 330 | return result; 331 | } 332 | 333 | String superClass = getDirectSuperClass(className); 334 | if(superClass == null) { 335 | log(LogLevel.WARNING, "Can't determine super class of %s", className); 336 | return null; 337 | } 338 | className = superClass; 339 | } 340 | } 341 | 342 | protected String getDirectSuperClass(String className) { 343 | ClassEntry entry = getClassEntry(className); 344 | if(entry != null && entry != CLASS_NOT_FOUND) { 345 | return entry.superName; 346 | } 347 | 348 | String superClass; 349 | synchronized(this) { 350 | superClass = superClasses.get(className); 351 | } 352 | if(superClass == null) { 353 | superClass = extractSuperClass(className); 354 | if(superClass != null) { 355 | String oldSuperClass; 356 | synchronized (this) { 357 | oldSuperClass = superClasses.put(className, superClass); 358 | } 359 | if(oldSuperClass != null) { 360 | if(!oldSuperClass.equals(superClass)) { 361 | log(LogLevel.WARNING, "Duplicate super class entry with different value: %s vs %s", oldSuperClass, superClass); 362 | } 363 | } 364 | } 365 | } 366 | return superClass; 367 | } 368 | 369 | public static boolean isJavaCore(String className) { 370 | return className.startsWith("java/") || className.startsWith("javax/") || 371 | className.startsWith("sun/") || className.startsWith("com/sun/"); 372 | } 373 | 374 | private static final ClassEntry CLASS_NOT_FOUND = new ClassEntry(""); 375 | 376 | static final class ClassEntry { 377 | private final HashMap methods; 378 | final String superName; 379 | 380 | public ClassEntry(String superName) { 381 | this.superName = superName; 382 | this.methods = new HashMap<>(); 383 | } 384 | 385 | public void set(String name, String desc, boolean suspendable) { 386 | String nameAndDesc = key(name, desc); 387 | methods.put(nameAndDesc, suspendable); 388 | } 389 | 390 | public Boolean check(String name, String desc) { 391 | return methods.get(key(name, desc)); 392 | } 393 | 394 | @Override 395 | public int hashCode() { 396 | return superName.hashCode() * 67 + methods.hashCode(); 397 | } 398 | 399 | @Override 400 | public boolean equals(Object obj) { 401 | if(!(obj instanceof ClassEntry)) { 402 | return false; 403 | } 404 | final ClassEntry other = (ClassEntry)obj; 405 | return superName.equals(other.superName) && methods.equals(other.methods); 406 | } 407 | 408 | private static String key(String methodName, String methodDesc) { 409 | return methodName.concat(methodDesc); 410 | } 411 | } 412 | } 413 | -------------------------------------------------------------------------------- /src/main/java/de/matthiasmann/continuations/instrument/NewValue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2013, Matthias Mann 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Matthias Mann nor the names of its contributors may 15 | * be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 22 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 23 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 26 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | package de.matthiasmann.continuations.instrument; 31 | 32 | import org.objectweb.asm.Opcodes; 33 | import org.objectweb.asm.Type; 34 | import org.objectweb.asm.tree.AbstractInsnNode; 35 | import org.objectweb.asm.tree.TypeInsnNode; 36 | import org.objectweb.asm.tree.analysis.BasicValue; 37 | 38 | /** 39 | * 40 | * @author Matthias Mann 41 | */ 42 | public class NewValue extends BasicValue { 43 | 44 | public final boolean isDupped; 45 | public final AbstractInsnNode insn; 46 | public boolean omitted; 47 | 48 | public NewValue(Type type, boolean isDupped, AbstractInsnNode insn) { 49 | super(type); 50 | this.isDupped = isDupped; 51 | this.insn = insn; 52 | } 53 | 54 | String formatInsn() { 55 | switch (insn.getOpcode()) { 56 | case Opcodes.NEW: 57 | return "NEW " + ((TypeInsnNode)insn).desc; 58 | case Opcodes.DUP: 59 | return "DUP"; 60 | default: 61 | return "UNEXPECTED INSTRUCTION: " + insn.getOpcode(); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/de/matthiasmann/continuations/instrument/OmittedInstruction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2013, Matthias Mann 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Matthias Mann nor the names of its contributors may 15 | * be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 22 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 23 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 26 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | package de.matthiasmann.continuations.instrument; 31 | 32 | import java.util.Map; 33 | 34 | import org.objectweb.asm.MethodVisitor; 35 | import org.objectweb.asm.tree.AbstractInsnNode; 36 | 37 | /** 38 | * 39 | * @author matthias 40 | */ 41 | public class OmittedInstruction extends AbstractInsnNode { 42 | 43 | private final AbstractInsnNode orgInsn; 44 | 45 | public OmittedInstruction(AbstractInsnNode orgInsn) { 46 | super(orgInsn.getOpcode()); 47 | this.orgInsn = orgInsn; 48 | } 49 | 50 | @Override 51 | public int getType() { 52 | return orgInsn.getType(); 53 | } 54 | 55 | @Override 56 | public void accept(MethodVisitor cv) { 57 | } 58 | 59 | @Override 60 | public AbstractInsnNode clone(Map labels) { 61 | return new OmittedInstruction(orgInsn.clone(labels)); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/de/matthiasmann/continuations/instrument/TypeAnalyzer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2013, Matthias Mann 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Matthias Mann nor the names of its contributors may 15 | * be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 22 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 23 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 26 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | package de.matthiasmann.continuations.instrument; 31 | 32 | import org.objectweb.asm.Opcodes; 33 | import org.objectweb.asm.Type; 34 | import org.objectweb.asm.tree.AbstractInsnNode; 35 | import org.objectweb.asm.tree.MethodInsnNode; 36 | import org.objectweb.asm.tree.analysis.Analyzer; 37 | import org.objectweb.asm.tree.analysis.AnalyzerException; 38 | import org.objectweb.asm.tree.analysis.BasicValue; 39 | import org.objectweb.asm.tree.analysis.Frame; 40 | import org.objectweb.asm.tree.analysis.Interpreter; 41 | import org.objectweb.asm.tree.analysis.Value; 42 | 43 | /** 44 | * 45 | * @author matthias 46 | */ 47 | public class TypeAnalyzer extends Analyzer { 48 | 49 | public TypeAnalyzer(MethodDatabase db) { 50 | super(new TypeInterpreter(db)); 51 | } 52 | 53 | @Override 54 | protected Frame newFrame(int nLocals, int nStack) { 55 | return new TypeFrame(nLocals, nStack); 56 | } 57 | 58 | @Override 59 | protected Frame newFrame(Frame src) { 60 | return new TypeFrame(src); 61 | } 62 | 63 | /** 64 | * Computes the number of arguments 65 | * Returns the same result as {@code Type.getArgumentTypes(desc).length } 66 | * just with no memory allocations 67 | */ 68 | static int getNumArguments(String methodDesc) { 69 | int off = 1; 70 | int size = 0; 71 | for(;;) { 72 | char car = methodDesc.charAt(off++); 73 | if (car == ')') { 74 | return size; 75 | } 76 | if (car != '[') { 77 | ++size; 78 | if (car == 'L') { 79 | off = methodDesc.indexOf(';', off) + 1; 80 | } 81 | } 82 | } 83 | } 84 | 85 | static class TypeFrame extends Frame { 86 | TypeFrame(int nLocals, int nStack) { 87 | super(nLocals, nStack); 88 | } 89 | 90 | TypeFrame(Frame src) { 91 | super(src); 92 | } 93 | 94 | @Override 95 | public void execute(AbstractInsnNode insn, Interpreter interpreter) throws AnalyzerException { 96 | switch (insn.getOpcode()) { 97 | case Opcodes.INVOKEVIRTUAL: 98 | case Opcodes.INVOKESPECIAL: 99 | case Opcodes.INVOKESTATIC: 100 | case Opcodes.INVOKEINTERFACE: { 101 | String desc = ((MethodInsnNode)insn).desc; 102 | for(int i=getNumArguments(desc) ; i>0 ; --i) { 103 | pop(); 104 | } 105 | if (insn.getOpcode() != Opcodes.INVOKESTATIC) { 106 | pop(); 107 | if(insn.getOpcode() == Opcodes.INVOKESPECIAL && getStackSize() > 0) { 108 | if("".equals(((MethodInsnNode)insn).name)) { 109 | Value value = pop(); 110 | if(value instanceof NewValue) { 111 | value = new BasicValue(((NewValue)value).getType()); 112 | } 113 | push(value); 114 | } 115 | } 116 | } 117 | Type returnType = Type.getReturnType(desc); 118 | if (returnType != Type.VOID_TYPE) { 119 | push(interpreter.newValue(returnType)); 120 | } 121 | break; 122 | } 123 | default: 124 | super.execute(insn, interpreter); 125 | } 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/de/matthiasmann/continuations/instrument/TypeInterpreter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2013, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations.instrument; 30 | 31 | import org.objectweb.asm.Opcodes; 32 | import org.objectweb.asm.Type; 33 | import org.objectweb.asm.tree.AbstractInsnNode; 34 | import org.objectweb.asm.tree.TypeInsnNode; 35 | import org.objectweb.asm.tree.analysis.AnalyzerException; 36 | import org.objectweb.asm.tree.analysis.BasicInterpreter; 37 | import org.objectweb.asm.tree.analysis.BasicValue; 38 | 39 | /** 40 | * An extension to {@link BasicInterpreter} which collects the type of 41 | * objects and arrays. 42 | * 43 | * @author Matthias Mann 44 | */ 45 | public class TypeInterpreter extends BasicInterpreter { 46 | 47 | private final MethodDatabase db; 48 | 49 | public TypeInterpreter(MethodDatabase db) { 50 | this.db = db; 51 | } 52 | 53 | @Override 54 | public BasicValue newValue(Type type) { 55 | if(type == null) { 56 | return BasicValue.UNINITIALIZED_VALUE; 57 | } 58 | if(type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { 59 | return new BasicValue(type); 60 | } 61 | return super.newValue(type); 62 | } 63 | 64 | @Override 65 | public BasicValue newOperation(AbstractInsnNode insn) throws AnalyzerException { 66 | if(insn.getOpcode() == Opcodes.NEW) { 67 | return new NewValue(Type.getObjectType(((TypeInsnNode) insn).desc), false, insn); 68 | } 69 | return super.newOperation(insn); 70 | } 71 | 72 | @Override 73 | public BasicValue copyOperation(AbstractInsnNode insn, BasicValue value) throws AnalyzerException { 74 | if(insn.getOpcode() == Opcodes.DUP) { 75 | if(value instanceof NewValue) { 76 | NewValue newValue = (NewValue)value; 77 | if(!newValue.isDupped) { 78 | return new NewValue(newValue.getType(), true, insn); 79 | } 80 | } 81 | } 82 | return super.copyOperation(insn, value); 83 | } 84 | 85 | @Override 86 | public BasicValue binaryOperation(AbstractInsnNode insn, BasicValue value1, BasicValue value2) throws AnalyzerException { 87 | if(insn.getOpcode() == Opcodes.AALOAD) { 88 | Type t1 = value1.getType(); 89 | if(t1 == null || t1.getSort() != Type.ARRAY) { 90 | throw new AnalyzerException(insn, "AALOAD needs an array as first parameter"); 91 | } 92 | 93 | Type resultType = Type.getType(t1.getDescriptor().substring(1)); 94 | return new BasicValue(resultType); 95 | } 96 | return super.binaryOperation(insn, value1, value2); 97 | } 98 | 99 | @Override 100 | public BasicValue merge(BasicValue v, BasicValue w) { 101 | if (!v.equals(w)) { 102 | if(v.isReference() && w.isReference()) { 103 | int dimensions = 0; 104 | Type typeV = v.getType(); 105 | Type typeW = w.getType(); 106 | if(typeV.getSort() != typeW.getSort()) { 107 | db.log(LogLevel.DEBUG, "Array and none array type can't be merged: %s %s", v, w); 108 | return BasicValue.UNINITIALIZED_VALUE; 109 | } 110 | if(typeW.getSort() == Type.ARRAY) { 111 | dimensions = typeV.getDimensions(); 112 | if(dimensions != typeW.getDimensions()) { 113 | db.log(LogLevel.DEBUG, "Arrays with different dimensions can't be merged: %s %s", v, w); 114 | return BasicValue.UNINITIALIZED_VALUE; 115 | } 116 | typeV = typeV.getElementType(); 117 | typeW = typeW.getElementType(); 118 | if(typeV.getSort() != Type.OBJECT || typeW.getSort() != Type.OBJECT) { 119 | db.log(LogLevel.DEBUG, "Arrays of different primitive type can't be merged: %s %s", v, w); 120 | return BasicValue.UNINITIALIZED_VALUE; 121 | } 122 | } 123 | String internalV = typeV.getInternalName(); 124 | String internalW = typeW.getInternalName(); 125 | if("null".equals(internalV)) { 126 | return w; 127 | } 128 | if("null".equals(internalW)) { 129 | return v; 130 | } 131 | String superClass = db.getCommonSuperClass(internalV, internalW); 132 | if(superClass == null) { 133 | if(db.isException(internalW)) { 134 | db.log(LogLevel.WARNING, "Could not determine super class for v=%s w=%s - decided to use exception %s", v, w, w); 135 | return w; 136 | } 137 | if(db.isException(internalV)) { 138 | db.log(LogLevel.WARNING, "Could not determine super class for v=%s w=%s - decided to use exception %s", v, w, v); 139 | return v; 140 | } 141 | db.log(LogLevel.WARNING, "Could not determine super class for v=%s w=%s - decided to use java/lang/Object", v, w); 142 | superClass = "java/lang/Object"; 143 | } 144 | String typeDescriptor = makeTypeDescriptor(superClass, dimensions); 145 | db.log(LogLevel.INFO, "Common super class for v=%s w=%s is %s", v, w, typeDescriptor); 146 | return new BasicValue(Type.getType(typeDescriptor)); 147 | } 148 | return BasicValue.UNINITIALIZED_VALUE; 149 | } 150 | return v; 151 | } 152 | 153 | private static String makeTypeDescriptor(String className, int dimensions) { 154 | int len = className.length(); 155 | char[] tmp = new char[len + 2 + dimensions]; 156 | for(int i=0 ; iThis exception is thrown when an unsupported construct was found in a class 33 | * that must be instrumented for suspension.

34 | * 35 | *

Note: this needs to be a RuntimeException - otherwise it can't be thrown 36 | * from {@link CheckInstrumentationVisitor}.

37 | * 38 | * @author Matthias Mann 39 | */ 40 | public class UnableToInstrumentException extends RuntimeException { 41 | 42 | private final String reason; 43 | private final String className; 44 | private final String methodName; 45 | private final String methodDesc; 46 | 47 | public UnableToInstrumentException(String reason, String className, String methodName, String methodDesc) { 48 | super(String.format("Unable to instrument class %s#%s%s because of %s", className, methodName, methodDesc, reason)); 49 | this.reason = reason; 50 | this.className = className; 51 | this.methodName = methodName; 52 | this.methodDesc = methodDesc; 53 | } 54 | 55 | public String getClassName() { 56 | return className; 57 | } 58 | 59 | public String getMethodName() { 60 | return methodName; 61 | } 62 | 63 | public String getMethodDesc() { 64 | return methodDesc; 65 | } 66 | 67 | public String getReason() { 68 | return reason; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/de/matthiasmann/continuations/instrument/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | *

Coroutine library instrumentation ANT task

3 | *
 4 |  * Copyright (c) 2008, Matthias Mann
 5 |  * All rights reserved.
 6 |  *
 7 |  * Redistribution and use in source and binary forms, with or without
 8 |  * modification, are permitted provided that the following conditions are met:
 9 |  *
10 |  *     * Redistributions of source code must retain the above copyright notice,
11 |  *       this list of conditions and the following disclaimer.
12 |  *     * Redistributions in binary form must reproduce the above copyright
13 |  *       notice, this list of conditions and the following disclaimer in the
14 |  *       documentation and/or other materials provided with the distribution.
15 |  *     * Neither the name of Matthias Mann nor the names of its
16 |  *       contributors may be used to endorse or promote products derived from
17 |  *       this software without specific prior written permission.
18 |  *
19 |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 |  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 |  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 |  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 |  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 |  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 |  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 |  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 |  * POSSIBILITY OF SUCH DAMAGE.
30 |  * 
31 | * 32 | * @see de.matthiasmann.continuations.instrument.InstrumentationTask 33 | */ 34 | package de.matthiasmann.continuations.instrument; 35 | -------------------------------------------------------------------------------- /src/main/java/de/matthiasmann/continuations/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | *

Continuations Library for Coroutine support in Java.

3 | *
 4 |  * Copyright (c) 2008, Matthias Mann
 5 |  * All rights reserved.
 6 |  *
 7 |  * Redistribution and use in source and binary forms, with or without
 8 |  * modification, are permitted provided that the following conditions are met:
 9 |  *
10 |  *     * Redistributions of source code must retain the above copyright notice,
11 |  *       this list of conditions and the following disclaimer.
12 |  *     * Redistributions in binary form must reproduce the above copyright
13 |  *       notice, this list of conditions and the following disclaimer in the
14 |  *       documentation and/or other materials provided with the distribution.
15 |  *     * Neither the name of Matthias Mann nor the names of its
16 |  *       contributors may be used to endorse or promote products derived from
17 |  *       this software without specific prior written permission.
18 |  *
19 |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 |  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 |  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 |  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 |  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 |  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 |  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 |  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 |  * POSSIBILITY OF SUCH DAMAGE.
30 |  * 
31 | * 32 | *

This package contains the runtime of the Continuations library.

33 | * 34 | * @see de.matthiasmann.continuations.Coroutine 35 | * @author Matthias Mann 36 | */ 37 | package de.matthiasmann.continuations; 38 | -------------------------------------------------------------------------------- /src/test/java/de/matthiasmann/continuations/ArrayIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2013, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations; 30 | 31 | import static org.junit.Assert.assertEquals; 32 | 33 | /** 34 | * 35 | * @author Matthias Mann 36 | */ 37 | public class ArrayIT implements CoroutineProto { 38 | 39 | private static final PatchLevel l1 = new PatchLevel(); 40 | private static final PatchLevel[] l2 = new PatchLevel[] { l1 }; 41 | private static final PatchLevel[][] l3 = new PatchLevel[][] { l2 }; 42 | 43 | public void testArray() { 44 | Coroutine co = new Coroutine(this); 45 | co.run(); 46 | assertEquals(42, l1.i); 47 | } 48 | 49 | @Override 50 | public void coExecute() throws SuspendExecution { 51 | PatchLevel[][] local_patch_levels = l3; 52 | PatchLevel patch_level = local_patch_levels[0][0]; 53 | patch_level.setLevel(42); 54 | } 55 | 56 | public static class PatchLevel { 57 | int i; 58 | 59 | public void setLevel(int value) throws SuspendExecution { 60 | i = value; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/test/java/de/matthiasmann/continuations/BlockingIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2013, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations; 30 | 31 | import static org.junit.Assert.assertTrue; 32 | 33 | import java.io.IOException; 34 | import java.io.InputStream; 35 | import java.util.HashSet; 36 | import java.util.Locale; 37 | import java.util.concurrent.locks.Lock; 38 | 39 | import org.junit.Test; 40 | import org.objectweb.asm.ClassReader; 41 | import org.objectweb.asm.ClassWriter; 42 | 43 | import de.matthiasmann.continuations.instrument.AlreadyInstrumented; 44 | import de.matthiasmann.continuations.instrument.InstrumentClass; 45 | import de.matthiasmann.continuations.instrument.Log; 46 | import de.matthiasmann.continuations.instrument.LogLevel; 47 | import de.matthiasmann.continuations.instrument.MethodDatabase; 48 | 49 | /** 50 | * Test to checking blocking call detection 51 | * 52 | * @author Matthias Mann 53 | */ 54 | @AlreadyInstrumented 55 | public class BlockingIT { 56 | 57 | @Test 58 | public void testSuspend() throws IOException { 59 | final String className = BlockingIT.class.getName().replace('.', '/'); 60 | final HashSet msgs = new HashSet<>(); 61 | msgs.add("Method "+className+"#t_wait()V contains potentially blocking call to java/lang/Object#wait()V"); 62 | msgs.add("Method "+className+"#t_sleep1()V contains potentially blocking call to java/lang/Thread#sleep(J)V"); 63 | msgs.add("Method "+className+"#t_sleep2()V contains potentially blocking call to java/lang/Thread#sleep(JI)V"); 64 | msgs.add("Method "+className+"#t_join1(Ljava/lang/Thread;)V contains potentially blocking call to java/lang/Thread#join()V"); 65 | msgs.add("Method "+className+"#t_join2(Ljava/lang/Thread;)V contains potentially blocking call to java/lang/Thread#join(J)V"); 66 | msgs.add("Method "+className+"#t_join3(Ljava/lang/Thread;)V contains potentially blocking call to java/lang/Thread#join(JI)V"); 67 | msgs.add("Method "+className+"#t_lock1(Ljava/util/concurrent/locks/Lock;)V contains potentially blocking call to java/util/concurrent/locks/Lock#lock()V"); 68 | msgs.add("Method "+className+"#t_lock2(Ljava/util/concurrent/locks/Lock;)V contains potentially blocking call to java/util/concurrent/locks/Lock#lockInterruptibly()V"); 69 | 70 | MethodDatabase db = new MethodDatabase(BlockingIT.class.getClassLoader()); 71 | db.setAllowBlocking(true); 72 | db.setLog(new Log() { 73 | public void log(LogLevel level, String msg, Object... args) { 74 | if(level == LogLevel.WARNING) { 75 | msg = String.format(Locale.ENGLISH, msg, args); 76 | assertTrue("Unexpected message: " + msg, msgs.remove(msg)); 77 | } 78 | } 79 | public void error(String msg, Exception ex) { 80 | throw new AssertionError(msg, ex); 81 | } 82 | }); 83 | 84 | InputStream in = BlockingIT.class.getResourceAsStream("BlockingIT.class"); 85 | try { 86 | ClassReader r = new ClassReader(in); 87 | ClassWriter cw = new ClassWriter(0); 88 | InstrumentClass ic = new InstrumentClass(cw, db, true); 89 | r.accept(ic, ClassReader.SKIP_FRAMES); 90 | } finally { 91 | in.close(); 92 | } 93 | 94 | assertTrue("Expected messages not generated: "+msgs.toString(), msgs.isEmpty()); 95 | } 96 | 97 | public void t_wait() throws SuspendExecution, InterruptedException { 98 | synchronized (this) { 99 | wait(); 100 | } 101 | } 102 | 103 | public void t_notify() throws SuspendExecution { 104 | synchronized (this) { 105 | notify(); 106 | } 107 | } 108 | 109 | public void t_sleep1() throws SuspendExecution, InterruptedException { 110 | Thread.sleep(1000); 111 | } 112 | 113 | public void t_sleep2() throws SuspendExecution, InterruptedException { 114 | Thread.sleep(1000, 100); 115 | } 116 | 117 | public void t_join1(Thread t) throws SuspendExecution, InterruptedException { 118 | t.join(); 119 | } 120 | 121 | public void t_join2(Thread t) throws SuspendExecution, InterruptedException { 122 | t.join(1000); 123 | } 124 | 125 | public void t_join3(Thread t) throws SuspendExecution, InterruptedException { 126 | t.join(1, 100); 127 | } 128 | 129 | public void t_lock1(Lock lock) throws SuspendExecution { 130 | lock.lock(); 131 | } 132 | 133 | public void t_lock2(Lock lock) throws SuspendExecution, InterruptedException { 134 | lock.lockInterruptibly(); 135 | } 136 | 137 | public void t_lock3() throws SuspendExecution { 138 | lock(); 139 | } 140 | 141 | public void lock() { 142 | System.out.println("Just a method which have similar signature"); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/test/java/de/matthiasmann/continuations/CatchIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations; 30 | 31 | import static org.junit.Assert.assertEquals; 32 | 33 | import java.util.ArrayList; 34 | import java.util.Iterator; 35 | 36 | import org.junit.Test; 37 | 38 | /** 39 | * Check that a generic catch all does not affect the suspendtion of a method 40 | * 41 | * @author Matthias Mann 42 | */ 43 | public class CatchIT implements CoroutineProto { 44 | 45 | private ArrayList results = new ArrayList<>(); 46 | 47 | int cnt = 0; 48 | private void throwOnSecondCall() throws SuspendExecution { 49 | results.add("cnt=" + cnt); 50 | Coroutine.yield(); 51 | if(++cnt >= 2) { 52 | throw new IllegalStateException("called second time"); 53 | } 54 | results.add("not thrown"); 55 | } 56 | 57 | @Override 58 | public void coExecute() throws SuspendExecution { 59 | results.add("A"); 60 | Coroutine.yield(); 61 | try { 62 | results.add("C"); 63 | Coroutine.yield(); 64 | throwOnSecondCall(); 65 | Coroutine.yield(); 66 | throwOnSecondCall(); 67 | results.add("never reached"); 68 | } catch(Throwable ex) { 69 | results.add(ex.getMessage()); 70 | } 71 | results.add("H"); 72 | } 73 | 74 | @Test 75 | public void testCatch() { 76 | results.clear(); 77 | 78 | try { 79 | Coroutine co = new Coroutine(this); 80 | co.run(); 81 | results.add("B"); 82 | co.run(); 83 | results.add("D"); 84 | co.run(); 85 | results.add("E"); 86 | co.run(); 87 | results.add("F"); 88 | co.run(); 89 | results.add("G"); 90 | co.run(); 91 | results.add("I"); 92 | } finally { 93 | System.out.println(results); 94 | } 95 | 96 | assertEquals(13, results.size()); 97 | Iterator iter = results.iterator(); 98 | assertEquals("A", iter.next()); 99 | assertEquals("B", iter.next()); 100 | assertEquals("C", iter.next()); 101 | assertEquals("D", iter.next()); 102 | assertEquals("cnt=0", iter.next()); 103 | assertEquals("E", iter.next()); 104 | assertEquals("not thrown", iter.next()); 105 | assertEquals("F", iter.next()); 106 | assertEquals("cnt=1", iter.next()); 107 | assertEquals("G", iter.next()); 108 | assertEquals("called second time", iter.next()); 109 | assertEquals("H", iter.next()); 110 | assertEquals("I", iter.next()); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/test/java/de/matthiasmann/continuations/CoIteratorIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations; 30 | 31 | import static org.junit.Assert.assertEquals; 32 | import static org.junit.Assert.assertFalse; 33 | 34 | import java.util.Iterator; 35 | 36 | import org.junit.Test; 37 | 38 | /** 39 | * 40 | * Test the {@link CoIterato} class 41 | * 42 | * @author Matthias Mann 43 | */ 44 | public class CoIteratorIT { 45 | 46 | @Test 47 | public void testCoIterator() { 48 | Iterator iter = new CoIterator() { 49 | @Override 50 | public void run() throws SuspendExecution { 51 | for(int j=0 ; j<3 ; j++) { 52 | produce("Hugo " + j); 53 | produce("Test"); 54 | for(int i=1 ; i<10 ; i++) { 55 | produce("Number " + i); 56 | } 57 | produce("Nix"); 58 | } 59 | } 60 | }; 61 | 62 | for(int j=0 ; j<3 ; j++) { 63 | assertEquals("Hugo " + j, iter.next()); 64 | assertEquals("Test", iter.next()); 65 | for(int i=1 ; i<10 ; i++) { 66 | assertEquals("Number " + i, iter.next()); 67 | } 68 | assertEquals("Nix", iter.next()); 69 | } 70 | 71 | assertFalse(iter.hasNext()); 72 | } 73 | 74 | } -------------------------------------------------------------------------------- /src/test/java/de/matthiasmann/continuations/DoubleIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2013, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations; 30 | 31 | import static org.junit.Assert.assertEquals; 32 | 33 | import org.junit.Test; 34 | 35 | /** 36 | * 37 | * @author Matthias Mann 38 | */ 39 | public class DoubleIT implements CoroutineProto { 40 | 41 | double result; 42 | 43 | @Test 44 | public void testDouble() { 45 | Coroutine co = new Coroutine(this); 46 | co.run(); 47 | assertEquals(0, result, 1e-8); 48 | co.run(); 49 | assertEquals(1, result, 1e-8); 50 | assertEquals(Coroutine.State.FINISHED, co.getState()); 51 | } 52 | 53 | @Override 54 | public void coExecute() throws SuspendExecution { 55 | double temp = Math.cos(0); 56 | Coroutine.yield(); 57 | this.result = temp; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/test/java/de/matthiasmann/continuations/FinallyIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations; 30 | 31 | import static org.junit.Assert.assertEquals; 32 | 33 | import java.util.ArrayList; 34 | 35 | import org.junit.Test; 36 | 37 | /** 38 | * Test correct execution of a finally statement 39 | * 40 | * @author Matthias Mann 41 | */ 42 | public class FinallyIT implements CoroutineProto { 43 | 44 | private ArrayList results = new ArrayList<>(); 45 | 46 | public void coExecute() throws SuspendExecution { 47 | results.add("A"); 48 | Coroutine.yield(); 49 | try { 50 | results.add("C"); 51 | Coroutine.yield(); 52 | results.add("E"); 53 | } finally { 54 | results.add("F"); 55 | } 56 | results.add("G"); 57 | Coroutine.yield(); 58 | results.add("I"); 59 | } 60 | 61 | @Test 62 | public void testFinally() { 63 | results.clear(); 64 | 65 | try { 66 | Coroutine co = new Coroutine(this); 67 | co.run(); 68 | results.add("B"); 69 | co.run(); 70 | results.add("D"); 71 | co.run(); 72 | results.add("H"); 73 | co.run(); 74 | } finally { 75 | System.out.println(results); 76 | } 77 | 78 | assertEquals(9, results.size()); 79 | assertEquals("A", results.get(0)); 80 | assertEquals("B", results.get(1)); 81 | assertEquals("C", results.get(2)); 82 | assertEquals("D", results.get(3)); 83 | assertEquals("E", results.get(4)); 84 | assertEquals("F", results.get(5)); 85 | assertEquals("G", results.get(6)); 86 | assertEquals("H", results.get(7)); 87 | assertEquals("I", results.get(8)); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/test/java/de/matthiasmann/continuations/InheritIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2013, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations; 30 | 31 | import static org.junit.Assert.assertEquals; 32 | 33 | import java.util.ArrayList; 34 | 35 | import org.junit.Test; 36 | 37 | /** 38 | * 39 | * @author mam 40 | */ 41 | public class InheritIT { 42 | 43 | @Test 44 | public void testInherit() { 45 | final C dut = new C(); 46 | Coroutine c = new Coroutine(new CoroutineProto() { 47 | public void coExecute() throws SuspendExecution { 48 | dut.myMethod(); 49 | } 50 | }); 51 | for(int i=0 ; i<3 ; i++) { 52 | c.run(); 53 | } 54 | 55 | assertEquals(5, dut.result.size()); 56 | assertEquals("a", dut.result.get(0)); 57 | assertEquals("o1", dut.result.get(1)); 58 | assertEquals("o2", dut.result.get(2)); 59 | assertEquals("b", dut.result.get(3)); 60 | assertEquals("b", dut.result.get(4)); 61 | } 62 | 63 | public static class A { 64 | public static void yield() throws SuspendExecution { 65 | Coroutine.yield(); 66 | } 67 | } 68 | 69 | public static class B extends A { 70 | final ArrayList result = new ArrayList<>(); 71 | } 72 | 73 | public static class C extends B { 74 | 75 | public void otherMethod() throws SuspendExecution { 76 | result.add("o1"); 77 | Coroutine.yield(); 78 | result.add("o2"); 79 | } 80 | 81 | public void myMethod() throws SuspendExecution { 82 | result.add("a"); 83 | otherMethod(); 84 | 85 | for(;;) { 86 | result.add("b"); 87 | if(result.size() > 10) { 88 | otherMethod(); 89 | result.add("Ohh!"); 90 | } 91 | yield(); 92 | } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/test/java/de/matthiasmann/continuations/InitialSizeIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2013, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations; 30 | 31 | import static org.junit.Assert.assertEquals; 32 | import static org.junit.Assert.assertTrue; 33 | 34 | import java.lang.reflect.Field; 35 | 36 | import org.junit.Test; 37 | 38 | /** 39 | * 40 | * @author Matthias Mann 41 | */ 42 | public class InitialSizeIT implements CoroutineProto { 43 | 44 | @Test 45 | public void test1() { 46 | testWithSize(1); 47 | } 48 | 49 | @Test 50 | public void test2() { 51 | testWithSize(2); 52 | } 53 | 54 | @Test 55 | public void test3() { 56 | testWithSize(3); 57 | } 58 | 59 | private void testWithSize(int stackSize) { 60 | Coroutine c = new Coroutine(this, stackSize); 61 | assertEquals(getStackSize(c), stackSize); 62 | c.run(); 63 | assertEquals(Coroutine.State.SUSPENDED, c.getState()); 64 | c.run(); 65 | assertEquals(Coroutine.State.FINISHED, c.getState()); 66 | assertTrue(getStackSize(c) > 10); 67 | } 68 | 69 | @Override 70 | public void coExecute() throws SuspendExecution { 71 | assertEquals(3628800, factorial(10)); 72 | } 73 | 74 | private int factorial(Integer a) throws SuspendExecution { 75 | if(a == 0) { 76 | Coroutine.yield(); 77 | return 1; 78 | } 79 | return a * factorial(a - 1); 80 | } 81 | 82 | private int getStackSize(Coroutine c) { 83 | try { 84 | Field stackField = Coroutine.class.getDeclaredField("stack"); 85 | stackField.setAccessible(true); 86 | Object stack = stackField.get(c); 87 | Field dataObjectField = Stack.class.getDeclaredField("dataObject"); 88 | dataObjectField.setAccessible(true); 89 | Object[] dataObject = (Object[])dataObjectField.get(stack); 90 | return dataObject.length; 91 | } catch(Throwable ex) { 92 | throw new AssertionError(ex); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/test/java/de/matthiasmann/continuations/InterfaceIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2013, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations; 30 | 31 | import org.junit.Test; 32 | 33 | /** 34 | * 35 | * @author Elias Naur 36 | */ 37 | public class InterfaceIT { 38 | 39 | public class C2 implements SomeInterface { 40 | public void doStuff() throws SuspendExecution { 41 | } 42 | } 43 | 44 | public class C implements SomeInterface { 45 | public void doStuff() throws SuspendExecution { 46 | /* float time = 0f; 47 | float seconds = .8f; 48 | do { 49 | float t = .06667f; 50 | System.out.println("time = " + time + " " + (time + t)); 51 | // time = StrictMath.min(time + t, seconds); 52 | time = time + t; 53 | System.out.println("seconds = " + seconds + " | time = " + time + " | t = " + t); 54 | System.out.println("this = " + this); 55 | 56 | System.out.println("time just not after = " + time); 57 | Coroutine.yield(); 58 | System.out.println("time after = " + time); 59 | } while (time < seconds); 60 | System.out.println("1 = " + 1);*/ 61 | } 62 | } 63 | 64 | @Test 65 | public void testSuspend() { 66 | // final I i = new C(); 67 | Coroutine co = new Coroutine(new CoroutineProto() { 68 | public final void coExecute() throws SuspendExecution { 69 | // next line causes an error because of incomplete merge in TypeInterpreter 70 | //SomeInterface i = System.currentTimeMillis() > 0 ? new C() : new C2(); 71 | SomeInterface i = new C(); 72 | System.out.println("i = " + i); 73 | i.doStuff(); 74 | } 75 | }); 76 | while(co.getState() != Coroutine.State.FINISHED) { 77 | System.out.println("State="+co.getState()); 78 | co.run(); 79 | } 80 | System.out.println("State="+co.getState()); 81 | } 82 | 83 | } -------------------------------------------------------------------------------- /src/test/java/de/matthiasmann/continuations/Merge2IT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2013, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations; 30 | 31 | import static org.junit.Assert.assertTrue; 32 | 33 | import org.junit.Test; 34 | 35 | /** 36 | * 37 | * @author mam 38 | */ 39 | public class Merge2IT implements CoroutineProto { 40 | 41 | public interface Interface { 42 | public void method(); 43 | } 44 | 45 | public static Interface getInterface() { 46 | return null; 47 | } 48 | 49 | public static void suspendable() throws SuspendExecution { 50 | } 51 | 52 | public void coExecute() throws SuspendExecution { 53 | try { 54 | Interface iface = getInterface(); 55 | iface.method(); 56 | } catch(IllegalStateException ise) { 57 | suspendable(); 58 | } 59 | } 60 | 61 | @Test 62 | public void testMerge2() { 63 | try { 64 | Coroutine c = new Coroutine(new Merge2IT()); 65 | c.run(); 66 | assertTrue("Should not reach here", false); 67 | } catch (NullPointerException ex) { 68 | // NPE expected 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/test/java/de/matthiasmann/continuations/Merge3IT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2013, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations; 30 | 31 | import org.junit.Test; 32 | 33 | 34 | /** 35 | * 36 | * @author Matthias Mann 37 | */ 38 | public class Merge3IT implements CoroutineProto { 39 | 40 | public boolean a; 41 | public boolean b; 42 | 43 | public void coExecute() throws SuspendExecution { 44 | if(a) { 45 | Object[] arr = new Object[2]; 46 | System.out.println(arr); 47 | } else { 48 | float[] arr = new float[3]; 49 | System.out.println(arr); 50 | } 51 | blub(); 52 | System.out.println(); 53 | } 54 | 55 | private void blub() throws SuspendExecution { 56 | } 57 | 58 | @Test 59 | public void testMerge3() { 60 | Coroutine c = new Coroutine(new Merge3IT()); 61 | c.run(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/test/java/de/matthiasmann/continuations/MergeIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2013, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations; 30 | 31 | import java.io.FileNotFoundException; 32 | import java.io.IOException; 33 | 34 | import org.junit.Test; 35 | 36 | /** 37 | * 38 | * @author mam 39 | */ 40 | public class MergeIT implements CoroutineProto { 41 | 42 | public static void throwsIO() throws IOException { 43 | } 44 | 45 | public void coExecute() throws SuspendExecution { 46 | try { 47 | throwsIO(); 48 | } catch(FileNotFoundException e) { 49 | e.printStackTrace(); 50 | } catch(IOException e) { 51 | e.printStackTrace(); 52 | } 53 | } 54 | 55 | @Test 56 | public void testMerge() { 57 | Coroutine c = new Coroutine(new MergeIT()); 58 | c.run(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/test/java/de/matthiasmann/continuations/NullIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2013, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations; 30 | 31 | import static org.junit.Assert.assertEquals; 32 | 33 | import org.junit.Test; 34 | 35 | /** 36 | * 37 | * @author Matthias Mann 38 | */ 39 | public class NullIT implements CoroutineProto { 40 | 41 | Object result = "b"; 42 | 43 | @Test 44 | public void testNull() { 45 | int count = 0; 46 | Coroutine co = new Coroutine(this); 47 | while(co.getState() != Coroutine.State.FINISHED) { 48 | ++count; 49 | co.run(); 50 | } 51 | assertEquals(2, count); 52 | assertEquals("a", result); 53 | } 54 | 55 | @Override 56 | public void coExecute() throws SuspendExecution { 57 | result = getProperty(); 58 | } 59 | 60 | private Object getProperty() throws SuspendExecution { 61 | Object x = null; 62 | 63 | Object y = getProperty("a"); 64 | if(y != null) { 65 | x = y; 66 | } 67 | 68 | return x; 69 | } 70 | 71 | private Object getProperty(String string) throws SuspendExecution { 72 | Coroutine.yield(); 73 | return string; 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/test/java/de/matthiasmann/continuations/SerializeIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations; 30 | 31 | import static org.junit.Assert.assertEquals; 32 | import static org.junit.Assert.assertFalse; 33 | import static org.junit.Assert.assertNotSame; 34 | 35 | import java.io.ByteArrayInputStream; 36 | import java.io.ByteArrayOutputStream; 37 | import java.io.IOException; 38 | import java.io.ObjectInputStream; 39 | import java.io.ObjectOutputStream; 40 | import java.util.Iterator; 41 | 42 | import org.junit.Test; 43 | 44 | /** 45 | * 46 | * @author Matthias Mann 47 | */ 48 | public class SerializeIT { 49 | 50 | @Test 51 | public void testSerialize() throws IOException, ClassNotFoundException, SuspendExecution { 52 | Iterator iter1 = new TestIterator(); 53 | 54 | assertEquals("A", iter1.next()); 55 | assertEquals("B", iter1.next()); 56 | assertEquals("C0", iter1.next()); 57 | assertEquals("C1", iter1.next()); 58 | 59 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 60 | ObjectOutputStream oos = new ObjectOutputStream(baos); 61 | oos.writeObject(iter1); 62 | oos.close(); 63 | 64 | byte[] bytes = baos.toByteArray(); 65 | 66 | ByteArrayInputStream bais = new ByteArrayInputStream(bytes); 67 | ObjectInputStream ois = new ObjectInputStream(bais); 68 | @SuppressWarnings("unchecked") 69 | Iterator iter2 = (Iterator)ois.readObject(); 70 | 71 | assertNotSame(iter1, iter2); 72 | 73 | assertEquals("C2", iter2.next()); 74 | assertEquals("C3", iter2.next()); 75 | assertEquals("D", iter2.next()); 76 | assertEquals("E", iter2.next()); 77 | assertFalse(iter2.hasNext()); 78 | 79 | assertEquals("C2", iter1.next()); 80 | assertEquals("C3", iter1.next()); 81 | assertEquals("D", iter1.next()); 82 | assertEquals("E", iter1.next()); 83 | assertFalse(iter1.hasNext()); 84 | } 85 | 86 | private static class TestIterator extends CoIterator { 87 | @Override 88 | protected void run() throws SuspendExecution { 89 | produce("A"); 90 | produce("B"); 91 | for(int i = 0; i < 4; i++) { 92 | produce("C" + i); 93 | } 94 | produce("D"); 95 | produce("E"); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/test/java/de/matthiasmann/continuations/SomeInterface.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2013, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations; 30 | 31 | /** 32 | * A dummy interface used for the InterfaceIT 33 | * 34 | * @author Elias Naur 35 | */ 36 | public interface SomeInterface { 37 | 38 | void doStuff() throws SuspendExecution; 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/de/matthiasmann/continuations/SuspendConstructorArgumentIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2013, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations; 30 | 31 | import static org.junit.Assert.assertEquals; 32 | import static org.junit.Assert.assertFalse; 33 | 34 | import java.io.IOException; 35 | import java.util.Iterator; 36 | 37 | import org.junit.Test; 38 | 39 | /** 40 | * Test to checking suspendable method calls as constructor parameters 41 | * 42 | * @author Matthias Mann 43 | */ 44 | public class SuspendConstructorArgumentIT { 45 | 46 | @Test 47 | public void testCalls() throws IOException, SuspendExecution { 48 | Iterator iter = new CoIterator() { 49 | @Override 50 | public void run() throws SuspendExecution { 51 | m1(); 52 | m2(); 53 | m3(); 54 | m4(); 55 | } 56 | 57 | private void m1() throws SuspendExecution { 58 | // this is the pattern generated by Eclipse's compiler for: str() + " Bla" 59 | produce(new StringBuilder(str()).append(" Bla").toString()); 60 | } 61 | 62 | private void m2() throws SuspendExecution { 63 | produce(new String(buf(), offset(), len())); 64 | } 65 | 66 | private void m3() throws SuspendExecution { 67 | produce(new Long(l()).toString()); 68 | } 69 | 70 | private void m4() throws SuspendExecution { 71 | produce(new StringBuilder(new String(buf(), offset(), len())).append(str()).toString()); 72 | } 73 | 74 | private String str() throws SuspendExecution { 75 | produce("str()"); 76 | return "Test"; 77 | } 78 | 79 | private char[] buf() throws SuspendExecution { 80 | produce("buf()"); 81 | return "Hugo".toCharArray(); 82 | } 83 | 84 | private int offset() throws SuspendExecution { 85 | produce("offset()"); 86 | return 1; 87 | } 88 | 89 | private int len() throws SuspendExecution { 90 | produce("len()"); 91 | return 3; 92 | } 93 | 94 | private long l() throws SuspendExecution { 95 | produce("l()"); 96 | return 4711L << 32; 97 | } 98 | }; 99 | 100 | assertEquals("str()", iter.next()); 101 | assertEquals("Test Bla", iter.next()); 102 | assertEquals("buf()", iter.next()); 103 | assertEquals("offset()", iter.next()); 104 | assertEquals("len()", iter.next()); 105 | assertEquals("ugo", iter.next()); 106 | assertEquals("l()", iter.next()); 107 | assertEquals(Long.toString(4711L << 32), iter.next()); 108 | assertEquals("buf()", iter.next()); 109 | assertEquals("offset()", iter.next()); 110 | assertEquals("len()", iter.next()); 111 | assertEquals("str()", iter.next()); 112 | assertEquals("ugoTest", iter.next()); 113 | assertFalse(iter.hasNext()); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/test/java/de/matthiasmann/continuations/SuspendIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations; 30 | 31 | import org.junit.Test; 32 | 33 | /** 34 | * Basic test 35 | * 36 | * @author Matthias Mann 37 | */ 38 | public class SuspendIT implements CoroutineProto { 39 | 40 | @Test 41 | public void testSuspend() { 42 | SuspendIT test = new SuspendIT(); 43 | Coroutine co = new Coroutine(test); 44 | 45 | while(co.getState() != Coroutine.State.FINISHED) { 46 | System.out.println("State="+co.getState()); 47 | co.run(); 48 | } 49 | System.out.println("State="+co.getState()); 50 | } 51 | 52 | public void coExecute() throws SuspendExecution { 53 | int i0=0, i1=1; 54 | for(int j=0 ; j<10 ; j++) { 55 | i1 = i1 + i0; 56 | i0 = i1 - i0; 57 | print("bla %d %d\n", i0, i1); 58 | } 59 | } 60 | 61 | private static void print(String fmt, Object ... args) throws SuspendExecution { 62 | System.out.printf(fmt, args); 63 | Coroutine.yield(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/test/java/de/matthiasmann/continuations/ThrowIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations; 30 | 31 | import static org.junit.Assert.assertEquals; 32 | import static org.junit.Assert.assertTrue; 33 | 34 | import java.util.ArrayList; 35 | 36 | import org.junit.Test; 37 | 38 | /** 39 | * Test the propagation of unhandled exceptions throw a suspendable call 40 | * 41 | * @author Matthias Mann 42 | */ 43 | public class ThrowIT implements CoroutineProto { 44 | 45 | private ArrayList results = new ArrayList<>(); 46 | 47 | public void coExecute() throws SuspendExecution { 48 | results.add("A"); 49 | Coroutine.yield(); 50 | try { 51 | results.add("C"); 52 | Coroutine.yield(); 53 | if("".length() == 0) { 54 | throw new IllegalStateException("bla"); 55 | } 56 | results.add("E"); 57 | } finally { 58 | results.add("F"); 59 | } 60 | results.add("G"); 61 | } 62 | 63 | @Test 64 | public void testThrow() { 65 | results.clear(); 66 | 67 | Coroutine co = new Coroutine(this); 68 | try { 69 | co.run(); 70 | results.add("B"); 71 | co.run(); 72 | results.add("D"); 73 | co.run(); 74 | assertTrue(false); 75 | } catch (IllegalStateException es) { 76 | assertEquals("bla", es.getMessage()); 77 | assertEquals(Coroutine.State.FINISHED, co.getState()); 78 | } finally { 79 | System.out.println(results); 80 | } 81 | 82 | assertEquals(5, results.size()); 83 | assertEquals("A", results.get(0)); 84 | assertEquals("B", results.get(1)); 85 | assertEquals("C", results.get(2)); 86 | assertEquals("D", results.get(3)); 87 | assertEquals("F", results.get(4)); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/test/java/de/matthiasmann/continuations/UninitializedIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2013, Matthias Mann 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Matthias Mann nor the names of its 14 | * contributors may be used to endorse or promote products derived from 15 | * this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | package de.matthiasmann.continuations; 30 | 31 | import static org.junit.Assert.assertEquals; 32 | 33 | import org.junit.Test; 34 | 35 | /** 36 | * 37 | * @author Matthias Mann 38 | */ 39 | public class UninitializedIT implements CoroutineProto { 40 | 41 | Object result = "b"; 42 | 43 | @Test 44 | public void testUninitialized() { 45 | int count = 0; 46 | Coroutine co = new Coroutine(this); 47 | while(co.getState() != Coroutine.State.FINISHED) { 48 | ++count; 49 | co.run(); 50 | } 51 | assertEquals(2, count); 52 | assertEquals("a", result); 53 | } 54 | 55 | public void coExecute() throws SuspendExecution { 56 | result = getProperty(); 57 | } 58 | 59 | private Object getProperty() throws SuspendExecution { 60 | Object x; 61 | 62 | Object y = getProperty("a"); 63 | if(y != null) { 64 | x = y; 65 | } else { 66 | x = getProperty("c"); 67 | } 68 | 69 | return x; 70 | } 71 | 72 | private Object getProperty(String string) throws SuspendExecution { 73 | Coroutine.yield(); 74 | return string; 75 | } 76 | 77 | } 78 | --------------------------------------------------------------------------------