Remove exported everywhere.
[SquirrelJME.git] / modules / tool-classfile / src / main / java / net / multiphasicapps / classfile / __StackMapParser__.java
blob2699e4064dae9c33e071ca445c43527f8d848cb5
1 // -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
3 // SquirrelJME
4 // Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
5 // ---------------------------------------------------------------------------
6 // SquirrelJME is under the GNU General Public License v3+, or later.
7 // See license.mkd for licensing and copyright information.
8 // ---------------------------------------------------------------------------
10 package net.multiphasicapps.classfile;
12 import java.io.ByteArrayInputStream;
13 import java.io.DataInputStream;
14 import java.io.IOException;
15 import java.util.LinkedHashMap;
16 import java.util.Map;
18 /**
19 * This class is used to parse the stack map and initialize the initial
20 * snapshot states for jump targets within the method.
22 * @since 2017/04/16
24 final class __StackMapParser__
26 /** The stream to decode from. */
27 protected final DataInputStream in;
29 /** The number of stack entries. */
30 protected final int maxstack;
32 /** The number of local entries. */
33 protected final int maxlocals;
35 /** The method byte code. */
36 protected final ByteCode code;
38 /** Constant pool. */
39 protected final Pool pool;
41 /** This type. */
42 protected final JavaType thistype;
44 /** Verification targets. */
45 private final Map<Integer, StackMapTableState> _targets;
47 /** The next stack state. */
48 private final StackMapTableEntry[] _nextstack;
50 /** The next local variable state. */
51 private final StackMapTableEntry[] _nextlocals;
53 /** The placement address. */
54 private int _placeaddr;
56 /** The top of the stack. */
57 private int _stacktop;
59 /**
60 * Initializes the stack map parser.
62 * @param __p The constant pool.
63 * @param __m The method this code exists within.
64 * @param __new Should the new stack map table format be used?
65 * @param __in The data for the stack map table.
66 * @param __bc The owning byte code.
67 * @param __tt This type.
68 * @throws InvalidClassFormatException If the stack map table is not
69 * valid.
70 * @throws NullPointerException On null arguments.
71 * @since 2017/04/16
73 __StackMapParser__(Pool __p, Method __m, boolean __new, byte[] __in,
74 ByteCode __bc, JavaType __tt)
75 throws InvalidClassFormatException, NullPointerException
77 // Check
78 if (__p == null || __m == null || __in == null || __bc == null ||
79 __tt == null)
80 throw new NullPointerException("NARG");
82 // Set
83 DataInputStream xin;
84 this.in = (xin = new DataInputStream(
85 new ByteArrayInputStream(__in)));
86 int maxstack = __bc.maxStack(),
87 maxlocals = __bc.maxLocals();
88 this.maxstack = maxstack;
89 this.maxlocals = maxlocals;
90 this.code = __bc;
91 this.pool = __p;
92 this.thistype = __tt;
94 // This is used to set which variables appear next before a state is
95 // constructed with them
96 StackMapTableEntry[] nextstack, nextlocals;
97 this._nextstack = (nextstack = new StackMapTableEntry[maxstack]);
98 this._nextlocals = (nextlocals = new StackMapTableEntry[maxlocals]);
100 // Setup initial state
101 // {@squirreljme.error JC43 The arguments that are required for the
102 // given method exceeds the maximum number of permitted local
103 // variables. (The method in question; The required number of local
104 // variables; The maximum number of local variables)}
105 MethodHandle handle = __m.handle();
106 boolean isinstance = !__m.flags().isStatic();
107 JavaType[] jis = handle.javaStack(isinstance);
108 int jn = jis.length;
109 if (jn > maxlocals)
110 throw new InvalidClassFormatException(
111 String.format("JC43 %s %d %d", handle, jn, maxlocals));
113 // Setup entries
114 // If this is an instance initializer method then only the first
115 // argument is not initialized
116 boolean isiinit = isinstance && __m.name().isInstanceInitializer();
117 for (int i = 0; i < jn; i++)
118 nextlocals[i] = new StackMapTableEntry(jis[i],
119 (!isiinit || (i != 0)));
121 // Initialize entries with nothing
122 for (int i = 0, n = nextstack.length; i < n; i++)
123 if (nextstack[i] == null)
124 nextstack[i] = StackMapTableEntry.NOTHING;
125 for (int i = 0, n = nextlocals.length; i < n; i++)
126 if (nextlocals[i] == null)
127 nextlocals[i] = StackMapTableEntry.NOTHING;
129 // Where states go
130 Map<Integer, StackMapTableState> targets = new LinkedHashMap<>();
131 this._targets = targets;
133 // Record state
134 this.__next(0, true, -1, -1);
136 // Parse the stack map table
137 try (DataInputStream in = xin)
139 // Parsing the class stack map table
140 if (!__new)
142 // Read the number of entries in the table
143 int ne = xin.readUnsignedShort();
145 // All entries in the table are full frames
146 for (int i = 0; i < ne; i++)
147 this.__next(this.__oldStyle(), true, -1, i);
150 // The modern stack map table
151 else
153 // Read the number of entries in the table
154 int ne = xin.readUnsignedShort();
156 // Read them all
157 for (int i = 0; i < ne; i++)
159 // Read the frame type
160 int type = xin.readUnsignedByte();
161 int addr;
163 // Full frame?
164 if (type == 255)
165 addr = this.__fullFrame();
167 // Same frame?
168 else if (type >= 0 && type <= 63)
169 addr = this.__sameFrame(type);
171 // Same locals but a single stack item
172 else if (type >= 64 && type <= 127)
173 addr = this.__sameLocalsSingleStack(type - 64);
175 // Same locals, single stack item, explicit delta
176 else if (type == 247)
177 addr = this.__sameLocalsSingleStackExplicit();
179 // Chopped frame
180 else if (type >= 248 && type <= 250)
181 addr = this.__choppedFrame(251 - type);
183 // Same frame but with a supplied delta
184 else if (type == 251)
185 addr = this.__sameFrameDelta();
187 // Appended frame
188 else if (type >= 252 && type <= 254)
189 addr = this.__appendFrame(type - 251);
191 // {@squirreljme.error JC44 Unknown StackMapTable
192 // verification type. (The verification type)}
193 else
194 throw new InvalidClassFormatException(
195 String.format("JC44 %d", type));
197 // Setup next
198 this.__next(addr, false, type, i);
203 // {@squirreljme.error JC45 Failed to parse the stack map table.}
204 catch (IOException e)
206 throw new InvalidClassFormatException("JC45", e);
211 * Returns the stack map table.
213 * @return The parsed stack map table.
214 * @since 2017/10/16
216 public StackMapTable get()
218 return new StackMapTable(this._targets);
222 * Append extra locals to the frame and clear the stack.
224 * @param __addlocs The number of local variables to add.
225 * @return The address offset.
226 * @throws IOException On read errors.
227 * @since 2016/03/26
229 private int __appendFrame(int __addlocs)
230 throws IOException
232 // Get the atom to use
233 DataInputStream in = this.in;
234 int rv = in.readUnsignedShort();
236 // Stack is cleared
237 this._stacktop = 0;
239 // Read in local variables
240 StackMapTableEntry[] nextlocals = this._nextlocals;
241 int n = this.maxlocals;
242 for (int i = 0; __addlocs > 0 && i < n; i++)
244 // Get slot here
245 StackMapTableEntry s = nextlocals[i];
247 // If it is not empty, ignore it
248 if (!s.equals(StackMapTableEntry.NOTHING))
249 continue;
251 // Set it
252 StackMapTableEntry aa;
253 nextlocals[i] = (aa = this.__loadInfo());
254 __addlocs--;
256 // If a wide element was added, then the next one becomes TOP
257 if (aa.isWide())
258 nextlocals[++i] = aa.topType();
261 // Error if added stuff remains
262 // {@squirreljme.error JC46 Appending local variables to the frame
263 // however there is no room to place them. (The remaining local count)}
264 if (__addlocs != 0)
265 throw new InvalidClassFormatException(
266 String.format("JC46 %d", __addlocs));
268 return rv;
272 * Similar frame with no stack and the top few locals removed.
274 * @param __chops The number of variables which get chopped.
275 * @return The address offset.
276 * @throws IOException On read errors.
277 * @since 2016/03/26
279 private int __choppedFrame(int __chops)
280 throws IOException
282 // Get the atom to use
283 DataInputStream in = this.in;
284 int rv = in.readUnsignedShort();
286 // No stack
287 this._stacktop = 0;
289 // Chop off some locals
290 StackMapTableEntry[] nextlocals = this._nextlocals;
291 int i, n = this.maxlocals;
292 for (i = n - 1; __chops > 0 && i >= 0; i--)
294 // Get slot here
295 StackMapTableEntry s = nextlocals[i];
297 // If it is empty, ignore it
298 if (s.equals(StackMapTableEntry.NOTHING))
299 continue;
301 // Clear top off, but only if it is not an undefined top
302 if (s.isTop() && !s.equals(StackMapTableEntry.TOP_UNDEFINED))
303 nextlocals[i--] = StackMapTableEntry.NOTHING;
305 // Clear it
306 nextlocals[i] = StackMapTableEntry.NOTHING;
307 __chops--;
310 // Still chops left?
311 // {@squirreljme.error JC47 Could not chop off all local variables
312 // because there are no variables remaining to be chopped. (The
313 // remaining variables to remove)}
314 if (__chops != 0)
315 throw new InvalidClassFormatException(
316 String.format("JC47 %d", __chops));
318 return rv;
322 * This reads and parses the full stack frame.
324 * @return The address offset.
325 * @throws IOException On read errors.
326 * @since 2016/03/26
328 private int __fullFrame()
329 throws IOException
331 // Get the atom to use
332 DataInputStream in = this.in;
333 int rv = in.readUnsignedShort();
335 // Read in local variables
336 int nl = in.readUnsignedShort();
338 // {@squirreljme.error JC48 The number of specified local variables in
339 // the full frame exceeds the maximum permitted local variable
340 // count. (The read local variable count; The number of locals the
341 // method uses)}
342 int maxlocals = this.maxlocals,
343 maxstack = this.maxstack;
344 if (nl > maxlocals)
345 throw new InvalidClassFormatException(
346 String.format("JC48 %d %d", nl, maxlocals));
347 int i, o;
348 StackMapTableEntry[] nextlocals = this._nextlocals;
349 for (i = 0, o = 0; i < nl; i++)
351 StackMapTableEntry e;
352 nextlocals[o++] = (e = this.__loadInfo());
354 // Add top?
355 if (e.isWide())
356 nextlocals[o++] = e.topType();
358 for (;o < maxlocals; o++)
359 nextlocals[o] = StackMapTableEntry.NOTHING;
361 // Read in stack variables
362 StackMapTableEntry[] nextstack = this._nextstack;
363 int ns = in.readUnsignedShort();
364 for (i = 0, o = 0; i < ns; i++)
366 StackMapTableEntry e;
367 nextstack[o++] = (e = this.__loadInfo());
369 // Add top?
370 if (e.isWide())
371 nextstack[o++] = e.topType();
373 this._stacktop = o;
375 return rv;
379 * Loads type information for the stack.
381 * @return The type which was parsed.
382 * @throws IOException On read errors.
383 * @since 2016/03/26
385 private StackMapTableEntry __loadInfo()
386 throws IOException
388 // Read the tag
389 DataInputStream in = this.in;
390 int tag = in.readUnsignedByte();
392 // Depends on the tag
393 switch (tag)
395 // Top
396 case 0:
397 return StackMapTableEntry.TOP_UNDEFINED;
399 // Integer
400 case 1:
401 return StackMapTableEntry.INTEGER;
403 // Float
404 case 2:
405 return StackMapTableEntry.FLOAT;
407 // Double
408 case 3:
409 return StackMapTableEntry.DOUBLE;
411 // Long
412 case 4:
413 return StackMapTableEntry.LONG;
415 // Nothing
416 case 5:
417 return StackMapTableEntry.NOTHING;
419 // Uninitialized this
420 case 6:
421 return new StackMapTableEntry(this.thistype, false);
423 // Initialized object
424 case 7:
425 return new StackMapTableEntry(new JavaType(
426 this.pool.<ClassName>get(ClassName.class,
427 in.readUnsignedShort()).field()), true);
429 // Uninitialized variable for a new instruction, the pc points
430 // to the new instruction so the class must be read from
431 // that instruction to determine the type of that actual
432 // object
433 case 8:
434 return new StackMapTableEntry(new JavaType(this.pool.
435 <ClassName>get(ClassName.class, this.code.
436 readRawCodeUnsignedShort(in.readUnsignedShort() + 1))),
437 false);
439 // Unknown
440 default:
441 // {@squirreljme.error JC49 The verification tag in the
442 // StackMap/StackMapTable attribute is not valid. (The tag)}
443 throw new InvalidClassFormatException(
444 String.format("JC49 %d", tag));
449 * Initializes the next state.
451 * @param __au The address offset.
452 * @param __abs Absolute position?
453 * @param __type The type of entry that was just handled, this is for
454 * debug purposes.
455 * @param __ne The entry number of this index.
456 * @return The state for the next address.
457 * @since 2016/05/20
459 StackMapTableState __next(int __au, boolean __abs, int __type, int __ne)
461 // Where are we?
462 int naddr = this._placeaddr;
464 // Generate it
465 StackMapTableState rv;
468 rv = new StackMapTableState(this._nextlocals,
469 this._nextstack, this._stacktop);
471 catch (InvalidClassFormatException e)
473 // {@squirreljme.error JC4a Invalid stack map table at the
474 // specified address. (The address offset; Is the address offset
475 // absolute?; The placement address; The type of entry which
476 // was just handled, -1 means it was old-style or initial state.)}
477 throw new InvalidClassFormatException(String.format(
478 "JC4a %d %b %d %d", __au, __abs, naddr, __type), e);
481 // Set new placement address, the first is always absolute
482 int pp = (__abs ? __au :
483 naddr + (__au + (__ne == 0 ? 0 : 1)));
484 this._placeaddr = pp;
486 // {@squirreljme.error JC4b A duplicate stack map information for the
487 // specified address has already been loaded. (The address; The
488 // already existing information; The information to be placed there;
489 // Absolute address?; Current address of parse; The address offset;
490 // The parsed type)}
491 // Note that the first instruction if it is a jump target may have an
492 // explicit state even if it one is always defined implicitly, so
493 // just ignore it
494 Map<Integer, StackMapTableState> targets = this._targets;
495 if (pp != 0 && targets.containsKey(pp))
496 throw new IllegalStateException(String.format(
497 "JC4b %d %s %s %b %d %d %d",
498 pp, targets.get(pp), rv, __abs, naddr, __au, __type));
499 targets.put(pp, rv);
501 // Debug
502 /*Debugging.debugNote("Read state @%d: %s%n", pp, rv);*/
504 // The stored state
505 return rv;
509 * Reads in an old style full frame.
511 * @return The address information.
512 * @throws IOException On read errors.
513 * @since 2016/03/26
515 private int __oldStyle()
516 throws IOException
518 // Get the atom to use
519 DataInputStream in = this.in;
520 int rv = in.readUnsignedShort();
522 // Read in local variables
523 int nl = in.readUnsignedShort();
524 StackMapTableEntry[] inlocals = new StackMapTableEntry[nl];
525 for (int i = 0; i < nl; i++)
526 inlocals[i] = this.__loadInfo();
528 // Read in stack variables
529 int ns = in.readUnsignedShort();
530 StackMapTableEntry[] instack = new StackMapTableEntry[ns];
531 for (int i = 0; i < ns; i++)
532 instack[i] = this.__loadInfo();
534 // Assign read local variables
535 int lat = 0;
536 StackMapTableEntry[] nextlocals = this._nextlocals;
537 for (int i = 0; i < nl; i++)
539 // Copy in
540 StackMapTableEntry e = inlocals[i];
541 nextlocals[lat++] = e;
543 // Handling wide type?
544 if (e.isWide())
546 // Set top
547 nextlocals[lat++] = e.topType();
549 // If the top is explicit, then skip it
550 if (i + 1 < nl && inlocals[i + 1].isTop())
551 i++;
555 // Assign read stack variables
556 int sat = 0;
557 StackMapTableEntry[] nextstack = this._nextstack;
558 for (int i = 0; i < ns; i++)
560 // Copy in
561 StackMapTableEntry e = instack[i];
562 nextstack[sat++] = e;
564 // Handling wide type?
565 if (e.isWide())
567 // Set top
568 nextstack[sat++] = e.topType();
570 // If the top is explicit, then skip it
571 if (i + 1 < ns && instack[i + 1].isTop())
572 i++;
576 // Stack depth is where the next stack would have been placed
577 this._stacktop = sat;
579 return rv;
583 * The same frame is used with no changes.
585 * @param __delta The offset from the earlier offset.
586 * @return The address information.
587 * @since 2016/03/26
589 private int __sameFrame(int __delta)
591 return __delta;
595 * Same frame but with a supplied delta rather than using it with the type.
597 * @return The address information.
598 * @throws IOException On read errors.
599 * @since 2016/03/26
601 private int __sameFrameDelta()
602 throws IOException
604 return this.in.readUnsignedShort();
608 * Same locals but the stack has only a single entry.
610 * @param __delta The delta offset.
611 * @return The address information.
612 * @throws IOException On read errors.
613 * @since 2016/03/26
615 private int __sameLocalsSingleStack(int __delta)
616 throws IOException
618 // Load single entry
619 StackMapTableEntry ent;
620 this._nextstack[0] = (ent = this.__loadInfo());
622 // If the entry is wide then the top type will not be specified as it
623 // will be implicit, so we need to set the according type
624 if (ent.isWide())
626 this._nextstack[1] = ent.topType();
627 this._stacktop = 2;
630 // Only a single entry exists
631 else
632 this._stacktop = 1;
634 return __delta;
638 * Same locals but the stack has only a single entry, the delta offset
639 * is specified.
641 * @return The address information.
642 * @throws IOException On read errors.
643 * @since 2016/03/26
645 private int __sameLocalsSingleStackExplicit()
646 throws IOException
648 return this.__sameLocalsSingleStack(this.in.readUnsignedShort());