View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.bcel;
21  
22  import org.apache.bcel.generic.GOTO;
23  import org.apache.bcel.generic.ILOAD;
24  import org.apache.bcel.generic.InstructionHandle;
25  import org.apache.bcel.generic.InstructionList;
26  import org.apache.bcel.generic.NOP;
27  import org.junit.jupiter.api.Test;
28  
29  /**
30   * Test for https://issues.apache.org/jira/browse/BCEL-267 "Race conditions on static fields in BranchHandle and
31   * InstructionHandle".
32   */
33  class HandleTest {
34  
35      static Throwable exception;
36      static final int MAXI = 100;
37      static final int MAXJ = 1000;
38  
39      /**
40       * Asserts that branch handles can be added an instruction list, without corrupting the list.
41       */
42      static void branchHandles() {
43          for (int i = 0; i < MAXI; i++) {
44              final InstructionList list = new InstructionList();
45              final InstructionHandle start = list.append(new NOP());
46              try {
47                  for (int j = 0; j < MAXJ; j++) {
48                      list.append(new GOTO(start));
49                  }
50                  final InstructionHandle[] instructionHandles = list.getInstructionHandles();
51                  for (int j = 0; j < instructionHandles.length; j++) {
52                      final InstructionHandle handle = instructionHandles[j];
53                      if (j > 0) {
54                          checkLinkage(handle, j);
55                          if (start != ((GOTO) handle.getInstruction()).getTarget()) {
56                              final AssertionError error = new AssertionError("unexpected instruction at index " + j);
57                              exception = error;
58                              throw error;
59                          }
60                      }
61                  }
62                  if (exception != null) {
63                      return;
64                  }
65              } catch (final NullPointerException e) {
66                  System.out.println("NPE at i=" + i);
67                  exception = e;
68                  throw e;
69              }
70              list.dispose(); // this initializes caching of unused instruction handles
71          }
72      }
73  
74      /**
75       * Assert that opposite next/prev pairs always match.
76       */
77      static void checkLinkage(final InstructionHandle ih, final int index) {
78          final InstructionHandle prev = ih.getPrev();
79          final InstructionHandle next = ih.getNext();
80          if (prev != null && prev.getNext() != ih || next != null && next.getPrev() != ih) {
81              final AssertionError error = new AssertionError("corrupt instruction list at index " + index);
82              exception = error;
83              throw error;
84          }
85      }
86  
87      /**
88       * Asserts that instruction handles can be added an instruction list, without corrupting the list.
89       */
90      static void handles() {
91          for (int i = 0; i < MAXI; i++) {
92              final InstructionList list = new InstructionList();
93              try {
94                  for (int j = 0; j < MAXJ; j++) {
95                      list.append(new ILOAD(j));
96                  }
97                  final InstructionHandle[] instructionHandles = list.getInstructionHandles();
98                  for (int j = 0; j < instructionHandles.length; j++) {
99                      final InstructionHandle handle = instructionHandles[j];
100                     checkLinkage(handle, j);
101                     if (j != ((ILOAD) handle.getInstruction()).getIndex()) {
102                         final AssertionError error = new AssertionError("unexpected instruction at index " + j);
103                         exception = error;
104                         throw error;
105                     }
106                 }
107                 if (exception != null) {
108                     return;
109                 }
110             } catch (final NullPointerException e) {
111                 System.out.println("NPE at i=" + i);
112                 exception = e;
113                 throw e;
114             }
115             list.dispose(); // this initializes caching of unused instruction handles
116         }
117     }
118 
119     /**
120      * Concurrently run the given runnable in two threads.
121      */
122     private void perform(final Runnable r) throws Throwable {
123         exception = null;
124         final Thread t1 = new Thread(r);
125         final Thread t2 = new Thread(r);
126         t1.start();
127         t2.start();
128         t1.join();
129         t2.join();
130         if (exception != null) {
131             throw exception;
132         }
133     }
134 
135     /**
136      * Assert that two independent instruction lists can be modified concurrently. Here: inserting branch instructions.
137      */
138     @Test
139     void testBranchHandle() throws Throwable {
140         perform(HandleTest::branchHandles);
141     }
142 
143     /**
144      * Assert that two independent instruction lists can be modified concurrently. Here: inserting regular instructions.
145      */
146     @Test
147     void testInstructionHandle() throws Throwable {
148         perform(HandleTest::handles);
149     }
150 }