├── README ├── test └── de │ └── matthiasmann │ └── continuations │ ├── SomeInterface.java │ ├── DoubleTest.java │ ├── MergeTest.java │ ├── Merge3Test.java │ ├── ArrayTest.java │ ├── Merge2Test.java │ ├── NullTest.java │ ├── UninitializedTest.java │ ├── InterfaceTest.java │ ├── InheritTest.java │ ├── InitialSizeTest.java │ ├── SerializeTest.java │ ├── SuspendTest.java │ ├── CoIteratorTest.java │ ├── FinallyTest.java │ ├── ThrowTest.java │ ├── CatchTest.java │ ├── SuspendConstructorArgumentTest.java │ └── BlockingTest.java ├── nbproject ├── genfiles.properties ├── project.xml └── project.properties └── src └── de └── matthiasmann └── continuations ├── instrument ├── package-info.java ├── LogLevel.java ├── Log.java ├── AlreadyInstrumented.java ├── ExtractSuperClass.java ├── DBClassWriter.java ├── OmittedInstruction.java ├── NewValue.java ├── UnableToInstrumentException.java ├── CheckInstrumentationVisitor.java ├── TypeAnalyzer.java ├── InstrumentClass.java ├── JavaAgent.java ├── TypeInterpreter.java ├── InstrumentationTask.java ├── MethodDatabase.java └── InstrumentMethod.java ├── package-info.java ├── CoroutineProto.java ├── Util.java ├── SuspendExecution.java ├── CoIterator.java ├── Stack.java └── Coroutine.java /README: -------------------------------------------------------------------------------- 1 | Continuations Library in Java 2 | ============================= 3 | 4 | It is hard to dismiss this small, fast, and easy to understand continuations 5 | library in Java. This *is not* my code. 6 | 7 | Links: 8 | http://blog.paralleluniverse.co/post/49445260575/quasar-pulsar 9 | http://www.matthiasmann.de/content/view/24/26/ 10 | -------------------------------------------------------------------------------- /test/de/matthiasmann/continuations/SomeInterface.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | 6 | package de.matthiasmann.continuations; 7 | 8 | /** 9 | * A dummy interface used for the InterfaceTest 10 | * 11 | * @author Elias Naur 12 | */ 13 | public interface SomeInterface { 14 | 15 | void doStuff() throws SuspendExecution; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /nbproject/genfiles.properties: -------------------------------------------------------------------------------- 1 | build.xml.data.CRC32=b472e9be 2 | build.xml.script.CRC32=0a953317 3 | build.xml.stylesheet.CRC32=be360661 4 | # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. 5 | # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. 6 | nbproject/build-impl.xml.data.CRC32=b472e9be 7 | nbproject/build-impl.xml.script.CRC32=b78ec55c 8 | nbproject/build-impl.xml.stylesheet.CRC32=6ddba6b6@1.53.1.46 9 | -------------------------------------------------------------------------------- /nbproject/project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.netbeans.modules.java.j2seproject 4 | 5 | 6 | Continuations 7 | 1.6.5 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/de/matthiasmann/continuations/DoubleTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | 6 | package de.matthiasmann.continuations; 7 | 8 | import junit.framework.TestCase; 9 | import org.junit.Test; 10 | 11 | /** 12 | * 13 | * @author Matthias Mann 14 | */ 15 | public class DoubleTest extends TestCase implements CoroutineProto { 16 | 17 | double result; 18 | 19 | @Test 20 | public void testDouble() { 21 | Coroutine co = new Coroutine(this); 22 | co.run(); 23 | assertEquals(0, result, 1e-8); 24 | co.run(); 25 | assertEquals(1, result, 1e-8); 26 | assertEquals(Coroutine.State.FINISHED, co.getState()); 27 | } 28 | 29 | @Override 30 | public void coExecute() throws SuspendExecution { 31 | double temp = Math.cos(0); 32 | Coroutine.yield(); 33 | this.result = temp; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /test/de/matthiasmann/continuations/MergeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | package de.matthiasmann.continuations; 6 | 7 | import java.io.FileNotFoundException; 8 | import java.io.IOException; 9 | import junit.framework.TestCase; 10 | import org.junit.Test; 11 | 12 | /** 13 | * 14 | * @author mam 15 | */ 16 | public class MergeTest extends TestCase implements CoroutineProto { 17 | 18 | public static void throwsIO() throws IOException { 19 | } 20 | 21 | public void coExecute() throws SuspendExecution { 22 | try { 23 | throwsIO(); 24 | } catch(FileNotFoundException e) { 25 | e.printStackTrace(); 26 | } catch(IOException e) { 27 | e.printStackTrace(); 28 | } 29 | } 30 | 31 | @Test 32 | public void testMerge() { 33 | Coroutine c = new Coroutine(new MergeTest()); 34 | c.run(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test/de/matthiasmann/continuations/Merge3Test.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | package de.matthiasmann.continuations; 6 | 7 | import junit.framework.TestCase; 8 | 9 | /** 10 | * 11 | * @author Matthias Mann 12 | */ 13 | public class Merge3Test extends TestCase implements CoroutineProto { 14 | 15 | public boolean a; 16 | public boolean b; 17 | 18 | public void coExecute() throws SuspendExecution { 19 | if(a) { 20 | Object[] arr = new Object[2]; 21 | System.out.println(arr); 22 | } else { 23 | float[] arr = new float[3]; 24 | System.out.println(arr); 25 | } 26 | blub(); 27 | System.out.println(); 28 | } 29 | 30 | private void blub() throws SuspendExecution { 31 | } 32 | 33 | @org.junit.Test 34 | public void testMerge3() { 35 | Coroutine c = new Coroutine(new Merge3Test()); 36 | c.run(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/de/matthiasmann/continuations/ArrayTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | 6 | package de.matthiasmann.continuations; 7 | 8 | import junit.framework.TestCase; 9 | 10 | /** 11 | * 12 | * @author Matthias Mann 13 | */ 14 | public class ArrayTest extends TestCase implements CoroutineProto { 15 | 16 | private static final PatchLevel l1 = new PatchLevel(); 17 | private static final PatchLevel[] l2 = new PatchLevel[] { l1 }; 18 | private static final PatchLevel[][] l3 = new PatchLevel[][] { l2 }; 19 | 20 | public void testArray() { 21 | Coroutine co = new Coroutine(this); 22 | co.run(); 23 | assertEquals(42, l1.i); 24 | } 25 | 26 | public void coExecute() throws SuspendExecution { 27 | PatchLevel[][] local_patch_levels = l3; 28 | PatchLevel patch_level = local_patch_levels[0][0]; 29 | patch_level.setLevel(42); 30 | } 31 | 32 | public static class PatchLevel { 33 | int i; 34 | 35 | public void setLevel(int value) throws SuspendExecution { 36 | i = value; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/de/matthiasmann/continuations/Merge2Test.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | package de.matthiasmann.continuations; 6 | 7 | import junit.framework.TestCase; 8 | 9 | /** 10 | * 11 | * @author mam 12 | */ 13 | public class Merge2Test extends TestCase implements CoroutineProto { 14 | 15 | public interface Interface { 16 | public void method(); 17 | } 18 | 19 | public static Interface getInterface() { 20 | return null; 21 | } 22 | 23 | public static void suspendable() throws SuspendExecution { 24 | } 25 | 26 | public void coExecute() throws SuspendExecution { 27 | try { 28 | Interface iface = getInterface(); 29 | iface.method(); 30 | } catch(IllegalStateException ise) { 31 | suspendable(); 32 | } 33 | } 34 | 35 | @org.junit.Test 36 | public void testMerge2() { 37 | try { 38 | Coroutine c = new Coroutine(new Merge2Test()); 39 | c.run(); 40 | assertTrue("Should not reach here", false); 41 | } catch (NullPointerException ex) { 42 | // NPE expected 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /test/de/matthiasmann/continuations/NullTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | 6 | package de.matthiasmann.continuations; 7 | 8 | import junit.framework.TestCase; 9 | import org.junit.Test; 10 | 11 | /** 12 | * 13 | * @author Matthias Mann 14 | */ 15 | public class NullTest extends TestCase implements CoroutineProto { 16 | 17 | Object result = "b"; 18 | 19 | @Test 20 | public void testNull() { 21 | int count = 0; 22 | Coroutine co = new Coroutine(this); 23 | while(co.getState() != Coroutine.State.FINISHED) { 24 | ++count; 25 | co.run(); 26 | } 27 | assertEquals(2, count); 28 | assertEquals("a", result); 29 | } 30 | 31 | public void coExecute() throws SuspendExecution { 32 | result = getProperty(); 33 | } 34 | 35 | private Object getProperty() throws SuspendExecution { 36 | Object x = null; 37 | 38 | Object y = getProtery("a"); 39 | if(y != null) { 40 | x = y; 41 | } 42 | 43 | return x; 44 | } 45 | 46 | private Object getProtery(String string) throws SuspendExecution { 47 | Coroutine.yield(); 48 | return string; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /test/de/matthiasmann/continuations/UninitializedTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | 6 | package de.matthiasmann.continuations; 7 | 8 | import junit.framework.TestCase; 9 | import org.junit.Test; 10 | 11 | /** 12 | * 13 | * @author Matthias Mann 14 | */ 15 | public class UninitializedTest extends TestCase implements CoroutineProto { 16 | 17 | Object result = "b"; 18 | 19 | @Test 20 | public void testUninitialized() { 21 | int count = 0; 22 | Coroutine co = new Coroutine(this); 23 | while(co.getState() != Coroutine.State.FINISHED) { 24 | ++count; 25 | co.run(); 26 | } 27 | assertEquals(2, count); 28 | assertEquals("a", result); 29 | } 30 | 31 | public void coExecute() throws SuspendExecution { 32 | result = getProperty(); 33 | } 34 | 35 | private Object getProperty() throws SuspendExecution { 36 | Object x; 37 | 38 | Object y = getProtery("a"); 39 | if(y != null) { 40 | x = y; 41 | } else { 42 | x = getProtery("c"); 43 | } 44 | 45 | return x; 46 | } 47 | 48 | private Object getProtery(String string) throws SuspendExecution { 49 | Coroutine.yield(); 50 | return string; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/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/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 | -------------------------------------------------------------------------------- /test/de/matthiasmann/continuations/InterfaceTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | 6 | package de.matthiasmann.continuations; 7 | 8 | import junit.framework.TestCase; 9 | import org.junit.Test; 10 | 11 | /** 12 | * 13 | * @author Elias Naur 14 | */ 15 | public class InterfaceTest extends TestCase { 16 | 17 | public class C2 implements SomeInterface { 18 | public void doStuff() throws SuspendExecution { 19 | } 20 | } 21 | 22 | public class C implements SomeInterface { 23 | public void doStuff() throws SuspendExecution { 24 | /* float time = 0f; 25 | float seconds = .8f; 26 | do { 27 | float t = .06667f; 28 | System.out.println("time = " + time + " " + (time + t)); 29 | // time = StrictMath.min(time + t, seconds); 30 | time = time + t; 31 | System.out.println("seconds = " + seconds + " | time = " + time + " | t = " + t); 32 | System.out.println("this = " + this); 33 | 34 | System.out.println("time just not after = " + time); 35 | Coroutine.yield(); 36 | System.out.println("time after = " + time); 37 | } while (time < seconds); 38 | System.out.println("1 = " + 1);*/ 39 | } 40 | } 41 | 42 | @Test 43 | public void testSuspend() { 44 | // final I i = new C(); 45 | Coroutine co = new Coroutine(new CoroutineProto() { 46 | public final void coExecute() throws SuspendExecution { 47 | // next line causes an error because of incomplete merge in TypeInterpreter 48 | //SomeInterface i = System.currentTimeMillis() > 0 ? new C() : new C2(); 49 | SomeInterface i = new C(); 50 | System.out.println("i = " + i); 51 | i.doStuff(); 52 | } 53 | }); 54 | while(co.getState() != Coroutine.State.FINISHED) { 55 | System.out.println("State="+co.getState()); 56 | co.run(); 57 | } 58 | System.out.println("State="+co.getState()); 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /src/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/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 | -------------------------------------------------------------------------------- /test/de/matthiasmann/continuations/InheritTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | 6 | package de.matthiasmann.continuations; 7 | 8 | import java.util.ArrayList; 9 | import junit.framework.TestCase; 10 | import org.junit.Test; 11 | 12 | /** 13 | * 14 | * @author mam 15 | */ 16 | public class InheritTest extends TestCase { 17 | 18 | @Test 19 | public void testInherit() { 20 | final C dut = new C(); 21 | Coroutine c = new Coroutine(new CoroutineProto() { 22 | public void coExecute() throws SuspendExecution { 23 | dut.myMethod(); 24 | } 25 | }); 26 | for(int i=0 ; i<3 ; i++) { 27 | c.run(); 28 | } 29 | 30 | assertEquals(5, dut.result.size()); 31 | assertEquals("a", dut.result.get(0)); 32 | assertEquals("o1", dut.result.get(1)); 33 | assertEquals("o2", dut.result.get(2)); 34 | assertEquals("b", dut.result.get(3)); 35 | assertEquals("b", dut.result.get(4)); 36 | } 37 | 38 | public static class A { 39 | public static void yield() throws SuspendExecution { 40 | Coroutine.yield(); 41 | } 42 | } 43 | 44 | public static class B extends A { 45 | final ArrayList result = new ArrayList(); 46 | } 47 | 48 | public static class C extends B { 49 | 50 | public void otherMethod() throws SuspendExecution { 51 | result.add("o1"); 52 | Coroutine.yield(); 53 | result.add("o2"); 54 | } 55 | 56 | public void myMethod() throws SuspendExecution { 57 | result.add("a"); 58 | otherMethod(); 59 | 60 | for(;;) { 61 | result.add("b"); 62 | if(result.size() > 10) { 63 | otherMethod(); 64 | result.add("Ohh!"); 65 | } 66 | yield(); 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /test/de/matthiasmann/continuations/InitialSizeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | package de.matthiasmann.continuations; 6 | 7 | import java.lang.reflect.Field; 8 | import junit.framework.TestCase; 9 | import org.junit.Test; 10 | 11 | /** 12 | * 13 | * @author Matthias Mann 14 | */ 15 | public class InitialSizeTest extends TestCase implements CoroutineProto { 16 | 17 | @Test 18 | public void test1() { 19 | testWithSize(1); 20 | } 21 | 22 | @Test 23 | public void test2() { 24 | testWithSize(2); 25 | } 26 | 27 | @Test 28 | public void test3() { 29 | testWithSize(3); 30 | } 31 | 32 | private void testWithSize(int stackSize) { 33 | Coroutine c = new Coroutine(this, stackSize); 34 | assertEquals(getStackSize(c), stackSize); 35 | c.run(); 36 | assertEquals(Coroutine.State.SUSPENDED, c.getState()); 37 | c.run(); 38 | assertEquals(Coroutine.State.FINISHED, c.getState()); 39 | assertTrue(getStackSize(c) > 10); 40 | } 41 | 42 | public void coExecute() throws SuspendExecution { 43 | assertEquals(3628800, factorial(10)); 44 | } 45 | 46 | private int factorial(Integer a) throws SuspendExecution { 47 | if(a == 0) { 48 | Coroutine.yield(); 49 | return 1; 50 | } 51 | return a * factorial(a - 1); 52 | } 53 | 54 | private int getStackSize(Coroutine c) { 55 | try { 56 | Field stackField = Coroutine.class.getDeclaredField("stack"); 57 | stackField.setAccessible(true); 58 | Object stack = stackField.get(c); 59 | Field dataObjectField = Stack.class.getDeclaredField("dataObject"); 60 | dataObjectField.setAccessible(true); 61 | Object[] dataObject = (Object[])dataObjectField.get(stack); 62 | return dataObject.length; 63 | } catch(Throwable ex) { 64 | throw new AssertionError(ex); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/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/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.ASM4); 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/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/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 | -------------------------------------------------------------------------------- /test/de/matthiasmann/continuations/SerializeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | 6 | package de.matthiasmann.continuations; 7 | 8 | import java.io.ByteArrayInputStream; 9 | import java.io.ByteArrayOutputStream; 10 | import java.io.IOException; 11 | import java.io.ObjectInputStream; 12 | import java.io.ObjectOutputStream; 13 | import java.io.Serializable; 14 | import java.util.Iterator; 15 | import junit.framework.TestCase; 16 | import org.junit.Test; 17 | 18 | /** 19 | * 20 | * @author Matthias Mann 21 | */ 22 | public class SerializeTest extends TestCase { 23 | 24 | @Test 25 | public void testSerialize() throws IOException, ClassNotFoundException { 26 | Iterator iter1 = new TestIterator(); 27 | 28 | assertEquals("A", iter1.next()); 29 | assertEquals("B", iter1.next()); 30 | assertEquals("C0", iter1.next()); 31 | assertEquals("C1", iter1.next()); 32 | 33 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 34 | ObjectOutputStream oos = new ObjectOutputStream(baos); 35 | oos.writeObject(iter1); 36 | oos.close(); 37 | 38 | byte[] bytes = baos.toByteArray(); 39 | 40 | ByteArrayInputStream bais = new ByteArrayInputStream(bytes); 41 | ObjectInputStream ois = new ObjectInputStream(bais); 42 | @SuppressWarnings("unchecked") 43 | Iterator iter2 = (Iterator)ois.readObject(); 44 | 45 | assertNotSame(iter1, iter2); 46 | 47 | assertEquals("C2", iter2.next()); 48 | assertEquals("C3", iter2.next()); 49 | assertEquals("D", iter2.next()); 50 | assertEquals("E", iter2.next()); 51 | assertFalse(iter2.hasNext()); 52 | 53 | assertEquals("C2", iter1.next()); 54 | assertEquals("C3", iter1.next()); 55 | assertEquals("D", iter1.next()); 56 | assertEquals("E", iter1.next()); 57 | assertFalse(iter1.hasNext()); 58 | } 59 | 60 | private static class TestIterator extends CoIterator implements Serializable { 61 | @Override 62 | protected void run() throws SuspendExecution { 63 | produce("A"); 64 | produce("B"); 65 | for(int i = 0; i < 4; i++) { 66 | produce("C" + i); 67 | } 68 | produce("D"); 69 | produce("E"); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/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/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 | import org.objectweb.asm.MethodVisitor; 34 | import org.objectweb.asm.tree.AbstractInsnNode; 35 | 36 | /** 37 | * 38 | * @author matthias 39 | */ 40 | public class OmittedInstruction extends AbstractInsnNode { 41 | 42 | private final AbstractInsnNode orgInsn; 43 | 44 | public OmittedInstruction(AbstractInsnNode orgInsn) { 45 | super(orgInsn.getOpcode()); 46 | this.orgInsn = orgInsn; 47 | } 48 | 49 | @Override 50 | public int getType() { 51 | return orgInsn.getType(); 52 | } 53 | 54 | @Override 55 | public void accept(MethodVisitor cv) { 56 | } 57 | 58 | @Override 59 | public AbstractInsnNode clone(Map labels) { 60 | return new OmittedInstruction(orgInsn.clone(labels)); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/de/matthiasmann/continuations/SuspendExecution.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 | *

An 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 | -------------------------------------------------------------------------------- /nbproject/project.properties: -------------------------------------------------------------------------------- 1 | annotation.processing.enabled=true 2 | annotation.processing.enabled.in.editor=false 3 | annotation.processing.processors.list= 4 | annotation.processing.run.all.processors=true 5 | application.title=Continuations 6 | application.vendor=mam 7 | build.classes.dir=${build.dir}/classes 8 | build.classes.excludes=**/*.java,**/*.form 9 | # This directory is removed when the project is cleaned: 10 | build.dir=build 11 | build.generated.dir=${build.dir}/generated 12 | build.generated.sources.dir=${build.dir}/generated-sources 13 | # Only compile against the classpath explicitly listed here: 14 | build.sysclasspath=ignore 15 | build.test.classes.dir=${build.dir}/test/classes 16 | build.test.results.dir=${build.dir}/test/results 17 | debug.classpath=\ 18 | ${run.classpath} 19 | debug.test.classpath=\ 20 | ${run.test.classpath} 21 | # This directory is removed when the project is cleaned: 22 | dist.dir=dist 23 | dist.jar=${dist.dir}/Continuations.jar 24 | dist.javadoc.dir=${dist.dir}/javadoc 25 | endorsed.classpath= 26 | excludes= 27 | includes=** 28 | jar.archive.disabled=${jnlp.enabled} 29 | jar.compress=false 30 | jar.index=${jnlp.enabled} 31 | javac.classpath=\ 32 | ${libs.ANT.classpath}:\ 33 | ${libs.ASM4.classpath} 34 | # Space-separated list of extra javac options 35 | javac.compilerargs= 36 | javac.deprecation=false 37 | javac.processorpath=\ 38 | ${javac.classpath} 39 | javac.source=1.5 40 | javac.target=1.5 41 | javac.test.classpath=\ 42 | ${javac.classpath}:\ 43 | ${build.classes.dir}:\ 44 | ${libs.junit_4.classpath} 45 | javadoc.additionalparam= 46 | javadoc.author=false 47 | javadoc.encoding=${source.encoding} 48 | javadoc.noindex=false 49 | javadoc.nonavbar=false 50 | javadoc.notree=true 51 | javadoc.private=false 52 | javadoc.splitindex=false 53 | javadoc.use=false 54 | javadoc.version=false 55 | javadoc.windowtitle=Continuations Library 56 | jnlp.codebase.type=no.codebase 57 | jnlp.descriptor=application 58 | jnlp.enabled=false 59 | jnlp.mixed.code=default 60 | jnlp.offline-allowed=false 61 | jnlp.signed=false 62 | jnlp.signing= 63 | jnlp.signing.alias= 64 | jnlp.signing.keystore= 65 | main.class=IterTest 66 | meta.inf.dir=${src.dir}/META-INF 67 | mkdist.disabled=false 68 | platform.active=default_platform 69 | run.classpath=\ 70 | ${javac.classpath}:\ 71 | ${build.classes.dir} 72 | # Space-separated list of JVM arguments used when running the project 73 | # (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value 74 | # or test-sys-prop.name=value to set system properties for unit tests): 75 | run.jvmargs=-ea 76 | run.test.classpath=\ 77 | ${javac.test.classpath}:\ 78 | ${build.test.classes.dir} 79 | source.encoding=UTF-8 80 | src.dir=src 81 | test.src.dir=test 82 | -------------------------------------------------------------------------------- /test/de/matthiasmann/continuations/SuspendTest.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 junit.framework.TestCase; 32 | import org.junit.Test; 33 | 34 | /** 35 | * Basic test 36 | * 37 | * @author Matthias Mann 38 | */ 39 | public class SuspendTest extends TestCase implements CoroutineProto { 40 | 41 | @Test 42 | public void testSuspend() { 43 | SuspendTest test = new SuspendTest(); 44 | Coroutine co = new Coroutine(test); 45 | 46 | while(co.getState() != Coroutine.State.FINISHED) { 47 | System.out.println("State="+co.getState()); 48 | co.run(); 49 | } 50 | System.out.println("State="+co.getState()); 51 | } 52 | 53 | public void coExecute() throws SuspendExecution { 54 | int i0=0, i1=1; 55 | for(int j=0 ; j<10 ; j++) { 56 | i1 = i1 + i0; 57 | i0 = i1 - i0; 58 | print("bla %d %d\n", i0, i1); 59 | } 60 | } 61 | 62 | private static void print(String fmt, Object ... args) throws SuspendExecution { 63 | System.out.printf(fmt, args); 64 | Coroutine.yield(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/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 | -------------------------------------------------------------------------------- /test/de/matthiasmann/continuations/CoIteratorTest.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.util.Iterator; 32 | import junit.framework.TestCase; 33 | import org.junit.Test; 34 | 35 | /** 36 | * 37 | * Test the {@link CoIterato} class 38 | * 39 | * @author Matthias Mann 40 | */ 41 | public class CoIteratorTest extends TestCase { 42 | 43 | @Test 44 | public void testCoIterator() { 45 | Iterator iter = new CoIterator() { 46 | @Override 47 | public void run() throws SuspendExecution { 48 | for(int j=0 ; j<3 ; j++) { 49 | produce("Hugo " + j); 50 | produce("Test"); 51 | for(int i=1 ; i<10 ; i++) { 52 | produce("Number " + i); 53 | } 54 | produce("Nix"); 55 | } 56 | } 57 | }; 58 | 59 | for(int j=0 ; j<3 ; j++) { 60 | assertEquals("Hugo " + j, iter.next()); 61 | assertEquals("Test", iter.next()); 62 | for(int i=1 ; i<10 ; i++) { 63 | assertEquals("Number " + i, iter.next()); 64 | } 65 | assertEquals("Nix", iter.next()); 66 | } 67 | 68 | assertFalse(iter.hasNext()); 69 | } 70 | 71 | } -------------------------------------------------------------------------------- /src/de/matthiasmann/continuations/instrument/UnableToInstrumentException.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 | /** 32 | *

This 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 | -------------------------------------------------------------------------------- /test/de/matthiasmann/continuations/FinallyTest.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.util.ArrayList; 32 | import junit.framework.TestCase; 33 | import org.junit.Test; 34 | 35 | /** 36 | * Test correct execution of a finally statement 37 | * 38 | * @author Matthias Mann 39 | */ 40 | public class FinallyTest extends TestCase implements CoroutineProto { 41 | 42 | private ArrayList results = new ArrayList(); 43 | 44 | public void coExecute() throws SuspendExecution { 45 | results.add("A"); 46 | Coroutine.yield(); 47 | try { 48 | results.add("C"); 49 | Coroutine.yield(); 50 | results.add("E"); 51 | } finally { 52 | results.add("F"); 53 | } 54 | results.add("G"); 55 | Coroutine.yield(); 56 | results.add("I"); 57 | } 58 | 59 | @Test 60 | public void testFinally() { 61 | results.clear(); 62 | 63 | try { 64 | Coroutine co = new Coroutine(this); 65 | co.run(); 66 | results.add("B"); 67 | co.run(); 68 | results.add("D"); 69 | co.run(); 70 | results.add("H"); 71 | co.run(); 72 | } finally { 73 | System.out.println(results); 74 | } 75 | 76 | assertEquals(9, results.size()); 77 | assertEquals("A", results.get(0)); 78 | assertEquals("B", results.get(1)); 79 | assertEquals("C", results.get(2)); 80 | assertEquals("D", results.get(3)); 81 | assertEquals("E", results.get(4)); 82 | assertEquals("F", results.get(5)); 83 | assertEquals("G", results.get(6)); 84 | assertEquals("H", results.get(7)); 85 | assertEquals("I", results.get(8)); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /test/de/matthiasmann/continuations/ThrowTest.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.util.ArrayList; 32 | import junit.framework.TestCase; 33 | import org.junit.Test; 34 | 35 | /** 36 | * Test the propagation of unhandled exceptions throw a suspendable call 37 | * 38 | * @author Matthias Mann 39 | */ 40 | public class ThrowTest extends TestCase implements CoroutineProto { 41 | 42 | private ArrayList results = new ArrayList(); 43 | 44 | public void coExecute() throws SuspendExecution { 45 | results.add("A"); 46 | Coroutine.yield(); 47 | try { 48 | results.add("C"); 49 | Coroutine.yield(); 50 | if("".length() == 0) { 51 | throw new IllegalStateException("bla"); 52 | } 53 | results.add("E"); 54 | } finally { 55 | results.add("F"); 56 | } 57 | results.add("G"); 58 | } 59 | 60 | @Test 61 | public void testThrow() { 62 | results.clear(); 63 | 64 | Coroutine co = new Coroutine(this); 65 | try { 66 | co.run(); 67 | results.add("B"); 68 | co.run(); 69 | results.add("D"); 70 | co.run(); 71 | assertTrue(false); 72 | } catch (IllegalStateException es) { 73 | assertEquals("bla", es.getMessage()); 74 | assertEquals(Coroutine.State.FINISHED, co.getState()); 75 | } finally { 76 | System.out.println(results); 77 | } 78 | 79 | assertEquals(5, results.size()); 80 | assertEquals("A", results.get(0)); 81 | assertEquals("B", results.get(1)); 82 | assertEquals("C", results.get(2)); 83 | assertEquals("D", results.get(3)); 84 | assertEquals("F", results.get(4)); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /test/de/matthiasmann/continuations/CatchTest.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.util.ArrayList; 32 | import java.util.Iterator; 33 | import junit.framework.TestCase; 34 | import org.junit.Test; 35 | 36 | /** 37 | * Check that a generic catch all does not affect the suspendtion of a method 38 | * 39 | * @author Matthias Mann 40 | */ 41 | public class CatchTest extends TestCase implements CoroutineProto { 42 | 43 | private ArrayList results = new ArrayList(); 44 | 45 | int cnt = 0; 46 | private void throwOnSecondCall() throws SuspendExecution { 47 | results.add("cnt=" + cnt); 48 | Coroutine.yield(); 49 | if(++cnt >= 2) { 50 | throw new IllegalStateException("called second time"); 51 | } 52 | results.add("not thrown"); 53 | } 54 | 55 | public void coExecute() throws SuspendExecution { 56 | results.add("A"); 57 | Coroutine.yield(); 58 | try { 59 | results.add("C"); 60 | Coroutine.yield(); 61 | throwOnSecondCall(); 62 | Coroutine.yield(); 63 | throwOnSecondCall(); 64 | results.add("never reached"); 65 | } catch(Throwable ex) { 66 | results.add(ex.getMessage()); 67 | } 68 | results.add("H"); 69 | } 70 | 71 | @Test 72 | public void testCatch() { 73 | results.clear(); 74 | 75 | try { 76 | Coroutine co = new Coroutine(this); 77 | co.run(); 78 | results.add("B"); 79 | co.run(); 80 | results.add("D"); 81 | co.run(); 82 | results.add("E"); 83 | co.run(); 84 | results.add("F"); 85 | co.run(); 86 | results.add("G"); 87 | co.run(); 88 | results.add("I"); 89 | } finally { 90 | System.out.println(results); 91 | } 92 | 93 | assertEquals(13, results.size()); 94 | Iterator iter = results.iterator(); 95 | assertEquals("A", iter.next()); 96 | assertEquals("B", iter.next()); 97 | assertEquals("C", iter.next()); 98 | assertEquals("D", iter.next()); 99 | assertEquals("cnt=0", iter.next()); 100 | assertEquals("E", iter.next()); 101 | assertEquals("not thrown", iter.next()); 102 | assertEquals("F", iter.next()); 103 | assertEquals("cnt=1", iter.next()); 104 | assertEquals("G", iter.next()); 105 | assertEquals("called second time", iter.next()); 106 | assertEquals("H", iter.next()); 107 | assertEquals("I", iter.next()); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/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.ASM4); 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 | -------------------------------------------------------------------------------- /test/de/matthiasmann/continuations/SuspendConstructorArgumentTest.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.IOException; 32 | import java.util.Iterator; 33 | import junit.framework.TestCase; 34 | import org.junit.Test; 35 | 36 | /** 37 | * Test to checking suspendable method calls as constructor parameters 38 | * 39 | * @author Matthias Mann 40 | */ 41 | public class SuspendConstructorArgumentTest extends TestCase { 42 | 43 | @Test 44 | public void testCalls() throws IOException { 45 | Iterator iter = new CoIterator() { 46 | @Override 47 | public void run() throws SuspendExecution { 48 | m1(); 49 | m2(); 50 | m3(); 51 | m4(); 52 | } 53 | 54 | private void m1() throws SuspendExecution { 55 | // this is the pattern generated by Eclipse's compiler for: str() + " Bla" 56 | produce(new StringBuilder(str()).append(" Bla").toString()); 57 | } 58 | 59 | private void m2() throws SuspendExecution { 60 | produce(new String(buf(), offset(), len())); 61 | } 62 | 63 | private void m3() throws SuspendExecution { 64 | produce(new Long(l()).toString()); 65 | } 66 | 67 | private void m4() throws SuspendExecution { 68 | produce(new StringBuilder(new String(buf(), offset(), len())).append(str()).toString()); 69 | } 70 | 71 | private String str() throws SuspendExecution { 72 | produce("str()"); 73 | return "Test"; 74 | } 75 | 76 | private char[] buf() throws SuspendExecution { 77 | produce("buf()"); 78 | return "Hugo".toCharArray(); 79 | } 80 | 81 | private int offset() throws SuspendExecution { 82 | produce("offset()"); 83 | return 1; 84 | } 85 | 86 | private int len() throws SuspendExecution { 87 | produce("len()"); 88 | return 3; 89 | } 90 | 91 | private long l() throws SuspendExecution { 92 | produce("l()"); 93 | return 4711L << 32; 94 | } 95 | }; 96 | 97 | assertEquals("str()", iter.next()); 98 | assertEquals("Test Bla", iter.next()); 99 | assertEquals("buf()", iter.next()); 100 | assertEquals("offset()", iter.next()); 101 | assertEquals("len()", iter.next()); 102 | assertEquals("ugo", iter.next()); 103 | assertEquals("l()", iter.next()); 104 | assertEquals(Long.toString(4711L << 32), iter.next()); 105 | assertEquals("buf()", iter.next()); 106 | assertEquals("offset()", iter.next()); 107 | assertEquals("len()", iter.next()); 108 | assertEquals("str()", iter.next()); 109 | assertEquals("ugoTest", iter.next()); 110 | assertFalse(iter.hasNext()); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/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 | public boolean hasNext() { 55 | while(!hasElement && co.getState() != Coroutine.State.FINISHED) { 56 | co.run(); 57 | } 58 | return hasElement; 59 | } 60 | 61 | public E next() { 62 | if(!hasNext()) { 63 | throw new NoSuchElementException(); 64 | } 65 | E result = element; 66 | hasElement = false; 67 | element = null; 68 | return result; 69 | } 70 | 71 | /** 72 | * Always throws UnsupportedOperationException. 73 | * @throws java.lang.UnsupportedOperationException always 74 | */ 75 | public void remove() throws UnsupportedOperationException { 76 | throw new UnsupportedOperationException("Not supported"); 77 | } 78 | 79 | /** 80 | * Produces the next value to be returned by the {@link #next} method. 81 | * 82 | * @param element The value that should be returned by {@link #next} 83 | * @throws de.matthiasmann.continuations.SuspendExecution This method will suspend the execution 84 | */ 85 | protected void produce(E element) throws SuspendExecution { 86 | if(hasElement) { 87 | throw new IllegalStateException("hasElement = true"); 88 | } 89 | this.element = element; 90 | hasElement = true; 91 | Coroutine.yield(); 92 | } 93 | 94 | /** 95 | *

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

97 | * 98 | *

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

102 | * 103 | *

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

105 | * 106 | * @throws de.matthiasmann.continuations.SuspendExecution 107 | */ 108 | protected abstract void run() throws SuspendExecution; 109 | 110 | private class DelegateExecute implements CoroutineProto, Serializable { 111 | private static final long serialVersionUID = 12784529515412L; 112 | 113 | public void coExecute() throws SuspendExecution { 114 | CoIterator.this.run(); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/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 | -------------------------------------------------------------------------------- /test/de/matthiasmann/continuations/BlockingTest.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 de.matthiasmann.continuations.instrument.AlreadyInstrumented; 32 | import de.matthiasmann.continuations.instrument.InstrumentClass; 33 | import de.matthiasmann.continuations.instrument.Log; 34 | import de.matthiasmann.continuations.instrument.LogLevel; 35 | import de.matthiasmann.continuations.instrument.MethodDatabase; 36 | import java.io.IOException; 37 | import java.io.InputStream; 38 | import java.util.HashSet; 39 | import java.util.Locale; 40 | import java.util.concurrent.locks.Lock; 41 | import junit.framework.TestCase; 42 | import org.junit.Test; 43 | import org.objectweb.asm.ClassReader; 44 | import org.objectweb.asm.ClassWriter; 45 | 46 | /** 47 | * Test to checking blocking call detection 48 | * 49 | * @author Matthias Mann 50 | */ 51 | @AlreadyInstrumented 52 | public class BlockingTest extends TestCase { 53 | 54 | @Test 55 | public void testSuspend() throws IOException { 56 | final String className = BlockingTest.class.getName().replace('.', '/'); 57 | final HashSet msgs = new HashSet(); 58 | msgs.add("Method "+className+"#t_wait()V contains potentially blocking call to java/lang/Object#wait()V"); 59 | msgs.add("Method "+className+"#t_sleep1()V contains potentially blocking call to java/lang/Thread#sleep(J)V"); 60 | msgs.add("Method "+className+"#t_sleep2()V contains potentially blocking call to java/lang/Thread#sleep(JI)V"); 61 | msgs.add("Method "+className+"#t_join1(Ljava/lang/Thread;)V contains potentially blocking call to java/lang/Thread#join()V"); 62 | msgs.add("Method "+className+"#t_join2(Ljava/lang/Thread;)V contains potentially blocking call to java/lang/Thread#join(J)V"); 63 | msgs.add("Method "+className+"#t_join3(Ljava/lang/Thread;)V contains potentially blocking call to java/lang/Thread#join(JI)V"); 64 | msgs.add("Method "+className+"#t_lock1(Ljava/util/concurrent/locks/Lock;)V contains potentially blocking call to java/util/concurrent/locks/Lock#lock()V"); 65 | msgs.add("Method "+className+"#t_lock2(Ljava/util/concurrent/locks/Lock;)V contains potentially blocking call to java/util/concurrent/locks/Lock#lockInterruptibly()V"); 66 | 67 | MethodDatabase db = new MethodDatabase(BlockingTest.class.getClassLoader()); 68 | db.setAllowBlocking(true); 69 | db.setLog(new Log() { 70 | public void log(LogLevel level, String msg, Object... args) { 71 | if(level == LogLevel.WARNING) { 72 | msg = String.format(Locale.ENGLISH, msg, args); 73 | assertTrue("Unexpected message: " + msg, msgs.remove(msg)); 74 | } 75 | } 76 | public void error(String msg, Exception ex) { 77 | throw new AssertionError(msg, ex); 78 | } 79 | }); 80 | 81 | InputStream in = BlockingTest.class.getResourceAsStream("BlockingTest.class"); 82 | try { 83 | ClassReader r = new ClassReader(in); 84 | ClassWriter cw = new ClassWriter(0); 85 | InstrumentClass ic = new InstrumentClass(cw, db, true); 86 | r.accept(ic, ClassReader.SKIP_FRAMES); 87 | } finally { 88 | in.close(); 89 | } 90 | 91 | assertTrue("Expected messages not generated: "+msgs.toString(), msgs.isEmpty()); 92 | } 93 | 94 | public void t_wait() throws SuspendExecution, InterruptedException { 95 | synchronized (this) { 96 | wait(); 97 | } 98 | } 99 | 100 | public void t_notify() throws SuspendExecution { 101 | synchronized (this) { 102 | notify(); 103 | } 104 | } 105 | 106 | public void t_sleep1() throws SuspendExecution, InterruptedException { 107 | Thread.sleep(1000); 108 | } 109 | 110 | public void t_sleep2() throws SuspendExecution, InterruptedException { 111 | Thread.sleep(1000, 100); 112 | } 113 | 114 | public void t_join1(Thread t) throws SuspendExecution, InterruptedException { 115 | t.join(); 116 | } 117 | 118 | public void t_join2(Thread t) throws SuspendExecution, InterruptedException { 119 | t.join(1000); 120 | } 121 | 122 | public void t_join3(Thread t) throws SuspendExecution, InterruptedException { 123 | t.join(1, 100); 124 | } 125 | 126 | public void t_lock1(Lock lock) throws SuspendExecution { 127 | lock.lock(); 128 | } 129 | 130 | public void t_lock2(Lock lock) throws SuspendExecution, InterruptedException { 131 | lock.lockInterruptibly(); 132 | } 133 | 134 | public void t_lock3() throws SuspendExecution { 135 | lock(); 136 | } 137 | 138 | public void lock() { 139 | System.out.println("Just a method which have similar signature"); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/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 de.matthiasmann.continuations.Coroutine; 32 | import de.matthiasmann.continuations.instrument.MethodDatabase.ClassEntry; 33 | import java.util.ArrayList; 34 | import java.util.List; 35 | import org.objectweb.asm.AnnotationVisitor; 36 | import org.objectweb.asm.ClassVisitor; 37 | import org.objectweb.asm.MethodVisitor; 38 | import org.objectweb.asm.Opcodes; 39 | import org.objectweb.asm.Type; 40 | import org.objectweb.asm.tree.MethodNode; 41 | import org.objectweb.asm.tree.analysis.AnalyzerException; 42 | 43 | /** 44 | * Instrument a class by instrumenting all suspendable methods and copying the others. 45 | * 46 | * @author Matthias Mann 47 | */ 48 | public class InstrumentClass extends ClassVisitor { 49 | 50 | static final String COROUTINE_NAME = Type.getInternalName(Coroutine.class); 51 | static final String ALREADY_INSTRUMENTED_NAME = Type.getDescriptor(AlreadyInstrumented.class); 52 | 53 | private final MethodDatabase db; 54 | private final boolean forceInstrumentation; 55 | private String className; 56 | private ClassEntry classEntry; 57 | private boolean alreadyInstrumented; 58 | private ArrayList methods; 59 | 60 | public InstrumentClass(ClassVisitor cv, MethodDatabase db, boolean forceInstrumentation) { 61 | super(Opcodes.ASM4, cv); 62 | 63 | this.db = db; 64 | this.forceInstrumentation = forceInstrumentation; 65 | } 66 | 67 | @Override 68 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 69 | this.className = name; 70 | this.classEntry = new ClassEntry(superName); 71 | 72 | // need atleast 1.5 for annotations to work 73 | if(version < Opcodes.V1_5) { 74 | version = Opcodes.V1_5; 75 | } 76 | 77 | super.visit(version, access, name, signature, superName, interfaces); 78 | } 79 | 80 | @Override 81 | public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 82 | if(desc.equals(InstrumentClass.ALREADY_INSTRUMENTED_NAME)) { 83 | alreadyInstrumented = true; 84 | } 85 | return super.visitAnnotation(desc, visible); 86 | } 87 | 88 | @Override 89 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 90 | boolean suspendable = CheckInstrumentationVisitor.checkExceptions(exceptions); 91 | classEntry.set(name, desc, suspendable); 92 | 93 | if(suspendable && checkAccess(access) && !(className.equals(COROUTINE_NAME) && name.equals("yield"))) { 94 | if(db.isDebug()) { 95 | db.log(LogLevel.INFO, "Instrumenting method %s#%s", className, name); 96 | } 97 | 98 | if(methods == null) { 99 | methods = new ArrayList(); 100 | } 101 | 102 | MethodNode mn = new MethodNode(access, name, desc, signature, exceptions); 103 | methods.add(mn); 104 | return mn; 105 | } 106 | return super.visitMethod(access, name, desc, signature, exceptions); 107 | } 108 | 109 | @Override 110 | @SuppressWarnings("CallToThreadDumpStack") 111 | public void visitEnd() { 112 | db.recordSuspendableMethods(className, classEntry); 113 | 114 | if(methods != null) { 115 | if(alreadyInstrumented && !forceInstrumentation) { 116 | for(MethodNode mn : methods) { 117 | mn.accept(makeOutMV(mn)); 118 | } 119 | } else { 120 | if(!alreadyInstrumented) { 121 | super.visitAnnotation(ALREADY_INSTRUMENTED_NAME, true); 122 | } 123 | 124 | for(MethodNode mn : methods) { 125 | MethodVisitor outMV = makeOutMV(mn); 126 | try { 127 | InstrumentMethod im = new InstrumentMethod(db, className, mn); 128 | if(im.collectCodeBlocks()) { 129 | if(mn.name.charAt(0) == '<') { 130 | throw new UnableToInstrumentException("special method", className, mn.name, mn.desc); 131 | } 132 | im.accept(outMV); 133 | } else { 134 | mn.accept(outMV); 135 | } 136 | } catch(AnalyzerException ex) { 137 | ex.printStackTrace(); 138 | throw new InternalError(ex.getMessage()); 139 | } 140 | } 141 | } 142 | } 143 | super.visitEnd(); 144 | } 145 | 146 | private MethodVisitor makeOutMV(MethodNode mn) { 147 | return super.visitMethod(mn.access, mn.name, mn.desc, mn.signature, toStringArray(mn.exceptions)); 148 | } 149 | 150 | private static boolean checkAccess(int access) { 151 | return (access & (Opcodes.ACC_ABSTRACT | Opcodes.ACC_NATIVE)) == 0; 152 | } 153 | 154 | private static String[] toStringArray(List l) { 155 | if(l.isEmpty()) { 156 | return null; 157 | } 158 | return l.toArray(new String[l.size()]); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/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 ; i 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/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 ; iInstrumentation ANT task

49 | * 50 | *

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

52 | *

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

55 | * 56 | *

The following options can be set:

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

63 | * 64 | * @see ANT FileSet 65 | * @see SuspendExecution 66 | * @author Matthias Mann 67 | */ 68 | public class InstrumentationTask extends Task { 69 | 70 | private ArrayList filesets = new ArrayList(); 71 | private boolean check; 72 | private boolean verbose; 73 | private boolean allowMonitors; 74 | private boolean allowBlocking; 75 | private boolean debug; 76 | private boolean writeClasses = true; 77 | 78 | public void addFileSet(FileSet fs) { 79 | filesets.add(fs); 80 | } 81 | 82 | public void setCheck(boolean check) { 83 | this.check = check; 84 | } 85 | 86 | public void setVerbose(boolean verbose) { 87 | this.verbose = verbose; 88 | } 89 | 90 | public void setAllowMonitors(boolean allowMonitors) { 91 | this.allowMonitors = allowMonitors; 92 | } 93 | 94 | public void setAllowBlocking(boolean allowBlocking) { 95 | this.allowBlocking = allowBlocking; 96 | } 97 | 98 | public void setDebug(boolean debug) { 99 | this.debug = debug; 100 | } 101 | 102 | public void setWriteClasses(boolean writeClasses) { 103 | this.writeClasses = writeClasses; 104 | } 105 | 106 | @Override 107 | public void execute() throws BuildException { 108 | MethodDatabase db = new MethodDatabase(getClass().getClassLoader()); 109 | 110 | db.setVerbose(verbose); 111 | db.setDebug(debug); 112 | db.setAllowMonitors(allowMonitors); 113 | db.setAllowBlocking(allowBlocking); 114 | db.setLog(new Log() { 115 | public void log(LogLevel level, String msg, Object... args) { 116 | int msgLevel; 117 | switch(level) { 118 | case DEBUG: msgLevel = Project.MSG_INFO; break; 119 | case INFO: msgLevel = Project.MSG_INFO; break; 120 | case WARNING: msgLevel = Project.MSG_WARN; break; 121 | default: throw new AssertionError("Unhandled log level: " + level); 122 | } 123 | InstrumentationTask.this.log(level+": "+String.format(msg, args), msgLevel); 124 | } 125 | public void error(String msg, Exception ex) { 126 | InstrumentationTask.this.log("ERROR: "+msg, ex, Project.MSG_ERR); 127 | } 128 | }); 129 | 130 | try { 131 | for(FileSet fs : filesets) { 132 | DirectoryScanner ds = fs.getDirectoryScanner(getProject()); 133 | String[] includedFiles = ds.getIncludedFiles(); 134 | 135 | for(String filename : includedFiles) { 136 | if(filename.endsWith(".class")) { 137 | File file = new File(fs.getDir(), filename); 138 | if(file.isFile()) { 139 | db.checkClass(file); 140 | } else { 141 | log("File not found: " + filename); 142 | } 143 | } 144 | } 145 | } 146 | 147 | db.log(LogLevel.INFO, "Instrumenting " + db.getWorkList().size() + " classes"); 148 | 149 | for(File f : db.getWorkList()) { 150 | instrumentClass(db, f); 151 | } 152 | } catch (UnableToInstrumentException ex) { 153 | log(ex.getMessage()); 154 | throw new BuildException(ex.getMessage(), ex); 155 | } 156 | } 157 | 158 | private void instrumentClass(MethodDatabase db, File f) { 159 | db.log(LogLevel.INFO, "Instrumenting class %s", f); 160 | 161 | try { 162 | ClassReader r; 163 | 164 | FileInputStream fis = new FileInputStream(f); 165 | try { 166 | r = new ClassReader(fis); 167 | } finally { 168 | fis.close(); 169 | } 170 | 171 | ClassWriter cw = new DBClassWriter(db, r); 172 | ClassVisitor cv = check ? new CheckClassAdapter(cw) : cw; 173 | InstrumentClass ic = new InstrumentClass(cv, db, false); 174 | r.accept(ic, ClassReader.SKIP_FRAMES); 175 | 176 | byte[] newClass = cw.toByteArray(); 177 | 178 | if(writeClasses) { 179 | FileOutputStream fos = new FileOutputStream(f); 180 | try { 181 | fos.write(newClass); 182 | } finally { 183 | fos.close(); 184 | } 185 | } 186 | } catch (IOException ex) { 187 | throw new BuildException("Instrumenting file " + f, ex); 188 | } 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/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 | 34 | /** 35 | *

A Coroutine is used to run a CoroutineProto.

36 | *

It also provides a function to suspend a running Coroutine.

37 | * 38 | *

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

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

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

135 | * 136 | *

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

139 | * 140 | * @return The current state of this Coroutine 141 | * @see #run() 142 | */ 143 | public State getState() { 144 | return state; 145 | } 146 | 147 | /** 148 | * Runs the Coroutine until it is finished or suspended. This method must only 149 | * be called when the Coroutine is in the states NEW or SUSPENDED. It is not 150 | * multi threading safe. 151 | * 152 | * @throws java.lang.IllegalStateException if the Coroutine is currently running or already finished. 153 | */ 154 | public void run() throws IllegalStateException { 155 | if(state != State.NEW && state != State.SUSPENDED) { 156 | throw new IllegalStateException("Not new or suspended"); 157 | } 158 | State result = State.FINISHED; 159 | Stack oldStack = Stack.getStack(); 160 | try { 161 | state = State.RUNNING; 162 | Stack.setStack(stack); 163 | try { 164 | proto.coExecute(); 165 | } catch (SuspendExecution ex) { 166 | assert ex == SuspendExecution.instance; 167 | result = State.SUSPENDED; 168 | //stack.dump(); 169 | stack.resumeStack(); 170 | } 171 | } finally { 172 | Stack.setStack(oldStack); 173 | state = result; 174 | } 175 | } 176 | 177 | private void writeObject(java.io.ObjectOutputStream out) throws IOException { 178 | if(state == State.RUNNING) { 179 | throw new IllegalStateException("trying to serialize a running coroutine"); 180 | } 181 | out.defaultWriteObject(); 182 | } 183 | 184 | @SuppressWarnings("unchecked") 185 | private boolean isInstrumented(CoroutineProto proto) { 186 | try { 187 | Class clz = Class.forName("de.matthiasmann.continuations.instrument.AlreadyInstrumented"); 188 | return proto.getClass().isAnnotationPresent(clz); 189 | } catch (ClassNotFoundException ex) { 190 | return true; // can't check 191 | } catch (Throwable ex) { 192 | return true; // it's just a check - make sure we don't fail if something goes wrong 193 | } 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/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 | import org.objectweb.asm.ClassReader; 38 | 39 | /** 40 | *

Collects information about classes and their suspendable methods.

41 | *

Provides access to configuration parameters and to logging

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

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

269 | *

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

271 | * @param className the class the needs to be analysed 272 | * @return a new CheckInstrumentationVisitor that has visited the specified 273 | * class or null if the class was not found 274 | */ 275 | protected CheckInstrumentationVisitor checkClass(String className) { 276 | InputStream is = cl.getResourceAsStream(className + ".class"); 277 | if(is != null) { 278 | return checkFileAndClose(is, className); 279 | } 280 | return null; 281 | } 282 | 283 | private CheckInstrumentationVisitor checkFileAndClose(InputStream is, String name) { 284 | try { 285 | try { 286 | ClassReader r = new ClassReader(is); 287 | 288 | CheckInstrumentationVisitor civ = new CheckInstrumentationVisitor(); 289 | r.accept(civ, ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES|ClassReader.SKIP_CODE); 290 | 291 | return civ; 292 | } finally { 293 | is.close(); 294 | } 295 | } catch (UnableToInstrumentException ex) { 296 | throw ex; 297 | } catch (Exception ex) { 298 | error(name, ex); 299 | } 300 | return null; 301 | } 302 | 303 | private String extractSuperClass(String className) { 304 | InputStream is = cl.getResourceAsStream(className + ".class"); 305 | if(is != null) { 306 | try { 307 | try { 308 | ClassReader r = new ClassReader(is); 309 | ExtractSuperClass esc = new ExtractSuperClass(); 310 | r.accept(esc, ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES); 311 | return esc.superClass; 312 | } finally { 313 | is.close(); 314 | } 315 | } catch(IOException ex) { 316 | error(className, ex); 317 | } 318 | } 319 | return null; 320 | } 321 | 322 | private ArrayList getSuperClasses(String className) { 323 | ArrayList result = new ArrayList(); 324 | for(;;) { 325 | result.add(0, className); 326 | if("java/lang/Object".equals(className)) { 327 | return result; 328 | } 329 | 330 | String superClass = getDirectSuperClass(className); 331 | if(superClass == null) { 332 | log(LogLevel.WARNING, "Can't determine super class of %s", className); 333 | return null; 334 | } 335 | className = superClass; 336 | } 337 | } 338 | 339 | protected String getDirectSuperClass(String className) { 340 | ClassEntry entry = getClassEntry(className); 341 | if(entry != null && entry != CLASS_NOT_FOUND) { 342 | return entry.superName; 343 | } 344 | 345 | String superClass; 346 | synchronized(this) { 347 | superClass = superClasses.get(className); 348 | } 349 | if(superClass == null) { 350 | superClass = extractSuperClass(className); 351 | if(superClass != null) { 352 | String oldSuperClass; 353 | synchronized (this) { 354 | oldSuperClass = superClasses.put(className, superClass); 355 | } 356 | if(oldSuperClass != null) { 357 | if(!oldSuperClass.equals(superClass)) { 358 | log(LogLevel.WARNING, "Duplicate super class entry with different value: %s vs %s", oldSuperClass, superClass); 359 | } 360 | } 361 | } 362 | } 363 | return superClass; 364 | } 365 | 366 | public static boolean isJavaCore(String className) { 367 | return className.startsWith("java/") || className.startsWith("javax/") || 368 | className.startsWith("sun/") || className.startsWith("com/sun/"); 369 | } 370 | 371 | private static final ClassEntry CLASS_NOT_FOUND = new ClassEntry(""); 372 | 373 | static final class ClassEntry { 374 | private final HashMap methods; 375 | final String superName; 376 | 377 | public ClassEntry(String superName) { 378 | this.superName = superName; 379 | this.methods = new HashMap(); 380 | } 381 | 382 | public void set(String name, String desc, boolean suspendable) { 383 | String nameAndDesc = key(name, desc); 384 | methods.put(nameAndDesc, suspendable); 385 | } 386 | 387 | public Boolean check(String name, String desc) { 388 | return methods.get(key(name, desc)); 389 | } 390 | 391 | @Override 392 | public int hashCode() { 393 | return superName.hashCode() * 67 + methods.hashCode(); 394 | } 395 | 396 | @Override 397 | public boolean equals(Object obj) { 398 | if(!(obj instanceof ClassEntry)) { 399 | return false; 400 | } 401 | final ClassEntry other = (ClassEntry)obj; 402 | return superName.equals(other.superName) && methods.equals(other.methods); 403 | } 404 | 405 | private static String key(String methodName, String methodDesc) { 406 | return methodName.concat(methodDesc); 407 | } 408 | } 409 | } 410 | -------------------------------------------------------------------------------- /src/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 de.matthiasmann.continuations.Stack; 32 | import de.matthiasmann.continuations.SuspendExecution; 33 | import java.util.List; 34 | import org.objectweb.asm.Label; 35 | import org.objectweb.asm.MethodVisitor; 36 | import org.objectweb.asm.Opcodes; 37 | import org.objectweb.asm.Type; 38 | import org.objectweb.asm.tree.AbstractInsnNode; 39 | import org.objectweb.asm.tree.AnnotationNode; 40 | import org.objectweb.asm.tree.InsnList; 41 | import org.objectweb.asm.tree.LabelNode; 42 | import org.objectweb.asm.tree.LocalVariableNode; 43 | import org.objectweb.asm.tree.MethodInsnNode; 44 | import org.objectweb.asm.tree.MethodNode; 45 | import org.objectweb.asm.tree.TryCatchBlockNode; 46 | import org.objectweb.asm.tree.analysis.Analyzer; 47 | import org.objectweb.asm.tree.analysis.AnalyzerException; 48 | import org.objectweb.asm.tree.analysis.BasicValue; 49 | import org.objectweb.asm.tree.analysis.Frame; 50 | import org.objectweb.asm.tree.analysis.Value; 51 | 52 | /** 53 | * Instrument a method to allow suspension 54 | * 55 | * @author Matthias Mann 56 | */ 57 | public class InstrumentMethod { 58 | 59 | private static final String STACK_NAME = Type.getInternalName(Stack.class); 60 | 61 | private final MethodDatabase db; 62 | private final String className; 63 | private final MethodNode mn; 64 | private final Frame[] frames; 65 | private final int lvarStack; 66 | private final int firstLocal; 67 | 68 | private FrameInfo[] codeBlocks = new FrameInfo[32]; 69 | private int numCodeBlocks; 70 | private int additionalLocals; 71 | 72 | private boolean warnedAboutMonitors; 73 | private int warnedAboutBlocking; 74 | 75 | private static final BlockingMethod BLOCKING_METHODS[] = { 76 | new BlockingMethod("java/lang/Thread", "sleep", "(J)V", "(JI)V"), 77 | new BlockingMethod("java/lang/Thread", "join", "()V", "(J)V", "(JI)V"), 78 | new BlockingMethod("java/lang/Object", "wait", "()V", "(J)V", "(JI)V"), 79 | new BlockingMethod("java/util/concurrent/locks/Lock", "lock", "()V"), 80 | new BlockingMethod("java/util/concurrent/locks/Lock", "lockInterruptibly", "()V"), 81 | }; 82 | 83 | public InstrumentMethod(MethodDatabase db, String className, MethodNode mn) throws AnalyzerException { 84 | this.db = db; 85 | this.className = className; 86 | this.mn = mn; 87 | 88 | try { 89 | Analyzer a = new TypeAnalyzer(db); 90 | this.frames = a.analyze(className, mn); 91 | this.lvarStack = mn.maxLocals; 92 | this.firstLocal = ((mn.access & Opcodes.ACC_STATIC) == Opcodes.ACC_STATIC) ? 0 : 1; 93 | } catch (UnsupportedOperationException ex) { 94 | throw new AnalyzerException(null, ex.getMessage(), ex); 95 | } 96 | } 97 | 98 | public boolean collectCodeBlocks() { 99 | int numIns = mn.instructions.size(); 100 | 101 | codeBlocks[0] = FrameInfo.FIRST; 102 | for(int i=0 ; i= 0) { 117 | int mask = 1 << blockingId; 118 | if(!db.isAllowBlocking()) { 119 | throw new UnableToInstrumentException("blocking call to " + 120 | min.owner + "#" + min.name + min.desc, className, mn.name, mn.desc); 121 | } else if((warnedAboutBlocking & mask) == 0) { 122 | warnedAboutBlocking |= mask; 123 | db.log(LogLevel.WARNING, "Method %s#%s%s contains potentially blocking call to " + 124 | min.owner + "#" + min.name + min.desc, className, mn.name, mn.desc); 125 | } 126 | } 127 | } 128 | } 129 | } 130 | } 131 | addCodeBlock(null, numIns); 132 | 133 | return numCodeBlocks > 1; 134 | } 135 | 136 | private static int isBlockingCall(MethodInsnNode ins) { 137 | for(int i=0,n=BLOCKING_METHODS.length ; i= fi.endInstruction) { 280 | //System.out.println("i="+i+" start="+start+" end="+end+" split="+splitIdx+ 281 | // " start="+mn.instructions.get(start)+" end="+mn.instructions.get(end)); 282 | 283 | // need to split try/catch around the suspendable call 284 | if(start == fi.endInstruction) { 285 | tcb.start = fi.createAfterLabel(); 286 | } else { 287 | if(end > fi.endInstruction) { 288 | TryCatchBlockNode tcb2 = new TryCatchBlockNode( 289 | fi.createAfterLabel(), 290 | tcb.end, tcb.handler, tcb.type); 291 | mn.tryCatchBlocks.add(i+1, tcb2); 292 | } 293 | 294 | tcb.end = fi.createBeforeLabel(); 295 | } 296 | } 297 | } 298 | } 299 | 300 | private void dumpCodeBlock(MethodVisitor mv, int idx, int skip) { 301 | int start = codeBlocks[idx].endInstruction; 302 | int end = codeBlocks[idx+1].endInstruction; 303 | 304 | for(int i=start+skip ; i".equals(min.name)) { 329 | int argSize = TypeAnalyzer.getNumArguments(min.desc); 330 | Frame frame = frames[i]; 331 | int stackIndex = frame.getStackSize() - argSize - 1; 332 | Value thisValue = frame.getStack(stackIndex); 333 | if(stackIndex >= 1 && 334 | isNewValue(thisValue, true) && 335 | isNewValue(frame.getStack(stackIndex-1), false)) { 336 | NewValue newValue = (NewValue)thisValue; 337 | if(newValue.omitted) { 338 | emitNewAndDup(mv, frame, stackIndex, min); 339 | } 340 | } else { 341 | db.log(LogLevel.WARNING, "Expected to find a NewValue on stack index %d: %s", stackIndex, frame); 342 | } 343 | } 344 | break; 345 | } 346 | ins.accept(mv); 347 | } 348 | } 349 | 350 | private static void dumpParameterAnnotations(MethodVisitor mv, List[] parameterAnnotations, boolean visible) { 351 | for(int i=0 ; i= -1 && value <= 5) { 363 | mv.visitInsn(Opcodes.ICONST_0 + value); 364 | } else if((byte)value == value) { 365 | mv.visitIntInsn(Opcodes.BIPUSH, value); 366 | } else if((short)value == value) { 367 | mv.visitIntInsn(Opcodes.SIPUSH, value); 368 | } else { 369 | mv.visitLdcInsn(value); 370 | } 371 | } 372 | 373 | private void emitNewAndDup(MethodVisitor mv, Frame frame, int stackIndex, MethodInsnNode min) { 374 | int arguments = frame.getStackSize() - stackIndex - 1; 375 | int neededLocals = 0; 376 | for(int i=arguments ; i>=1 ; i--) { 377 | BasicValue v = (BasicValue)frame.getStack(stackIndex+i); 378 | mv.visitVarInsn(v.getType().getOpcode(Opcodes.ISTORE), lvarStack+1+neededLocals); 379 | neededLocals += v.getSize(); 380 | } 381 | db.log(LogLevel.DEBUG, "Inserting NEW & DUP for constructor call %s%s with %d arguments (%d locals)", min.owner, min.desc, arguments, neededLocals); 382 | if(additionalLocals < neededLocals) { 383 | additionalLocals = neededLocals; 384 | } 385 | ((NewValue)frame.getStack(stackIndex-1)).insn.accept(mv); 386 | ((NewValue)frame.getStack(stackIndex )).insn.accept(mv); 387 | for(int i=1 ; i<=arguments ; i++) { 388 | BasicValue v = (BasicValue)frame.getStack(stackIndex+i); 389 | neededLocals -= v.getSize(); 390 | mv.visitVarInsn(v.getType().getOpcode(Opcodes.ILOAD), lvarStack+1+neededLocals); 391 | } 392 | } 393 | 394 | private void emitPopMethod(MethodVisitor mv) { 395 | mv.visitVarInsn(Opcodes.ALOAD, lvarStack); 396 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, STACK_NAME, "popMethod", "()V"); 397 | } 398 | 399 | private void emitStoreState(MethodVisitor mv, int idx, FrameInfo fi) { 400 | Frame f = frames[fi.endInstruction]; 401 | 402 | if(fi.lBefore != null) { 403 | fi.lBefore.accept(mv); 404 | } 405 | 406 | mv.visitVarInsn(Opcodes.ALOAD,lvarStack); 407 | emitConst(mv, idx); 408 | emitConst(mv, fi.numSlots); 409 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, STACK_NAME, "pushMethodAndReserveSpace", "(II)V"); 410 | 411 | for(int i=f.getStackSize() ; i-->0 ;) { 412 | BasicValue v = (BasicValue) f.getStack(i); 413 | if(!isOmitted(v)) { 414 | if(!isNullType(v)) { 415 | int slotIdx = fi.stackSlotIndices[i]; 416 | assert slotIdx >= 0 && slotIdx < fi.numSlots; 417 | emitStoreValue(mv, v, lvarStack, slotIdx); 418 | } else { 419 | db.log(LogLevel.DEBUG, "NULL stack entry: type=%s size=%d", v.getType(), v.getSize()); 420 | mv.visitInsn(Opcodes.POP); 421 | } 422 | } 423 | } 424 | 425 | for(int i=firstLocal ; i= 0 && slotIdx < fi.numSlots; 431 | emitStoreValue(mv, v, lvarStack, slotIdx); 432 | } 433 | } 434 | } 435 | 436 | private void emitRestoreState(MethodVisitor mv, int idx, FrameInfo fi) { 437 | Frame f = frames[fi.endInstruction]; 438 | 439 | for(int i=firstLocal ; i= 0 && slotIdx < fi.numSlots; 444 | emitRestoreValue(mv, v, lvarStack, slotIdx); 445 | mv.visitVarInsn(v.getType().getOpcode(Opcodes.ISTORE), i); 446 | } else if(v != BasicValue.UNINITIALIZED_VALUE) { 447 | mv.visitInsn(Opcodes.ACONST_NULL); 448 | mv.visitVarInsn(Opcodes.ASTORE, i); 449 | } 450 | } 451 | 452 | for(int i=0 ; i= 0 && slotIdx < fi.numSlots; 458 | emitRestoreValue(mv, v, lvarStack, slotIdx); 459 | } else { 460 | mv.visitInsn(Opcodes.ACONST_NULL); 461 | } 462 | } 463 | } 464 | 465 | if(fi.lAfter != null) { 466 | fi.lAfter.accept(mv); 467 | } 468 | } 469 | 470 | private void emitStoreValue(MethodVisitor mv, BasicValue v, int lvarStack, int idx) throws InternalError, IndexOutOfBoundsException { 471 | String desc; 472 | 473 | switch(v.getType().getSort()) { 474 | case Type.OBJECT: 475 | case Type.ARRAY: 476 | desc = "(Ljava/lang/Object;L"+STACK_NAME+";I)V"; 477 | break; 478 | case Type.BOOLEAN: 479 | case Type.BYTE: 480 | case Type.SHORT: 481 | case Type.CHAR: 482 | case Type.INT: 483 | desc = "(IL"+STACK_NAME+";I)V"; 484 | break; 485 | case Type.FLOAT: 486 | desc = "(FL"+STACK_NAME+";I)V"; 487 | break; 488 | case Type.LONG: 489 | desc = "(JL"+STACK_NAME+";I)V"; 490 | break; 491 | case Type.DOUBLE: 492 | desc = "(DL"+STACK_NAME+";I)V"; 493 | break; 494 | default: 495 | throw new InternalError("Unexpected type: " + v.getType()); 496 | } 497 | 498 | mv.visitVarInsn(Opcodes.ALOAD, lvarStack); 499 | emitConst(mv, idx); 500 | mv.visitMethodInsn(Opcodes.INVOKESTATIC, STACK_NAME, "push", desc); 501 | } 502 | 503 | private void emitRestoreValue(MethodVisitor mv, BasicValue v, int lvarStack, int idx) { 504 | mv.visitVarInsn(Opcodes.ALOAD, lvarStack); 505 | emitConst(mv, idx); 506 | 507 | switch(v.getType().getSort()) { 508 | case Type.OBJECT: 509 | String internalName = v.getType().getInternalName(); 510 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, STACK_NAME, "getObject", "(I)Ljava/lang/Object;"); 511 | if(!internalName.equals("java/lang/Object")) { // don't cast to Object ;) 512 | mv.visitTypeInsn(Opcodes.CHECKCAST, internalName); 513 | } 514 | break; 515 | case Type.ARRAY: 516 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, STACK_NAME, "getObject", "(I)Ljava/lang/Object;"); 517 | mv.visitTypeInsn(Opcodes.CHECKCAST, v.getType().getDescriptor()); 518 | break; 519 | case Type.BYTE: 520 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, STACK_NAME, "getInt", "(I)I"); 521 | mv.visitInsn(Opcodes.I2B); 522 | break; 523 | case Type.SHORT: 524 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, STACK_NAME, "getInt", "(I)I"); 525 | mv.visitInsn(Opcodes.I2S); 526 | break; 527 | case Type.CHAR: 528 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, STACK_NAME, "getInt", "(I)I"); 529 | mv.visitInsn(Opcodes.I2C); 530 | break; 531 | case Type.BOOLEAN: 532 | case Type.INT: 533 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, STACK_NAME, "getInt", "(I)I"); 534 | break; 535 | case Type.FLOAT: 536 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, STACK_NAME, "getFloat", "(I)F"); 537 | break; 538 | case Type.LONG: 539 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, STACK_NAME, "getLong", "(I)J"); 540 | break; 541 | case Type.DOUBLE: 542 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, STACK_NAME, "getDouble", "(I)D"); 543 | break; 544 | default: 545 | throw new InternalError("Unexpected type: " + v.getType()); 546 | } 547 | } 548 | 549 | static boolean isNullType(BasicValue v) { 550 | return (v == BasicValue.UNINITIALIZED_VALUE) || 551 | (v.isReference() && v.getType().getInternalName().equals("null")); 552 | } 553 | 554 | static boolean isOmitted(BasicValue v) { 555 | if(v instanceof NewValue) { 556 | return ((NewValue)v).omitted; 557 | } 558 | return false; 559 | } 560 | 561 | static boolean isNewValue(Value v, boolean dupped) { 562 | if(v instanceof NewValue) { 563 | return ((NewValue)v).isDupped == dupped; 564 | } 565 | return false; 566 | } 567 | 568 | static class BlockLabelNode extends LabelNode { 569 | final int idx; 570 | 571 | BlockLabelNode(int idx) { 572 | this.idx = idx; 573 | } 574 | } 575 | 576 | static class FrameInfo { 577 | static final FrameInfo FIRST = new FrameInfo(null, 0, 0, null, null); 578 | 579 | final int endInstruction; 580 | final int numSlots; 581 | final int numObjSlots; 582 | final int[] localSlotIndices; 583 | final int[] stackSlotIndices; 584 | 585 | BlockLabelNode lBefore; 586 | BlockLabelNode lAfter; 587 | 588 | FrameInfo(Frame f, int firstLocal, int endInstruction, InsnList insnList, MethodDatabase db) { 589 | this.endInstruction = endInstruction; 590 | 591 | int idxObj = 0; 592 | int idxPrim = 0; 593 | 594 | if(f != null) { 595 | stackSlotIndices = new int[f.getStackSize()]; 596 | for(int i=0 ; i