Mercurial > lasercutter
comparison src/clojure/asm/commons/LocalVariablesSorter.java @ 10:ef7dbbd6452c
added clojure source goodness
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 21 Aug 2010 06:25:44 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
9:35cf337adfcf | 10:ef7dbbd6452c |
---|---|
1 /*** | |
2 * ASM: a very small and fast Java bytecode manipulation framework | |
3 * Copyright (c) 2000-2005 INRIA, France Telecom | |
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 | |
8 * are met: | |
9 * 1. Redistributions of source code must retain the above copyright | |
10 * notice, this list of conditions and the following disclaimer. | |
11 * 2. 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 * 3. Neither the name of the copyright holders nor the names of its | |
15 * contributors may be used to endorse or promote products derived from | |
16 * this software without specific prior written permission. | |
17 * | |
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
28 * THE POSSIBILITY OF SUCH DAMAGE. | |
29 */ | |
30 package clojure.asm.commons; | |
31 | |
32 import clojure.asm.Label; | |
33 import clojure.asm.MethodAdapter; | |
34 import clojure.asm.MethodVisitor; | |
35 import clojure.asm.Opcodes; | |
36 import clojure.asm.Type; | |
37 | |
38 /** | |
39 * A {@link MethodAdapter} that renumbers local variables in their order of | |
40 * appearance. This adapter allows one to easily add new local variables to a | |
41 * method. It may be used by inheriting from this class, but the preferred way | |
42 * of using it is via delegation: the next visitor in the chain can indeed add | |
43 * new locals when needed by calling {@link #newLocal} on this adapter (this | |
44 * requires a reference back to this {@link LocalVariablesSorter}). | |
45 * | |
46 * @author Chris Nokleberg | |
47 * @author Eugene Kuleshov | |
48 * @author Eric Bruneton | |
49 */ | |
50 public class LocalVariablesSorter extends MethodAdapter{ | |
51 | |
52 private final static Type OBJECT_TYPE = Type.getObjectType("java/lang/Object"); | |
53 | |
54 /** | |
55 * Mapping from old to new local variable indexes. A local variable at index | |
56 * i of size 1 is remapped to 'mapping[2*i]', while a local variable at | |
57 * index i of size 2 is remapped to 'mapping[2*i+1]'. | |
58 */ | |
59 private int[] mapping = new int[40]; | |
60 | |
61 /** | |
62 * Array used to store stack map local variable types after remapping. | |
63 */ | |
64 private Object[] newLocals = new Object[20]; | |
65 | |
66 /** | |
67 * Index of the first local variable, after formal parameters. | |
68 */ | |
69 protected final int firstLocal; | |
70 | |
71 /** | |
72 * Index of the next local variable to be created by {@link #newLocal}. | |
73 */ | |
74 protected int nextLocal; | |
75 | |
76 /** | |
77 * Indicates if at least one local variable has moved due to remapping. | |
78 */ | |
79 private boolean changed; | |
80 | |
81 /** | |
82 * Creates a new {@link LocalVariablesSorter}. | |
83 * | |
84 * @param access access flags of the adapted method. | |
85 * @param desc the method's descriptor (see {@link Type Type}). | |
86 * @param mv the method visitor to which this adapter delegates calls. | |
87 */ | |
88 public LocalVariablesSorter( | |
89 final int access, | |
90 final String desc, | |
91 final MethodVisitor mv){ | |
92 super(mv); | |
93 Type[] args = Type.getArgumentTypes(desc); | |
94 nextLocal = (Opcodes.ACC_STATIC & access) != 0 ? 0 : 1; | |
95 for(int i = 0; i < args.length; i++) | |
96 { | |
97 nextLocal += args[i].getSize(); | |
98 } | |
99 firstLocal = nextLocal; | |
100 } | |
101 | |
102 public void visitVarInsn(final int opcode, final int var){ | |
103 Type type; | |
104 switch(opcode) | |
105 { | |
106 case Opcodes.LLOAD: | |
107 case Opcodes.LSTORE: | |
108 type = Type.LONG_TYPE; | |
109 break; | |
110 | |
111 case Opcodes.DLOAD: | |
112 case Opcodes.DSTORE: | |
113 type = Type.DOUBLE_TYPE; | |
114 break; | |
115 | |
116 case Opcodes.FLOAD: | |
117 case Opcodes.FSTORE: | |
118 type = Type.FLOAT_TYPE; | |
119 break; | |
120 | |
121 case Opcodes.ILOAD: | |
122 case Opcodes.ISTORE: | |
123 type = Type.INT_TYPE; | |
124 break; | |
125 | |
126 case Opcodes.ALOAD: | |
127 case Opcodes.ASTORE: | |
128 type = OBJECT_TYPE; | |
129 break; | |
130 | |
131 // case RET: | |
132 default: | |
133 type = Type.VOID_TYPE; | |
134 } | |
135 mv.visitVarInsn(opcode, remap(var, type)); | |
136 } | |
137 | |
138 public void visitIincInsn(final int var, final int increment){ | |
139 mv.visitIincInsn(remap(var, Type.INT_TYPE), increment); | |
140 } | |
141 | |
142 public void visitMaxs(final int maxStack, final int maxLocals){ | |
143 mv.visitMaxs(maxStack, nextLocal); | |
144 } | |
145 | |
146 public void visitLocalVariable( | |
147 final String name, | |
148 final String desc, | |
149 final String signature, | |
150 final Label start, | |
151 final Label end, | |
152 final int index){ | |
153 int size = "J".equals(desc) || "D".equals(desc) ? 2 : 1; | |
154 int newIndex = remap(index, size); | |
155 mv.visitLocalVariable(name, desc, signature, start, end, newIndex); | |
156 } | |
157 | |
158 public void visitFrame( | |
159 final int type, | |
160 final int nLocal, | |
161 final Object[] local, | |
162 final int nStack, | |
163 final Object[] stack){ | |
164 if(type != Opcodes.F_NEW) | |
165 { // uncompressed frame | |
166 throw new IllegalStateException("ClassReader.accept() should be called with EXPAND_FRAMES flag"); | |
167 } | |
168 | |
169 if(!changed) | |
170 { // optimization for the case where mapping = identity | |
171 mv.visitFrame(type, nLocal, local, nStack, stack); | |
172 return; | |
173 } | |
174 | |
175 // creates a copy of newLocals | |
176 Object[] oldLocals = new Object[newLocals.length]; | |
177 System.arraycopy(newLocals, 0, oldLocals, 0, oldLocals.length); | |
178 | |
179 // copies types from 'local' to 'newLocals' | |
180 // 'newLocals' already contains the variables added with 'newLocal' | |
181 | |
182 int index = 0; // old local variable index | |
183 int number = 0; // old local variable number | |
184 for(; number < nLocal; ++number) | |
185 { | |
186 Object t = local[number]; | |
187 int size = t == Opcodes.LONG || t == Opcodes.DOUBLE ? 2 : 1; | |
188 if(t != Opcodes.TOP) | |
189 { | |
190 setFrameLocal(remap(index, size), t); | |
191 } | |
192 index += size; | |
193 } | |
194 | |
195 // removes TOP after long and double types as well as trailing TOPs | |
196 | |
197 index = 0; | |
198 number = 0; | |
199 for(int i = 0; index < newLocals.length; ++i) | |
200 { | |
201 Object t = newLocals[index++]; | |
202 if(t != null && t != Opcodes.TOP) | |
203 { | |
204 newLocals[i] = t; | |
205 number = i + 1; | |
206 if(t == Opcodes.LONG || t == Opcodes.DOUBLE) | |
207 { | |
208 index += 1; | |
209 } | |
210 } | |
211 else | |
212 { | |
213 newLocals[i] = Opcodes.TOP; | |
214 } | |
215 } | |
216 | |
217 // visits remapped frame | |
218 mv.visitFrame(type, number, newLocals, nStack, stack); | |
219 | |
220 // restores original value of 'newLocals' | |
221 newLocals = oldLocals; | |
222 } | |
223 | |
224 // ------------- | |
225 | |
226 /** | |
227 * Creates a new local variable of the given type. | |
228 * | |
229 * @param type the type of the local variable to be created. | |
230 * @return the identifier of the newly created local variable. | |
231 */ | |
232 public int newLocal(final Type type){ | |
233 Object t; | |
234 switch(type.getSort()) | |
235 { | |
236 case Type.BOOLEAN: | |
237 case Type.CHAR: | |
238 case Type.BYTE: | |
239 case Type.SHORT: | |
240 case Type.INT: | |
241 t = Opcodes.INTEGER; | |
242 break; | |
243 case Type.FLOAT: | |
244 t = Opcodes.FLOAT; | |
245 break; | |
246 case Type.LONG: | |
247 t = Opcodes.LONG; | |
248 break; | |
249 case Type.DOUBLE: | |
250 t = Opcodes.DOUBLE; | |
251 break; | |
252 case Type.ARRAY: | |
253 t = type.getDescriptor(); | |
254 break; | |
255 // case Type.OBJECT: | |
256 default: | |
257 t = type.getInternalName(); | |
258 break; | |
259 } | |
260 int local = nextLocal; | |
261 setLocalType(local, type); | |
262 setFrameLocal(local, t); | |
263 nextLocal += type.getSize(); | |
264 return local; | |
265 } | |
266 | |
267 /** | |
268 * Sets the current type of the given local variable. The default | |
269 * implementation of this method does nothing. | |
270 * | |
271 * @param local a local variable identifier, as returned by {@link #newLocal | |
272 * newLocal()}. | |
273 * @param type the type of the value being stored in the local variable | |
274 */ | |
275 protected void setLocalType(final int local, final Type type){ | |
276 } | |
277 | |
278 private void setFrameLocal(final int local, final Object type){ | |
279 int l = newLocals.length; | |
280 if(local >= l) | |
281 { | |
282 Object[] a = new Object[Math.max(2 * l, local + 1)]; | |
283 System.arraycopy(newLocals, 0, a, 0, l); | |
284 newLocals = a; | |
285 } | |
286 newLocals[local] = type; | |
287 } | |
288 | |
289 private int remap(final int var, final Type type){ | |
290 if(var < firstLocal) | |
291 { | |
292 return var; | |
293 } | |
294 int key = 2 * var + type.getSize() - 1; | |
295 int size = mapping.length; | |
296 if(key >= size) | |
297 { | |
298 int[] newMapping = new int[Math.max(2 * size, key + 1)]; | |
299 System.arraycopy(mapping, 0, newMapping, 0, size); | |
300 mapping = newMapping; | |
301 } | |
302 int value = mapping[key]; | |
303 if(value == 0) | |
304 { | |
305 value = nextLocal + 1; | |
306 mapping[key] = value; | |
307 setLocalType(nextLocal, type); | |
308 nextLocal += type.getSize(); | |
309 } | |
310 if(value - 1 != var) | |
311 { | |
312 changed = true; | |
313 } | |
314 return value - 1; | |
315 } | |
316 | |
317 private int remap(final int var, final int size){ | |
318 if(var < firstLocal || !changed) | |
319 { | |
320 return var; | |
321 } | |
322 int key = 2 * var + size - 1; | |
323 int value = key < mapping.length ? mapping[key] : 0; | |
324 if(value == 0) | |
325 { | |
326 throw new IllegalStateException("Unknown local variable " + var); | |
327 } | |
328 return value - 1; | |
329 } | |
330 } |