├── 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