View Javadoc
1   package io.jawk.intermediate;
2   
3   /*-
4    * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
5    * Jawk
6    * ჻჻჻჻჻჻
7    * Copyright (C) 2006 - 2026 MetricsHub
8    * ჻჻჻჻჻჻
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation, either version 3 of the
12   * License, or (at your option) any later version.
13   *
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Lesser Public License for more details.
18   *
19   * You should have received a copy of the GNU General Lesser Public
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
22   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
23   */
24  
25  import java.io.PrintStream;
26  import java.io.Serializable;
27  import java.util.ArrayDeque;
28  import java.util.ArrayList;
29  import java.util.Arrays;
30  import java.util.Collections;
31  import java.util.Deque;
32  import java.util.HashMap;
33  import java.util.HashSet;
34  import java.util.IdentityHashMap;
35  import java.util.List;
36  import java.util.Map;
37  import java.util.Set;
38  import java.util.function.Supplier;
39  import java.util.regex.Pattern;
40  import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
41  import io.jawk.ext.ExtensionFunction;
42  import io.jawk.jrt.JRT;
43  
44  /**
45   * <p>
46   * AwkTuples class.
47   * </p>
48   *
49   * @author Danny Daglas
50   */
51  public class AwkTuples implements Serializable {
52  
53  	private static final long serialVersionUID = 3L;
54  
55  	/** Address manager */
56  	private final AddressManager addressManager = new AddressManager();
57  
58  	/** Description of the primary script source, used for runtime diagnostics. */
59  	private String sourceDescription;
60  
61  	/**
62  	 * Records the description of the primary script source (typically its file
63  	 * name) so runtime diagnostics can point at it.
64  	 *
65  	 * @param sourceDescriptionParam script source description
66  	 */
67  	public void setSourceDescription(String sourceDescriptionParam) {
68  		this.sourceDescription = sourceDescriptionParam;
69  	}
70  
71  	/**
72  	 * Returns the description of the primary script source.
73  	 *
74  	 * @return script source description, or {@code null} when unknown
75  	 */
76  	public String getSourceDescription() {
77  		return sourceDescription;
78  	}
79  
80  	// made public to access static members of AwkTuples via Java Reflection
81  
82  	// made public to be accessable via Java Reflection
83  	// (see toOpcodeString() method below)
84  
85  	/**
86  	 * Override add() to populate the line number for each tuple,
87  	 * rather than polluting all the constructors with this assignment.
88  	 */
89  	/**
90  	 * The tuple queue intentionally uses an {@link ArrayList}. The address mapping
91  	 * logic stores tuple indexes (rather than node references) so that jump targets
92  	 * can be serialized and patched efficiently. A linked list would make every
93  	 * lookup O(n) and complicate address reassignment.
94  	 */
95  	private List<Tuple> queue = new ArrayList<Tuple>(100) {
96  		private static final long serialVersionUID = -6334362156408598578L;
97  
98  		@Override
99  		public boolean add(Tuple t) {
100 			t.setLineNumber(linenoStack.peek());
101 			return super.add(t);
102 		}
103 	};
104 
105 	/** Whether tuple post-processing has already been applied. */
106 	private boolean postProcessed;
107 
108 	/** Whether optimization passes have already been applied. */
109 	private boolean optimized;
110 
111 	/** Whether this tuple stream was produced by {@code compileExpression()}. */
112 	private boolean evalTupleStream;
113 
114 	/**
115 	 * Address of the END blocks section, where a runtime {@code exit} jumps;
116 	 * {@code null} for expression streams. Property addresses are remapped
117 	 * explicitly by the optimizer and seeded as reachability roots, so they
118 	 * stay valid even when no tuple references them.
119 	 */
120 	private Address exitAddress;
121 
122 	/**
123 	 * Address of the ENDFILE section, or {@code null} when the program has
124 	 * no BEGINFILE/ENDFILE rules.
125 	 */
126 	private Address endFileAddress;
127 
128 	/**
129 	 * Address of the {@code NEXT_FILE} tuple that opens each input file, or
130 	 * {@code null} when the program does not use per-file input stepping.
131 	 */
132 	private Address nextFileAddress;
133 
134 	/**
135 	 * <p>
136 	 * toOpcodeString.
137 	 * </p>
138 	 *
139 	 * @param opcode a int
140 	 * @return a {@link java.lang.String} object
141 	 */
142 	public static String toOpcodeString(int opcode) {
143 		return Opcode.fromId(opcode).name();
144 	}
145 
146 	/**
147 	 * <p>
148 	 * pop.
149 	 * </p>
150 	 */
151 	public void pop() {
152 		queue.add(new Tuple.NoOperandTuple(Opcode.POP));
153 	}
154 
155 	/**
156 	 * Discards a value that was evaluated in scalar context.
157 	 */
158 	public void popScalar() {
159 		queue.add(new Tuple.ScalarPopTuple());
160 	}
161 
162 	/**
163 	 * <p>
164 	 * push.
165 	 * </p>
166 	 *
167 	 * @param o a {@link java.lang.Object} object
168 	 */
169 	public void push(Object o) {
170 		if (o instanceof String) {
171 			queue.add(new Tuple.PushStringTuple(o.toString()));
172 		} else if (o instanceof Integer) {
173 			queue.add(new Tuple.PushLongTuple((long) (Integer) o));
174 		} else if (o instanceof Long) {
175 			queue.add(new Tuple.PushLongTuple((long) (Long) o));
176 		} else if (o instanceof Double) {
177 			queue.add(new Tuple.PushDoubleTuple((Double) o));
178 		}
179 	}
180 
181 	/**
182 	 * <p>
183 	 * ifFalse.
184 	 * </p>
185 	 *
186 	 * @param address a {@link io.jawk.intermediate.Address} object
187 	 */
188 	public void ifFalse(Address address) {
189 		queue.add(new Tuple.AddressTuple(Opcode.IFFALSE, address));
190 	}
191 
192 	/**
193 	 * <p>
194 	 * toNumber.
195 	 * </p>
196 	 */
197 	public void toNumber() {
198 		queue.add(new Tuple.NoOperandTuple(Opcode.TO_NUMBER));
199 	}
200 
201 	/**
202 	 * <p>
203 	 * ifTrue.
204 	 * </p>
205 	 *
206 	 * @param address a {@link io.jawk.intermediate.Address} object
207 	 */
208 	public void ifTrue(Address address) {
209 		queue.add(new Tuple.AddressTuple(Opcode.IFTRUE, address));
210 	}
211 
212 	/**
213 	 * <p>
214 	 * gotoAddress.
215 	 * </p>
216 	 *
217 	 * @param address a {@link io.jawk.intermediate.Address} object
218 	 */
219 	public void gotoAddress(Address address) {
220 		queue.add(new Tuple.AddressTuple(Opcode.GOTO, address));
221 	}
222 
223 	/**
224 	 * <p>
225 	 * createAddress.
226 	 * </p>
227 	 *
228 	 * @param label a {@link java.lang.String} object
229 	 * @return a {@link io.jawk.intermediate.Address} object
230 	 */
231 	public Address createAddress(String label) {
232 		return addressManager.createAddress(label);
233 	}
234 
235 	/**
236 	 * <p>
237 	 * address.
238 	 * </p>
239 	 *
240 	 * @param address a {@link io.jawk.intermediate.Address} object
241 	 * @return a {@link io.jawk.intermediate.AwkTuples} object
242 	 */
243 	public AwkTuples address(Address address) {
244 		addressManager.resolveAddress(address, queue.size());
245 		return this;
246 	}
247 
248 	/**
249 	 * <p>
250 	 * nop.
251 	 * </p>
252 	 */
253 	public void nop() {
254 		queue.add(new Tuple.NoOperandTuple(Opcode.NOP));
255 	}
256 
257 	/**
258 	 * <p>
259 	 * print.
260 	 * </p>
261 	 *
262 	 * @param numExprs a int
263 	 */
264 	public void print(int numExprs) {
265 		queue.add(new Tuple.CountTuple(Opcode.PRINT, numExprs));
266 	}
267 
268 	/**
269 	 * <p>
270 	 * printToFile.
271 	 * </p>
272 	 *
273 	 * @param numExprs a int
274 	 * @param append a boolean
275 	 */
276 	public void printToFile(int numExprs, boolean append) {
277 		queue.add(new Tuple.CountAndAppendTuple(Opcode.PRINT_TO_FILE, numExprs, append));
278 	}
279 
280 	/**
281 	 * <p>
282 	 * printToPipe.
283 	 * </p>
284 	 *
285 	 * @param numExprs a int
286 	 */
287 	public void printToPipe(int numExprs) {
288 		queue.add(new Tuple.CountTuple(Opcode.PRINT_TO_PIPE, numExprs));
289 	}
290 
291 	/**
292 	 * <p>
293 	 * printf.
294 	 * </p>
295 	 *
296 	 * @param numExprs a int
297 	 */
298 	public void printf(int numExprs) {
299 		queue.add(new Tuple.CountTuple(Opcode.PRINTF, numExprs));
300 	}
301 
302 	/**
303 	 * <p>
304 	 * printfToFile.
305 	 * </p>
306 	 *
307 	 * @param numExprs a int
308 	 * @param append a boolean
309 	 */
310 	public void printfToFile(int numExprs, boolean append) {
311 		queue.add(new Tuple.CountAndAppendTuple(Opcode.PRINTF_TO_FILE, numExprs, append));
312 	}
313 
314 	/**
315 	 * <p>
316 	 * printfToPipe.
317 	 * </p>
318 	 *
319 	 * @param numExprs a int
320 	 */
321 	public void printfToPipe(int numExprs) {
322 		queue.add(new Tuple.CountTuple(Opcode.PRINTF_TO_PIPE, numExprs));
323 	}
324 
325 	/**
326 	 * <p>
327 	 * sprintf.
328 	 * </p>
329 	 *
330 	 * @param numExprs a int
331 	 */
332 	public void sprintf(int numExprs) {
333 		queue.add(new Tuple.CountTuple(Opcode.SPRINTF, numExprs));
334 	}
335 
336 	/**
337 	 * <p>
338 	 * length.
339 	 * </p>
340 	 *
341 	 * @param numExprs a int
342 	 */
343 	public void length(int numExprs) {
344 		queue.add(new Tuple.CountTuple(Opcode.LENGTH, numExprs));
345 	}
346 
347 	/**
348 	 * <p>
349 	 * concat.
350 	 * </p>
351 	 */
352 	public void concat() {
353 		queue.add(new Tuple.NoOperandTuple(Opcode.CONCAT));
354 	}
355 
356 	/**
357 	 * <p>
358 	 * assign.
359 	 * </p>
360 	 *
361 	 * @param offset a int
362 	 * @param isGlobal a boolean
363 	 */
364 	public void assign(int offset, boolean isGlobal) {
365 		queue.add(new Tuple.VariableTuple(Opcode.ASSIGN, offset, isGlobal));
366 	}
367 
368 	/**
369 	 * <p>
370 	 * assignArray.
371 	 * </p>
372 	 *
373 	 * @param offset a int
374 	 * @param isGlobal a boolean
375 	 */
376 	public void assignArray(int offset, boolean isGlobal) {
377 		queue.add(new Tuple.VariableTuple(Opcode.ASSIGN_ARRAY, offset, isGlobal));
378 	}
379 
380 	/**
381 	 * Assigns a value to a stack-provided associative-array element.
382 	 */
383 	public void assignMapElement() {
384 		queue.add(new Tuple.NoOperandTuple(Opcode.ASSIGN_MAP_ELEMENT));
385 	}
386 
387 	/**
388 	 * <p>
389 	 * assignAsInput.
390 	 * </p>
391 	 */
392 	public void assignAsInput() {
393 		queue.add(new Tuple.NoOperandTuple(Opcode.ASSIGN_AS_INPUT));
394 	}
395 
396 	/**
397 	 * Marks this tuple stream as an expression-eval program rather than a full
398 	 * AWK script. Eval tuple streams can use small tuple-level optimizations that
399 	 * are unsafe for the general case.
400 	 */
401 	public void markEvalTupleStream() {
402 		evalTupleStream = true;
403 	}
404 
405 	/**
406 	 * <p>
407 	 * assignAsInputField.
408 	 * </p>
409 	 */
410 	public void assignAsInputField() {
411 		queue.add(new Tuple.NoOperandTuple(Opcode.ASSIGN_AS_INPUT_FIELD));
412 	}
413 
414 	/**
415 	 * <p>
416 	 * dereference.
417 	 * </p>
418 	 *
419 	 * @param offset a int
420 	 * @param isArray a boolean
421 	 * @param isGlobal a boolean
422 	 */
423 	public void dereference(int offset, boolean isArray, boolean isGlobal) {
424 		queue.add(new Tuple.DereferenceTuple(offset, isArray, isGlobal));
425 	}
426 
427 	/**
428 	 * Emits a variable read that does not assign a blank value when the variable
429 	 * is still untyped.
430 	 * <p>
431 	 * This is used by extension functions such as gawk's {@code typeof()} that
432 	 * need the current lvalue state, not AWK's normal scalar autovivification side
433 	 * effect.
434 	 * </p>
435 	 *
436 	 * @param offset variable offset
437 	 * @param isGlobal whether the variable is global
438 	 */
439 	public void peekDereference(int offset, boolean isGlobal) {
440 		queue.add(new Tuple.VariableTuple(Opcode.PEEK_DEREFERENCE, offset, isGlobal));
441 	}
442 
443 	/**
444 	 * Emits a variable reference whose scalar value is captured immediately while
445 	 * retaining the variable location for a runtime-selected array parameter.
446 	 *
447 	 * @param offset variable offset
448 	 * @param isGlobal whether the variable is global
449 	 */
450 	public void pushIndirectArgument(int offset, boolean isGlobal) {
451 		queue.add(new Tuple.VariableTuple(Opcode.PUSH_INDIRECT_ARGUMENT, offset, isGlobal));
452 	}
453 
454 	/**
455 	 * Emits an indirect-call subarray argument after its containing map and key.
456 	 */
457 	public void pushIndirectArrayArgument() {
458 		queue.add(new Tuple.NoOperandTuple(Opcode.PUSH_INDIRECT_ARRAY_ARGUMENT));
459 	}
460 
461 	/**
462 	 * <p>
463 	 * plusEq.
464 	 * </p>
465 	 *
466 	 * @param offset a int
467 	 * @param isGlobal a boolean
468 	 */
469 	public void plusEq(int offset, boolean isGlobal) {
470 		queue.add(new Tuple.CompoundAssignTuple(Opcode.PLUS_EQ, offset, isGlobal));
471 	}
472 
473 	/**
474 	 * <p>
475 	 * minusEq.
476 	 * </p>
477 	 *
478 	 * @param offset a int
479 	 * @param isGlobal a boolean
480 	 */
481 	public void minusEq(int offset, boolean isGlobal) {
482 		queue.add(new Tuple.CompoundAssignTuple(Opcode.MINUS_EQ, offset, isGlobal));
483 	}
484 
485 	/**
486 	 * <p>
487 	 * multEq.
488 	 * </p>
489 	 *
490 	 * @param offset a int
491 	 * @param isGlobal a boolean
492 	 */
493 	public void multEq(int offset, boolean isGlobal) {
494 		queue.add(new Tuple.CompoundAssignTuple(Opcode.MULT_EQ, offset, isGlobal));
495 	}
496 
497 	/**
498 	 * <p>
499 	 * divEq.
500 	 * </p>
501 	 *
502 	 * @param offset a int
503 	 * @param isGlobal a boolean
504 	 */
505 	public void divEq(int offset, boolean isGlobal) {
506 		queue.add(new Tuple.CompoundAssignTuple(Opcode.DIV_EQ, offset, isGlobal));
507 	}
508 
509 	/**
510 	 * <p>
511 	 * modEq.
512 	 * </p>
513 	 *
514 	 * @param offset a int
515 	 * @param isGlobal a boolean
516 	 */
517 	public void modEq(int offset, boolean isGlobal) {
518 		queue.add(new Tuple.CompoundAssignTuple(Opcode.MOD_EQ, offset, isGlobal));
519 	}
520 
521 	/**
522 	 * <p>
523 	 * powEq.
524 	 * </p>
525 	 *
526 	 * @param offset a int
527 	 * @param isGlobal a boolean
528 	 */
529 	public void powEq(int offset, boolean isGlobal) {
530 		queue.add(new Tuple.CompoundAssignTuple(Opcode.POW_EQ, offset, isGlobal));
531 	}
532 
533 	/**
534 	 * <p>
535 	 * plusEqArray.
536 	 * </p>
537 	 *
538 	 * @param offset a int
539 	 * @param isGlobal a boolean
540 	 */
541 	public void plusEqArray(int offset, boolean isGlobal) {
542 		queue.add(new Tuple.CompoundAssignArrayTuple(Opcode.PLUS_EQ_ARRAY, offset, isGlobal));
543 	}
544 
545 	/**
546 	 * Applies {@code +=} to a stack-provided associative-array element.
547 	 */
548 	public void plusEqMapElement() {
549 		queue.add(new Tuple.CompoundAssignMapElementTuple(Opcode.PLUS_EQ_MAP_ELEMENT));
550 	}
551 
552 	/**
553 	 * <p>
554 	 * minusEqArray.
555 	 * </p>
556 	 *
557 	 * @param offset a int
558 	 * @param isGlobal a boolean
559 	 */
560 	public void minusEqArray(int offset, boolean isGlobal) {
561 		queue.add(new Tuple.CompoundAssignArrayTuple(Opcode.MINUS_EQ_ARRAY, offset, isGlobal));
562 	}
563 
564 	/**
565 	 * Applies {@code -=} to a stack-provided associative-array element.
566 	 */
567 	public void minusEqMapElement() {
568 		queue.add(new Tuple.CompoundAssignMapElementTuple(Opcode.MINUS_EQ_MAP_ELEMENT));
569 	}
570 
571 	/**
572 	 * <p>
573 	 * multEqArray.
574 	 * </p>
575 	 *
576 	 * @param offset a int
577 	 * @param isGlobal a boolean
578 	 */
579 	public void multEqArray(int offset, boolean isGlobal) {
580 		queue.add(new Tuple.CompoundAssignArrayTuple(Opcode.MULT_EQ_ARRAY, offset, isGlobal));
581 	}
582 
583 	/**
584 	 * Applies {@code *=} to a stack-provided associative-array element.
585 	 */
586 	public void multEqMapElement() {
587 		queue.add(new Tuple.CompoundAssignMapElementTuple(Opcode.MULT_EQ_MAP_ELEMENT));
588 	}
589 
590 	/**
591 	 * <p>
592 	 * divEqArray.
593 	 * </p>
594 	 *
595 	 * @param offset a int
596 	 * @param isGlobal a boolean
597 	 */
598 	public void divEqArray(int offset, boolean isGlobal) {
599 		queue.add(new Tuple.CompoundAssignArrayTuple(Opcode.DIV_EQ_ARRAY, offset, isGlobal));
600 	}
601 
602 	/**
603 	 * Applies {@code /=} to a stack-provided associative-array element.
604 	 */
605 	public void divEqMapElement() {
606 		queue.add(new Tuple.CompoundAssignMapElementTuple(Opcode.DIV_EQ_MAP_ELEMENT));
607 	}
608 
609 	/**
610 	 * <p>
611 	 * modEqArray.
612 	 * </p>
613 	 *
614 	 * @param offset a int
615 	 * @param isGlobal a boolean
616 	 */
617 	public void modEqArray(int offset, boolean isGlobal) {
618 		queue.add(new Tuple.CompoundAssignArrayTuple(Opcode.MOD_EQ_ARRAY, offset, isGlobal));
619 	}
620 
621 	/**
622 	 * Applies {@code %=} to a stack-provided associative-array element.
623 	 */
624 	public void modEqMapElement() {
625 		queue.add(new Tuple.CompoundAssignMapElementTuple(Opcode.MOD_EQ_MAP_ELEMENT));
626 	}
627 
628 	/**
629 	 * <p>
630 	 * powEqArray.
631 	 * </p>
632 	 *
633 	 * @param offset a int
634 	 * @param isGlobal a boolean
635 	 */
636 	public void powEqArray(int offset, boolean isGlobal) {
637 		queue.add(new Tuple.CompoundAssignArrayTuple(Opcode.POW_EQ_ARRAY, offset, isGlobal));
638 	}
639 
640 	/**
641 	 * Applies exponentiation assignment to a stack-provided associative-array
642 	 * element.
643 	 */
644 	public void powEqMapElement() {
645 		queue.add(new Tuple.CompoundAssignMapElementTuple(Opcode.POW_EQ_MAP_ELEMENT));
646 	}
647 
648 	/**
649 	 * <p>
650 	 * plusEqInputField.
651 	 * </p>
652 	 */
653 	public void plusEqInputField() {
654 		queue.add(new Tuple.CompoundAssignInputFieldTuple(Opcode.PLUS_EQ_INPUT_FIELD));
655 	}
656 
657 	/**
658 	 * <p>
659 	 * minusEqInputField.
660 	 * </p>
661 	 */
662 	public void minusEqInputField() {
663 		queue.add(new Tuple.CompoundAssignInputFieldTuple(Opcode.MINUS_EQ_INPUT_FIELD));
664 	}
665 
666 	/**
667 	 * <p>
668 	 * multEqInputField.
669 	 * </p>
670 	 */
671 	public void multEqInputField() {
672 		queue.add(new Tuple.CompoundAssignInputFieldTuple(Opcode.MULT_EQ_INPUT_FIELD));
673 	}
674 
675 	/**
676 	 * <p>
677 	 * divEqInputField.
678 	 * </p>
679 	 */
680 	public void divEqInputField() {
681 		queue.add(new Tuple.CompoundAssignInputFieldTuple(Opcode.DIV_EQ_INPUT_FIELD));
682 	}
683 
684 	/**
685 	 * <p>
686 	 * modEqInputField.
687 	 * </p>
688 	 */
689 	public void modEqInputField() {
690 		queue.add(new Tuple.CompoundAssignInputFieldTuple(Opcode.MOD_EQ_INPUT_FIELD));
691 	}
692 
693 	/**
694 	 * <p>
695 	 * powEqInputField.
696 	 * </p>
697 	 */
698 	public void powEqInputField() {
699 		queue.add(new Tuple.CompoundAssignInputFieldTuple(Opcode.POW_EQ_INPUT_FIELD));
700 	}
701 
702 	/**
703 	 * <p>
704 	 * srand.
705 	 * </p>
706 	 *
707 	 * @param num a int
708 	 */
709 	public void srand(int num) {
710 		queue.add(new Tuple.CountTuple(Opcode.SRAND, num));
711 	}
712 
713 	/**
714 	 * <p>
715 	 * rand.
716 	 * </p>
717 	 */
718 	public void rand() {
719 		queue.add(new Tuple.NoOperandTuple(Opcode.RAND));
720 	}
721 
722 	/**
723 	 * <p>
724 	 * intFunc.
725 	 * </p>
726 	 */
727 	public void intFunc() {
728 		queue.add(new Tuple.NoOperandTuple(Opcode.INTFUNC));
729 	}
730 
731 	/**
732 	 * <p>
733 	 * sqrt.
734 	 * </p>
735 	 */
736 	public void sqrt() {
737 		queue.add(new Tuple.NoOperandTuple(Opcode.SQRT));
738 	}
739 
740 	/**
741 	 * <p>
742 	 * log.
743 	 * </p>
744 	 */
745 	public void log() {
746 		queue.add(new Tuple.NoOperandTuple(Opcode.LOG));
747 	}
748 
749 	/**
750 	 * <p>
751 	 * exp.
752 	 * </p>
753 	 */
754 	public void exp() {
755 		queue.add(new Tuple.NoOperandTuple(Opcode.EXP));
756 	}
757 
758 	/**
759 	 * <p>
760 	 * sin.
761 	 * </p>
762 	 */
763 	public void sin() {
764 		queue.add(new Tuple.NoOperandTuple(Opcode.SIN));
765 	}
766 
767 	/**
768 	 * <p>
769 	 * cos.
770 	 * </p>
771 	 */
772 	public void cos() {
773 		queue.add(new Tuple.NoOperandTuple(Opcode.COS));
774 	}
775 
776 	/**
777 	 * <p>
778 	 * atan2.
779 	 * </p>
780 	 */
781 	public void atan2() {
782 		queue.add(new Tuple.NoOperandTuple(Opcode.ATAN2));
783 	}
784 
785 	/**
786 	 * <p>
787 	 * match.
788 	 * </p>
789 	 */
790 	public void match() {
791 		queue.add(new Tuple.NoOperandTuple(Opcode.MATCH));
792 	}
793 
794 	/**
795 	 * <p>
796 	 * index.
797 	 * </p>
798 	 */
799 	public void index() {
800 		queue.add(new Tuple.NoOperandTuple(Opcode.INDEX));
801 	}
802 
803 	/**
804 	 * <p>
805 	 * subForDollar0.
806 	 * </p>
807 	 *
808 	 * @param isGsub a boolean
809 	 */
810 	public void subForDollar0(boolean isGsub) {
811 		queue.add(new Tuple.BooleanTuple(Opcode.SUB_FOR_DOLLAR_0, isGsub));
812 	}
813 
814 	/**
815 	 * <p>
816 	 * subForDollarReference.
817 	 * </p>
818 	 *
819 	 * @param isGsub a boolean
820 	 */
821 	public void subForDollarReference(boolean isGsub) {
822 		queue.add(new Tuple.BooleanTuple(Opcode.SUB_FOR_DOLLAR_REFERENCE, isGsub));
823 	}
824 
825 	/**
826 	 * <p>
827 	 * subForVariable.
828 	 * </p>
829 	 *
830 	 * @param offset a int
831 	 * @param isGlobal a boolean
832 	 * @param isGsub a boolean
833 	 */
834 	public void subForVariable(int offset, boolean isGlobal, boolean isGsub) {
835 		queue.add(new Tuple.SubstitutionVariableTuple(Opcode.SUB_FOR_VARIABLE, offset, isGlobal, isGsub));
836 	}
837 
838 	/**
839 	 * <p>
840 	 * subForArrayReference.
841 	 * </p>
842 	 *
843 	 * @param offset a int
844 	 * @param isGlobal a boolean
845 	 * @param isGsub a boolean
846 	 */
847 	public void subForArrayReference(int offset, boolean isGlobal, boolean isGsub) {
848 		queue.add(new Tuple.SubstitutionVariableTuple(Opcode.SUB_FOR_ARRAY_REFERENCE, offset, isGlobal, isGsub));
849 	}
850 
851 	/**
852 	 * Applies {@code sub}/{@code gsub} to a stack-provided associative-array
853 	 * element.
854 	 *
855 	 * @param isGsub {@code true} for {@code gsub}, {@code false} for {@code sub}
856 	 */
857 	public void subForMapReference(boolean isGsub) {
858 		queue.add(new Tuple.BooleanTuple(Opcode.SUB_FOR_MAP_REFERENCE, isGsub));
859 	}
860 
861 	/**
862 	 * <p>
863 	 * split.
864 	 * </p>
865 	 *
866 	 * @param numargs a int
867 	 */
868 	public void split(int numargs) {
869 		queue.add(new Tuple.CountTuple(Opcode.SPLIT, numargs));
870 	}
871 
872 	/**
873 	 * <p>
874 	 * substr.
875 	 * </p>
876 	 *
877 	 * @param numargs a int
878 	 */
879 	public void substr(int numargs) {
880 		queue.add(new Tuple.CountTuple(Opcode.SUBSTR, numargs));
881 	}
882 
883 	/**
884 	 * <p>
885 	 * tolower.
886 	 * </p>
887 	 */
888 	public void tolower() {
889 		queue.add(new Tuple.NoOperandTuple(Opcode.TOLOWER));
890 	}
891 
892 	/**
893 	 * <p>
894 	 * toupper.
895 	 * </p>
896 	 */
897 	public void toupper() {
898 		queue.add(new Tuple.NoOperandTuple(Opcode.TOUPPER));
899 	}
900 
901 	/**
902 	 * <p>
903 	 * system.
904 	 * </p>
905 	 */
906 	public void system() {
907 		queue.add(new Tuple.NoOperandTuple(Opcode.SYSTEM));
908 	}
909 
910 	/**
911 	 * <p>
912 	 * swap.
913 	 * </p>
914 	 */
915 	public void swap() {
916 		queue.add(new Tuple.NoOperandTuple(Opcode.SWAP));
917 	}
918 
919 	/**
920 	 * <p>
921 	 * add.
922 	 * </p>
923 	 */
924 	public void add() {
925 		queue.add(new Tuple.NoOperandTuple(Opcode.ADD));
926 	}
927 
928 	/**
929 	 * <p>
930 	 * subtract.
931 	 * </p>
932 	 */
933 	public void subtract() {
934 		queue.add(new Tuple.NoOperandTuple(Opcode.SUBTRACT));
935 	}
936 
937 	/**
938 	 * <p>
939 	 * multiply.
940 	 * </p>
941 	 */
942 	public void multiply() {
943 		queue.add(new Tuple.NoOperandTuple(Opcode.MULTIPLY));
944 	}
945 
946 	/**
947 	 * <p>
948 	 * divide.
949 	 * </p>
950 	 */
951 	public void divide() {
952 		queue.add(new Tuple.NoOperandTuple(Opcode.DIVIDE));
953 	}
954 
955 	/**
956 	 * <p>
957 	 * mod.
958 	 * </p>
959 	 */
960 	public void mod() {
961 		queue.add(new Tuple.NoOperandTuple(Opcode.MOD));
962 	}
963 
964 	/**
965 	 * <p>
966 	 * pow.
967 	 * </p>
968 	 */
969 	public void pow() {
970 		queue.add(new Tuple.NoOperandTuple(Opcode.POW));
971 	}
972 
973 	/**
974 	 * <p>
975 	 * inc.
976 	 * </p>
977 	 *
978 	 * @param offset a int
979 	 * @param isGlobal a boolean
980 	 */
981 	public void inc(int offset, boolean isGlobal) {
982 		queue.add(new Tuple.VariableTuple(Opcode.INC, offset, isGlobal));
983 	}
984 
985 	/**
986 	 * <p>
987 	 * dec.
988 	 * </p>
989 	 *
990 	 * @param offset a int
991 	 * @param isGlobal a boolean
992 	 */
993 	public void dec(int offset, boolean isGlobal) {
994 		queue.add(new Tuple.VariableTuple(Opcode.DEC, offset, isGlobal));
995 	}
996 
997 	/**
998 	 * <p>
999 	 * postInc.
1000 	 * </p>
1001 	 *
1002 	 * @param offset a int
1003 	 * @param isGlobal a boolean
1004 	 */
1005 	public void postInc(int offset, boolean isGlobal) {
1006 		queue.add(new Tuple.VariableTuple(Opcode.POSTINC, offset, isGlobal));
1007 	}
1008 
1009 	/**
1010 	 * <p>
1011 	 * postDec.
1012 	 * </p>
1013 	 *
1014 	 * @param offset a int
1015 	 * @param isGlobal a boolean
1016 	 */
1017 	public void postDec(int offset, boolean isGlobal) {
1018 		queue.add(new Tuple.VariableTuple(Opcode.POSTDEC, offset, isGlobal));
1019 	}
1020 
1021 	/**
1022 	 * <p>
1023 	 * incArrayRef.
1024 	 * </p>
1025 	 *
1026 	 * @param offset a int
1027 	 * @param isGlobal a boolean
1028 	 */
1029 	public void incArrayRef(int offset, boolean isGlobal) {
1030 		queue.add(new Tuple.VariableTuple(Opcode.INC_ARRAY_REF, offset, isGlobal));
1031 	}
1032 
1033 	/**
1034 	 * Increments a stack-provided associative-array element reference.
1035 	 */
1036 	public void incMapRef() {
1037 		queue.add(new Tuple.NoOperandTuple(Opcode.INC_MAP_REF));
1038 	}
1039 
1040 	/**
1041 	 * <p>
1042 	 * decArrayRef.
1043 	 * </p>
1044 	 *
1045 	 * @param offset a int
1046 	 * @param isGlobal a boolean
1047 	 */
1048 	public void decArrayRef(int offset, boolean isGlobal) {
1049 		queue.add(new Tuple.VariableTuple(Opcode.DEC_ARRAY_REF, offset, isGlobal));
1050 	}
1051 
1052 	/**
1053 	 * Decrements a stack-provided associative-array element reference.
1054 	 */
1055 	public void decMapRef() {
1056 		queue.add(new Tuple.NoOperandTuple(Opcode.DEC_MAP_REF));
1057 	}
1058 
1059 	/**
1060 	 * <p>
1061 	 * incDollarRef.
1062 	 * </p>
1063 	 */
1064 	public void incDollarRef() {
1065 		queue.add(new Tuple.NoOperandTuple(Opcode.INC_DOLLAR_REF));
1066 	}
1067 
1068 	/**
1069 	 * <p>
1070 	 * decDollarRef.
1071 	 * </p>
1072 	 */
1073 	public void decDollarRef() {
1074 		queue.add(new Tuple.NoOperandTuple(Opcode.DEC_DOLLAR_REF));
1075 	}
1076 
1077 	/**
1078 	 * <p>
1079 	 * dup.
1080 	 * </p>
1081 	 */
1082 	public void dup() {
1083 		queue.add(new Tuple.NoOperandTuple(Opcode.DUP));
1084 	}
1085 
1086 	/**
1087 	 * <p>
1088 	 * not.
1089 	 * </p>
1090 	 */
1091 	public void not() {
1092 		queue.add(new Tuple.NoOperandTuple(Opcode.NOT));
1093 	}
1094 
1095 	/**
1096 	 * <p>
1097 	 * negate.
1098 	 * </p>
1099 	 */
1100 	public void negate() {
1101 		queue.add(new Tuple.NoOperandTuple(Opcode.NEGATE));
1102 	}
1103 
1104 	/**
1105 	 * <p>
1106 	 * unary plus.
1107 	 * </p>
1108 	 */
1109 	public void unaryPlus() {
1110 		queue.add(new Tuple.NoOperandTuple(Opcode.UNARY_PLUS));
1111 	}
1112 
1113 	/**
1114 	 * <p>
1115 	 * cmpEq.
1116 	 * </p>
1117 	 */
1118 	public void cmpEq() {
1119 		queue.add(new Tuple.NoOperandTuple(Opcode.CMP_EQ));
1120 	}
1121 
1122 	/**
1123 	 * <p>
1124 	 * cmpLt.
1125 	 * </p>
1126 	 */
1127 	public void cmpLt() {
1128 		queue.add(new Tuple.NoOperandTuple(Opcode.CMP_LT));
1129 	}
1130 
1131 	/**
1132 	 * <p>
1133 	 * cmpGt.
1134 	 * </p>
1135 	 */
1136 	public void cmpGt() {
1137 		queue.add(new Tuple.NoOperandTuple(Opcode.CMP_GT));
1138 	}
1139 
1140 	/**
1141 	 * <p>
1142 	 * matches.
1143 	 * </p>
1144 	 */
1145 	public void matches() {
1146 		queue.add(new Tuple.NoOperandTuple(Opcode.MATCHES));
1147 	}
1148 
1149 	/**
1150 	 * <p>
1151 	 * dereferenceArray.
1152 	 * </p>
1153 	 */
1154 	public void dereferenceArray() {
1155 		queue.add(new Tuple.NoOperandTuple(Opcode.DEREF_ARRAY));
1156 	}
1157 
1158 	/**
1159 	 * Looks up an associative-array element without creating a blank entry when
1160 	 * the key is missing.
1161 	 */
1162 	public void peekArrayElement() {
1163 		queue.add(new Tuple.NoOperandTuple(Opcode.PEEK_ARRAY_ELEMENT));
1164 	}
1165 
1166 	/**
1167 	 * Dereferences an associative-array element as a nested array, creating it if
1168 	 * needed.
1169 	 */
1170 	public void ensureArrayElement() {
1171 		queue.add(new Tuple.NoOperandTuple(Opcode.ENSURE_ARRAY_ELEMENT));
1172 	}
1173 
1174 	/**
1175 	 * <p>
1176 	 * key list.
1177 	 * </p>
1178 	 */
1179 	public void keylist() {
1180 		queue.add(new Tuple.NoOperandTuple(Opcode.KEYLIST));
1181 	}
1182 
1183 	/**
1184 	 * <p>
1185 	 * isEmptyList.
1186 	 * </p>
1187 	 *
1188 	 * @param address a {@link io.jawk.intermediate.Address} object
1189 	 */
1190 	public void isEmptyList(Address address) {
1191 		queue.add(new Tuple.AddressTuple(Opcode.IS_EMPTY_KEYLIST, address));
1192 	}
1193 
1194 	/**
1195 	 * <p>
1196 	 * getFirstAndRemoveFromList.
1197 	 * </p>
1198 	 */
1199 	public void getFirstAndRemoveFromList() {
1200 		queue.add(new Tuple.NoOperandTuple(Opcode.GET_FIRST_AND_REMOVE_FROM_KEYLIST));
1201 	}
1202 
1203 	/**
1204 	 * <p>
1205 	 * checkClass.
1206 	 * </p>
1207 	 *
1208 	 * @param cls a {@link java.lang.Class} object
1209 	 * @return a boolean
1210 	 */
1211 	public boolean checkClass(Class<?> cls) {
1212 		queue.add(new Tuple.ClassTuple(cls));
1213 		return true;
1214 	}
1215 
1216 	/**
1217 	 * <p>
1218 	 * getInputField.
1219 	 * </p>
1220 	 */
1221 	public void getInputField() {
1222 		queue.add(new Tuple.NoOperandTuple(Opcode.GET_INPUT_FIELD));
1223 	}
1224 
1225 	/**
1226 	 * <p>
1227 	 * getInputField.
1228 	 * </p>
1229 	 *
1230 	 * @param fieldIndex a long
1231 	 */
1232 	public void getInputField(long fieldIndex) {
1233 		queue.add(new Tuple.InputFieldTuple(fieldIndex));
1234 	}
1235 
1236 	/**
1237 	 * <p>
1238 	 * consumeInput.
1239 	 * </p>
1240 	 *
1241 	 * @param address a {@link io.jawk.intermediate.Address} object
1242 	 */
1243 	public void consumeInput(Address address) {
1244 		queue.add(new Tuple.AddressTuple(Opcode.CONSUME_INPUT, address));
1245 	}
1246 
1247 	/**
1248 	 * <p>
1249 	 * getlineInput.
1250 	 * </p>
1251 	 */
1252 	public void getlineInput() {
1253 		queue.add(new Tuple.NoOperandTuple(Opcode.GETLINE_INPUT));
1254 	}
1255 
1256 	/**
1257 	 * <p>
1258 	 * getlineInputToTarget.
1259 	 * </p>
1260 	 */
1261 	public void getlineInputToTarget() {
1262 		queue.add(new Tuple.NoOperandTuple(Opcode.GETLINE_INPUT_TO_TARGET));
1263 	}
1264 
1265 	/**
1266 	 * <p>
1267 	 * useAsFileInput.
1268 	 * </p>
1269 	 */
1270 	public void useAsFileInput() {
1271 		queue.add(new Tuple.NoOperandTuple(Opcode.USE_AS_FILE_INPUT));
1272 	}
1273 
1274 	/**
1275 	 * <p>
1276 	 * useAsCommandInput.
1277 	 * </p>
1278 	 */
1279 	public void useAsCommandInput() {
1280 		queue.add(new Tuple.NoOperandTuple(Opcode.USE_AS_COMMAND_INPUT));
1281 	}
1282 
1283 	/**
1284 	 * <p>
1285 	 * nfOffset.
1286 	 * </p>
1287 	 *
1288 	 * @param offset a int
1289 	 */
1290 	public void nfOffset(int offset) {
1291 		queue.add(new Tuple.LongTuple(Opcode.NF_OFFSET, offset));
1292 	}
1293 
1294 	/**
1295 	 * <p>
1296 	 * nrOffset.
1297 	 * </p>
1298 	 *
1299 	 * @param offset a int
1300 	 */
1301 	public void nrOffset(int offset) {
1302 		queue.add(new Tuple.LongTuple(Opcode.NR_OFFSET, offset));
1303 	}
1304 
1305 	/**
1306 	 * <p>
1307 	 * fnrOffset.
1308 	 * </p>
1309 	 *
1310 	 * @param offset a int
1311 	 */
1312 	public void fnrOffset(int offset) {
1313 		queue.add(new Tuple.LongTuple(Opcode.FNR_OFFSET, offset));
1314 	}
1315 
1316 	/**
1317 	 * <p>
1318 	 * fsOffset.
1319 	 * </p>
1320 	 *
1321 	 * @param offset a int
1322 	 */
1323 	public void fsOffset(int offset) {
1324 		queue.add(new Tuple.LongTuple(Opcode.FS_OFFSET, offset));
1325 	}
1326 
1327 	/**
1328 	 * <p>
1329 	 * rsOffset.
1330 	 * </p>
1331 	 *
1332 	 * @param offset a int
1333 	 */
1334 	public void rsOffset(int offset) {
1335 		queue.add(new Tuple.LongTuple(Opcode.RS_OFFSET, offset));
1336 	}
1337 
1338 	/**
1339 	 * <p>
1340 	 * ofsOffset.
1341 	 * </p>
1342 	 *
1343 	 * @param offset a int
1344 	 */
1345 	public void ofsOffset(int offset) {
1346 		queue.add(new Tuple.LongTuple(Opcode.OFS_OFFSET, offset));
1347 	}
1348 
1349 	/**
1350 	 * <p>
1351 	 * orsOffset.
1352 	 * </p>
1353 	 *
1354 	 * @param offset a int
1355 	 */
1356 	public void orsOffset(int offset) {
1357 		queue.add(new Tuple.LongTuple(Opcode.ORS_OFFSET, offset));
1358 	}
1359 
1360 	/**
1361 	 * <p>
1362 	 * rstartOffset.
1363 	 * </p>
1364 	 *
1365 	 * @param offset a int
1366 	 */
1367 	public void rstartOffset(int offset) {
1368 		queue.add(new Tuple.LongTuple(Opcode.RSTART_OFFSET, offset));
1369 	}
1370 
1371 	/**
1372 	 * <p>
1373 	 * rlengthOffset.
1374 	 * </p>
1375 	 *
1376 	 * @param offset a int
1377 	 */
1378 	public void rlengthOffset(int offset) {
1379 		queue.add(new Tuple.LongTuple(Opcode.RLENGTH_OFFSET, offset));
1380 	}
1381 
1382 	/**
1383 	 * <p>
1384 	 * filenameOffset.
1385 	 * </p>
1386 	 *
1387 	 * @param offset a int
1388 	 */
1389 	public void filenameOffset(int offset) {
1390 		queue.add(new Tuple.LongTuple(Opcode.FILENAME_OFFSET, offset));
1391 	}
1392 
1393 	/**
1394 	 * <p>
1395 	 * subsepOffset.
1396 	 * </p>
1397 	 *
1398 	 * @param offset a int
1399 	 */
1400 	public void subsepOffset(int offset) {
1401 		queue.add(new Tuple.LongTuple(Opcode.SUBSEP_OFFSET, offset));
1402 	}
1403 
1404 	/**
1405 	 * <p>
1406 	 * convfmtOffset.
1407 	 * </p>
1408 	 *
1409 	 * @param offset a int
1410 	 */
1411 	public void convfmtOffset(int offset) {
1412 		queue.add(new Tuple.LongTuple(Opcode.CONVFMT_OFFSET, offset));
1413 	}
1414 
1415 	/**
1416 	 * <p>
1417 	 * ofmtOffset.
1418 	 * </p>
1419 	 *
1420 	 * @param offset a int
1421 	 */
1422 	public void ofmtOffset(int offset) {
1423 		queue.add(new Tuple.LongTuple(Opcode.OFMT_OFFSET, offset));
1424 	}
1425 
1426 	/**
1427 	 * <p>
1428 	 * environOffset.
1429 	 * </p>
1430 	 *
1431 	 * @param offset a int
1432 	 */
1433 	public void environOffset(int offset) {
1434 		queue.add(new Tuple.LongTuple(Opcode.ENVIRON_OFFSET, offset));
1435 	}
1436 
1437 	/**
1438 	 * Emits the tuple that runs the extension beforeStart hooks, placed at the
1439 	 * end of the preamble.
1440 	 */
1441 	public void beforeStartHooks() {
1442 		queue.add(new Tuple.NoOperandTuple(Opcode.BEFORE_START_HOOKS));
1443 	}
1444 
1445 	/**
1446 	 * Emits the tuple populating the SYMTAB array.
1447 	 *
1448 	 * @param offset offset of the SYMTAB global
1449 	 */
1450 	public void updateSymtab(int offset) {
1451 		queue.add(new Tuple.LongTuple(Opcode.UPDATE_SYMTAB, offset));
1452 	}
1453 
1454 	/**
1455 	 * Emits the tuple populating the FUNCTAB array.
1456 	 *
1457 	 * @param offset offset of the FUNCTAB global
1458 	 */
1459 	public void updateFunctab(int offset) {
1460 		queue.add(new Tuple.LongTuple(Opcode.UPDATE_FUNCTAB, offset));
1461 	}
1462 
1463 	/**
1464 	 * <p>
1465 	 * argcOffset.
1466 	 * </p>
1467 	 *
1468 	 * @param offset a int
1469 	 */
1470 	public void argcOffset(int offset) {
1471 		queue.add(new Tuple.LongTuple(Opcode.ARGC_OFFSET, offset));
1472 	}
1473 
1474 	/**
1475 	 * <p>
1476 	 * argvOffset.
1477 	 * </p>
1478 	 *
1479 	 * @param offset a int
1480 	 */
1481 	public void argvOffset(int offset) {
1482 		queue.add(new Tuple.LongTuple(Opcode.ARGV_OFFSET, offset));
1483 	}
1484 
1485 	// JRT-managed special variable helpers
1486 	/** Pushes the current value of {@code NF} onto the operand stack. */
1487 	public void pushNF() {
1488 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_NF));
1489 	}
1490 
1491 	/** Assigns the top-of-stack value to {@code NF}. */
1492 	public void assignNF() {
1493 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_NF));
1494 	}
1495 
1496 	/** Pushes the current value of {@code NR} onto the operand stack. */
1497 	public void pushNR() {
1498 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_NR));
1499 	}
1500 
1501 	/** Assigns the top-of-stack value to {@code NR}. */
1502 	public void assignNR() {
1503 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_NR));
1504 	}
1505 
1506 	/** Pushes the current value of {@code FNR} onto the operand stack. */
1507 	public void pushFNR() {
1508 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_FNR));
1509 	}
1510 
1511 	/** Assigns the top-of-stack value to {@code FNR}. */
1512 	public void assignFNR() {
1513 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_FNR));
1514 	}
1515 
1516 	/** Pushes the current value of {@code FS} onto the operand stack. */
1517 	public void pushFS() {
1518 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_FS));
1519 	}
1520 
1521 	/** Assigns the top-of-stack value to {@code FS}. */
1522 	public void assignFS() {
1523 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_FS));
1524 	}
1525 
1526 	/**
1527 	 * Emits a tuple pushing the value of IGNORECASE.
1528 	 */
1529 	public void pushIGNORECASE() {
1530 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_IGNORECASE));
1531 	}
1532 
1533 	/**
1534 	 * Emits a tuple assigning the top of the stack to IGNORECASE.
1535 	 */
1536 	public void assignIGNORECASE() {
1537 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_IGNORECASE));
1538 	}
1539 
1540 	/**
1541 	 * Emits the tuple pushing the value of ERRNO, managed by the JRT.
1542 	 */
1543 	public void pushERRNO() {
1544 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_ERRNO));
1545 	}
1546 
1547 	/**
1548 	 * Emits the tuple assigning the top of the stack to ERRNO, managed by the
1549 	 * JRT.
1550 	 */
1551 	public void assignERRNO() {
1552 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_ERRNO));
1553 	}
1554 
1555 	/**
1556 	 * Emits the tuple pushing the value of ARGIND, managed by the JRT.
1557 	 */
1558 	public void pushARGIND() {
1559 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_ARGIND));
1560 	}
1561 
1562 	/**
1563 	 * Emits the tuple assigning the top of the stack to ARGIND, managed by
1564 	 * the JRT.
1565 	 */
1566 	public void assignARGIND() {
1567 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_ARGIND));
1568 	}
1569 
1570 	/** Pushes the current value of {@code RS} onto the operand stack. */
1571 	public void pushRS() {
1572 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_RS));
1573 	}
1574 
1575 	/** Assigns the top-of-stack value to {@code RS}. */
1576 	public void assignRS() {
1577 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_RS));
1578 	}
1579 
1580 	/** Pushes the current value of {@code OFS} onto the operand stack. */
1581 	public void pushOFS() {
1582 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_OFS));
1583 	}
1584 
1585 	/** Assigns the top-of-stack value to {@code OFS}. */
1586 	public void assignOFS() {
1587 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_OFS));
1588 	}
1589 
1590 	/** Pushes the current value of {@code ORS} onto the operand stack. */
1591 	public void pushORS() {
1592 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_ORS));
1593 	}
1594 
1595 	/** Assigns the top-of-stack value to {@code ORS}. */
1596 	public void assignORS() {
1597 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_ORS));
1598 	}
1599 
1600 	/** Pushes the current value of {@code RSTART} onto the operand stack. */
1601 	public void pushRSTART() {
1602 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_RSTART));
1603 	}
1604 
1605 	/** Assigns the top-of-stack value to {@code RSTART}. */
1606 	public void assignRSTART() {
1607 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_RSTART));
1608 	}
1609 
1610 	/** Pushes the current value of {@code RLENGTH} onto the operand stack. */
1611 	public void pushRLENGTH() {
1612 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_RLENGTH));
1613 	}
1614 
1615 	/** Assigns the top-of-stack value to {@code RLENGTH}. */
1616 	public void assignRLENGTH() {
1617 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_RLENGTH));
1618 	}
1619 
1620 	/** Pushes the current value of {@code FILENAME} onto the operand stack. */
1621 	public void pushFILENAME() {
1622 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_FILENAME));
1623 	}
1624 
1625 	/** Assigns the top-of-stack value to {@code FILENAME}. */
1626 	public void assignFILENAME() {
1627 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_FILENAME));
1628 	}
1629 
1630 	/** Pushes the current value of {@code SUBSEP} onto the operand stack. */
1631 	public void pushSUBSEP() {
1632 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_SUBSEP));
1633 	}
1634 
1635 	/** Assigns the top-of-stack value to {@code SUBSEP}. */
1636 	public void assignSUBSEP() {
1637 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_SUBSEP));
1638 	}
1639 
1640 	/** Pushes the current value of {@code CONVFMT} onto the operand stack. */
1641 	public void pushCONVFMT() {
1642 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_CONVFMT));
1643 	}
1644 
1645 	/** Assigns the top-of-stack value to {@code CONVFMT}. */
1646 	public void assignCONVFMT() {
1647 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_CONVFMT));
1648 	}
1649 
1650 	/** Pushes the current value of {@code OFMT} onto the operand stack. */
1651 	public void pushOFMT() {
1652 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_OFMT));
1653 	}
1654 
1655 	/** Assigns the top-of-stack value to {@code OFMT}. */
1656 	public void assignOFMT() {
1657 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_OFMT));
1658 	}
1659 
1660 	/** Pushes the current value of {@code ARGC} onto the operand stack. */
1661 	public void pushARGC() {
1662 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_ARGC));
1663 	}
1664 
1665 	/** Assigns the top-of-stack value to {@code ARGC}. */
1666 	public void assignARGC() {
1667 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_ARGC));
1668 	}
1669 
1670 	/**
1671 	 * <p>
1672 	 * applyRS.
1673 	 * </p>
1674 	 */
1675 	public void applyRS() {
1676 		queue.add(new Tuple.NoOperandTuple(Opcode.APPLY_RS));
1677 	}
1678 
1679 	/**
1680 	 * <p>
1681 	 * function.
1682 	 * </p>
1683 	 *
1684 	 * @param funcName a {@link java.lang.String} object
1685 	 * @param numFormalParams a int
1686 	 */
1687 	public void function(String funcName, int numFormalParams) {
1688 		queue.add(new Tuple.FunctionTuple(funcName, numFormalParams));
1689 	}
1690 
1691 	/**
1692 	 * <p>
1693 	 * callFunction.
1694 	 * </p>
1695 	 *
1696 	 * @param addressSupplier supplier resolving the function's entry point
1697 	 * @param funcName a {@link java.lang.String} object
1698 	 * @param numFormalParams a int
1699 	 * @param numActualParams a int
1700 	 */
1701 	public void callFunction(
1702 			Supplier<Address> addressSupplier,
1703 			String funcName,
1704 			int numFormalParams,
1705 			int numActualParams) {
1706 		queue.add(new Tuple.CallFunctionTuple(addressSupplier, funcName, numFormalParams, numActualParams));
1707 	}
1708 
1709 	/**
1710 	 * Emits a call whose function name is evaluated at runtime.
1711 	 *
1712 	 * @param userFunctions available user-defined function targets
1713 	 * @param extensionFunctions available extension function targets
1714 	 * @param numActualParams number of evaluated actual parameters
1715 	 * @param sourceName source name for runtime diagnostics
1716 	 * @param lineNumber source line for runtime diagnostics
1717 	 */
1718 	public void indirectCall(
1719 			Map<String, Tuple.IndirectFunctionTarget> userFunctions,
1720 			Map<String, ExtensionFunction> extensionFunctions,
1721 			int numActualParams,
1722 			String sourceName,
1723 			int lineNumber) {
1724 		queue
1725 				.add(
1726 						new Tuple.IndirectCallTuple(
1727 								userFunctions,
1728 								extensionFunctions,
1729 								numActualParams,
1730 								sourceName,
1731 								lineNumber));
1732 	}
1733 
1734 	/**
1735 	 * Emits a tuple that prints a diagnostic message to the warning stream when
1736 	 * executed. Planted by the parser just before the instruction it describes,
1737 	 * so the warning appears in runtime order, exactly where gawk emits it.
1738 	 *
1739 	 * @param message warning text to print
1740 	 */
1741 	public void warning(String message) {
1742 		queue.add(new Tuple.WarningTuple(message));
1743 	}
1744 
1745 	/**
1746 	 * <p>
1747 	 * setReturnResult.
1748 	 * </p>
1749 	 */
1750 	public void setReturnResult() {
1751 		queue.add(new Tuple.NoOperandTuple(Opcode.SET_RETURN_RESULT));
1752 	}
1753 
1754 	/**
1755 	 * <p>
1756 	 * returnFromFunction.
1757 	 * </p>
1758 	 */
1759 	public void returnFromFunction() {
1760 		queue.add(new Tuple.NoOperandTuple(Opcode.RETURN_FROM_FUNCTION));
1761 	}
1762 
1763 	/**
1764 	 * <p>
1765 	 * setNumGlobals.
1766 	 * </p>
1767 	 *
1768 	 * @param numGlobals a int
1769 	 */
1770 	public void setNumGlobals(int numGlobals) {
1771 		queue.add(new Tuple.CountTuple(Opcode.SET_NUM_GLOBALS, numGlobals));
1772 	}
1773 
1774 	/**
1775 	 * <p>
1776 	 * close.
1777 	 * </p>
1778 	 */
1779 	public void close() {
1780 		queue.add(new Tuple.NoOperandTuple(Opcode.CLOSE));
1781 	}
1782 
1783 	/**
1784 	 * <p>
1785 	 * applySubsep.
1786 	 * </p>
1787 	 *
1788 	 * @param count a int
1789 	 */
1790 	public void applySubsep(int count) {
1791 		queue.add(new Tuple.CountTuple(Opcode.APPLY_SUBSEP, count));
1792 	}
1793 
1794 	/**
1795 	 * <p>
1796 	 * deleteArrayElement.
1797 	 * </p>
1798 	 *
1799 	 * @param offset a int
1800 	 * @param isGlobal a boolean
1801 	 */
1802 	public void deleteArrayElement(int offset, boolean isGlobal) {
1803 		queue.add(new Tuple.VariableTuple(Opcode.DELETE_ARRAY_ELEMENT, offset, isGlobal));
1804 	}
1805 
1806 	/**
1807 	 * Deletes a stack-provided associative-array element.
1808 	 */
1809 	public void deleteMapElement() {
1810 		queue.add(new Tuple.NoOperandTuple(Opcode.DELETE_MAP_ELEMENT));
1811 	}
1812 
1813 	/**
1814 	 * <p>
1815 	 * deleteArray.
1816 	 * </p>
1817 	 *
1818 	 * @param offset a int
1819 	 * @param isGlobal a boolean
1820 	 */
1821 	public void deleteArray(int offset, boolean isGlobal) {
1822 		queue.add(new Tuple.VariableTuple(Opcode.DELETE_ARRAY, offset, isGlobal));
1823 	}
1824 
1825 	/**
1826 	 * Registers the address of the END blocks section, so that a runtime
1827 	 * {@code exit} statement can jump to it. A property of the tuple stream
1828 	 * rather than a tuple: the interpreter reads it once when it installs
1829 	 * the program.
1830 	 *
1831 	 * @param addr address of the END blocks section
1832 	 */
1833 	public void setExitAddress(Address addr) {
1834 		exitAddress = addr;
1835 	}
1836 
1837 	/**
1838 	 * Returns the address of the END blocks section, or {@code null} when
1839 	 * the tuple stream is an expression stream with no END blocks.
1840 	 *
1841 	 * @return address of the END blocks section, or {@code null}
1842 	 */
1843 	public Address getExitAddress() {
1844 		return exitAddress;
1845 	}
1846 
1847 	/**
1848 	 * <p>
1849 	 * setWithinEndBlocks.
1850 	 * </p>
1851 	 *
1852 	 * @param b a boolean
1853 	 */
1854 	public void setWithinEndBlocks(boolean b) {
1855 		queue.add(new Tuple.BooleanTuple(Opcode.SET_WITHIN_END_BLOCKS, b));
1856 	}
1857 
1858 	/**
1859 	 * Registers the address of the ENDFILE section, so that a runtime
1860 	 * {@code nextfile} statement can jump to it. A property of the tuple
1861 	 * stream rather than a tuple: the interpreter reads it once when it
1862 	 * installs the program.
1863 	 *
1864 	 * @param addr address of the ENDFILE section
1865 	 */
1866 	public void setEndFileAddress(Address addr) {
1867 		endFileAddress = addr;
1868 	}
1869 
1870 	/**
1871 	 * Returns the address of the ENDFILE section, or {@code null} when the
1872 	 * program has no BEGINFILE/ENDFILE rules.
1873 	 *
1874 	 * @return address of the ENDFILE section, or {@code null}
1875 	 */
1876 	public Address getEndFileAddress() {
1877 		return endFileAddress;
1878 	}
1879 
1880 	/**
1881 	 * Registers the address of the {@code NEXT_FILE} tuple that opens each
1882 	 * input file, so that a runtime {@code nextfile} statement can bypass
1883 	 * the ENDFILE rules for input files that could not be opened. A property
1884 	 * of the tuple stream rather than a tuple: the interpreter reads it once
1885 	 * when it installs the program.
1886 	 *
1887 	 * @param addr address of the NEXT_FILE tuple
1888 	 */
1889 	public void setNextFileAddress(Address addr) {
1890 		nextFileAddress = addr;
1891 	}
1892 
1893 	/**
1894 	 * Returns the address of the {@code NEXT_FILE} tuple that opens each
1895 	 * input file, or {@code null} when the program does not use per-file
1896 	 * input stepping.
1897 	 *
1898 	 * @return address of the NEXT_FILE tuple, or {@code null}
1899 	 */
1900 	public Address getNextFileAddress() {
1901 		return nextFileAddress;
1902 	}
1903 
1904 	/**
1905 	 * Emits the tuple advancing the main input to the next input file, or
1906 	 * jumping to the given address when no input file remains.
1907 	 *
1908 	 * @param address address to jump to when no more input files remain
1909 	 */
1910 	public void nextFile(Address address) {
1911 		queue.add(new Tuple.AddressTuple(Opcode.NEXT_FILE, address));
1912 	}
1913 
1914 	/**
1915 	 * Emits the tuple consuming one record of the current input file only,
1916 	 * jumping to the given address at end of the current file.
1917 	 *
1918 	 * @param address address to jump to at end of the current input file
1919 	 */
1920 	public void consumeFileInput(Address address) {
1921 		queue.add(new Tuple.AddressTuple(Opcode.CONSUME_FILE_INPUT, address));
1922 	}
1923 
1924 	/**
1925 	 * Emits the tuple executing the {@code nextfile} statement at runtime.
1926 	 */
1927 	public void execNextfile() {
1928 		queue.add(new Tuple.NoOperandTuple(Opcode.EXEC_NEXTFILE));
1929 	}
1930 
1931 	/**
1932 	 * <p>
1933 	 * exitWithCode.
1934 	 * </p>
1935 	 */
1936 	public void exitWithCode() {
1937 		queue.add(new Tuple.NoOperandTuple(Opcode.EXIT_WITH_CODE));
1938 	}
1939 
1940 	/**
1941 	 * <p>
1942 	 * exitWithCode.
1943 	 * </p>
1944 	 */
1945 	public void exitWithoutCode() {
1946 		queue.add(new Tuple.NoOperandTuple(Opcode.EXIT_WITHOUT_CODE));
1947 	}
1948 
1949 	/**
1950 	 * <p>
1951 	 * regexp.
1952 	 * </p>
1953 	 *
1954 	 * @param regexpStr a {@link java.lang.String} object
1955 	 */
1956 	public void regexp(String regexpStr) {
1957 		// For literal regexes (created by RegexpAst), precompile the Pattern
1958 		// and store it alongside the original string to skip runtime compilation.
1959 		Pattern precompiled = Pattern.compile(regexpStr);
1960 		queue.add(new Tuple.RegexTuple(regexpStr, precompiled));
1961 	}
1962 
1963 	/**
1964 	 * <p>
1965 	 * regexpPair.
1966 	 * </p>
1967 	 *
1968 	 * @deprecated Evaluates both range conditions on every record, which is
1969 	 *             incorrect when the conditions have side effects. Use
1970 	 *             {@link #conditionPairInRange(long)},
1971 	 *             {@link #conditionPairEnter(long)} and
1972 	 *             {@link #conditionPairLeave(long)} with conditional jumps instead.
1973 	 */
1974 	@Deprecated
1975 	public void conditionPair() {
1976 		queue.add(new Tuple.NoOperandTuple(Opcode.CONDITION_PAIR));
1977 	}
1978 
1979 	/**
1980 	 * Pushes whether the specified range pattern is currently active, i.e. its
1981 	 * start condition matched a previous record and its end condition hasn't
1982 	 * matched yet.
1983 	 *
1984 	 * @param id unique identifier of the range pattern within the script
1985 	 */
1986 	public void conditionPairInRange(long id) {
1987 		queue.add(new Tuple.LongTuple(Opcode.CONDITION_PAIR_IN_RANGE, id));
1988 	}
1989 
1990 	/**
1991 	 * Marks the specified range pattern as active, after its start condition
1992 	 * matched the current record.
1993 	 *
1994 	 * @param id unique identifier of the range pattern within the script
1995 	 */
1996 	public void conditionPairEnter(long id) {
1997 		queue.add(new Tuple.LongTuple(Opcode.CONDITION_PAIR_ENTER, id));
1998 	}
1999 
2000 	/**
2001 	 * Marks the specified range pattern as inactive, after its end condition
2002 	 * matched the current record.
2003 	 *
2004 	 * @param id unique identifier of the range pattern within the script
2005 	 */
2006 	public void conditionPairLeave(long id) {
2007 		queue.add(new Tuple.LongTuple(Opcode.CONDITION_PAIR_LEAVE, id));
2008 	}
2009 
2010 	/**
2011 	 * <p>
2012 	 * isIn.
2013 	 * </p>
2014 	 */
2015 	public void isIn() {
2016 		queue.add(new Tuple.NoOperandTuple(Opcode.IS_IN));
2017 	}
2018 
2019 	/**
2020 	 * Emits a tuple that pushes the current script context onto the stack.
2021 	 */
2022 	public void scriptThis() {
2023 		queue.add(new Tuple.NoOperandTuple(Opcode.THIS));
2024 	}
2025 
2026 	/**
2027 	 * Emits an extension invocation tuple.
2028 	 *
2029 	 * @param function metadata describing the extension method to invoke
2030 	 * @param paramCount number of arguments supplied for the call
2031 	 * @param isInitial {@code true} when this tuple opens an extension call sequence
2032 	 */
2033 	public void extension(ExtensionFunction function, int paramCount, boolean isInitial) {
2034 		queue.add(new Tuple.ExtensionTuple(function, paramCount, isInitial));
2035 	}
2036 
2037 	/**
2038 	 * Dumps the queued tuples to the provided {@link PrintStream}.
2039 	 *
2040 	 * @param ps destination stream for the tuple listing
2041 	 */
2042 	public void dump(PrintStream ps) {
2043 		ps.println("(intermediate serialVersionUID = " + serialVersionUID + ")");
2044 		ps.println();
2045 		for (int i = 0; i < queue.size(); i++) {
2046 			Address address = addressManager.getAddress(i);
2047 			if (address == null) {
2048 				ps.println(i + " : " + queue.get(i));
2049 			} else {
2050 				ps.println(i + " : [" + address + "] : " + queue.get(i));
2051 			}
2052 		}
2053 	}
2054 
2055 	/**
2056 	 * <p>
2057 	 * top.
2058 	 * </p>
2059 	 *
2060 	 * @return a {@link io.jawk.intermediate.PositionTracker} object
2061 	 */
2062 	public PositionTracker top() {
2063 		return new PositionTracker(queue);
2064 	}
2065 
2066 	/**
2067 	 * Executed after all tuples are entered in the queue.
2068 	 * Its main functions are:
2069 	 * <ul>
2070 	 * <li>Assign queue.next to the next element in the queue.
2071 	 * <li>Calls touch(...) per Tuple so that addresses can be normalized/assigned/allocated
2072 	 * properly.
2073 	 * </ul>
2074 	 */
2075 	public void postProcess() {
2076 		if (postProcessed) {
2077 			return;
2078 		}
2079 		if (!queue.isEmpty() && queue.get(0).hasNext()) {
2080 			postProcessed = true;
2081 			return;
2082 		}
2083 		assignSequentialNextPointers();
2084 		for (Tuple tuple : queue) {
2085 			tuple.touch(queue);
2086 		}
2087 		postProcessed = true;
2088 	}
2089 
2090 	/**
2091 	 * Performs tuple queue optimizations such as reachability pruning, redundant
2092 	 * eval-global setup removal, and NOP collapsing.
2093 	 * <p>
2094 	 * This method is idempotent. Repeated invocations after a successful
2095 	 * optimization run will have no additional effect.
2096 	 * </p>
2097 	 * <p>
2098 	 * Peephole optimization happens at the tuple layer instead of during AST
2099 	 * construction. Folding after parsing guarantees that any tuple-level
2100 	 * transformations (for example, address resolution and extension hooks) have
2101 	 * already run, and it keeps a single optimization toggle ({@code optimize()})
2102 	 * for callers. Performing the work at the tuple layer also lets us recurse
2103 	 * until no more changes occur without complicating the parser.
2104 	 * </p>
2105 	 */
2106 	public void optimize() {
2107 		if (optimized) {
2108 			return;
2109 		}
2110 		if (!postProcessed) {
2111 			postProcess();
2112 		}
2113 		boolean queueModified = removeRedundantEvalSetNumGlobals();
2114 		queueModified |= peepholeOptimize();
2115 		if (queueModified) {
2116 			reprocessQueue();
2117 		}
2118 		simplifyControlFlow();
2119 		optimizeQueue();
2120 		optimized = true;
2121 	}
2122 
2123 	/**
2124 	 * Removes the synthetic {@code SET_NUM_GLOBALS} prelude from eval tuple
2125 	 * streams that never touch runtime-stack-backed variables or global metadata.
2126 	 * <p>
2127 	 * Expression compilation always emits the opcode up front, but field-only or
2128 	 * JRT-special-only expressions can execute without initializing the AVM global
2129 	 * frame. Dropping the tuple here keeps the runtime path lean while preserving
2130 	 * the parser's simpler tuple construction flow.
2131 	 * </p>
2132 	 *
2133 	 * @return {@code true} when a redundant eval {@code SET_NUM_GLOBALS} tuple was
2134 	 *         removed
2135 	 */
2136 	private boolean removeRedundantEvalSetNumGlobals() {
2137 		int setNumGlobalsIndex = -1;
2138 		for (int i = 0; i < queue.size(); i++) {
2139 			Opcode opcode = queue.get(i).getOpcode();
2140 			if (opcode == null) {
2141 				continue;
2142 			}
2143 			switch (opcode) {
2144 			case SET_NUM_GLOBALS:
2145 				if (setNumGlobalsIndex != -1) {
2146 					return false;
2147 				}
2148 				setNumGlobalsIndex = i;
2149 				break;
2150 			default:
2151 				if (requiresEvalGlobalFrame(opcode)) {
2152 					return false;
2153 				}
2154 				break;
2155 			}
2156 		}
2157 		if (!evalTupleStream || setNumGlobalsIndex < 0) {
2158 			return false;
2159 		}
2160 
2161 		int[] indexMapping = new int[queue.size()];
2162 		for (int i = 0, nextIndex = 0; i < queue.size(); i++) {
2163 			if (i == setNumGlobalsIndex) {
2164 				indexMapping[i] = nextIndex;
2165 			} else {
2166 				indexMapping[i] = nextIndex++;
2167 			}
2168 		}
2169 		queue.remove(setNumGlobalsIndex);
2170 		remapAddresses(indexMapping);
2171 		return true;
2172 	}
2173 
2174 	private boolean peepholeOptimize() {
2175 		// Keep running the local rewrite pass because one fold can expose another.
2176 		// Example: PUSH 1, PUSH 2, ADD, NEGATE first becomes PUSH 3, NEGATE and
2177 		// only the next pass can fold it to PUSH -3.
2178 		boolean modified = false;
2179 		boolean passModified;
2180 		do {
2181 			passModified = peepholeOptimizePass();
2182 			modified |= passModified;
2183 		} while (passModified);
2184 		return modified;
2185 	}
2186 
2187 	private boolean peepholeOptimizePass() {
2188 		int originalSize = queue.size();
2189 		if (originalSize < 2) {
2190 			return false;
2191 		}
2192 
2193 		List<Tuple> original = new ArrayList<Tuple>(queue);
2194 		int[] indexMapping = new int[originalSize];
2195 		Arrays.fill(indexMapping, -1);
2196 		List<Tuple> optimizedQueue = new ArrayList<Tuple>(originalSize);
2197 		boolean[] isAddressTarget = addressTargets(original, originalSize);
2198 
2199 		boolean modified = false;
2200 		int oldIndex = 0;
2201 		int newIndex = 0;
2202 		while (oldIndex < originalSize) {
2203 			Tuple tuple = original.get(oldIndex);
2204 			// If an earlier rewrite already happened in this pass, wait for the
2205 			// next pass before collapsing concat runs. That gives literal folding
2206 			// priority so fully constant chains become one PUSH_STRING instead of a
2207 			// partially folded PUSH_STRING plus MULTI_CONCAT.
2208 			ConcatRun concatRun = !modified ? concatRun(original, isAddressTarget, oldIndex) : null;
2209 			if (concatRun != null) {
2210 				// Chained concatenations compile as a run of binary CONCAT tuples
2211 				// after all operands have been pushed. Collapse that postfix run into
2212 				// one counted MULTI_CONCAT, e.g. CONCAT, CONCAT, CONCAT ->
2213 				// MULTI_CONCAT 4.
2214 				Tuple replacement = createMultiConcat(concatRun.itemCount, tuple.getLineNumber());
2215 				optimizedQueue.add(replacement);
2216 				mapFoldedRange(indexMapping, oldIndex, concatRun.tupleCount, newIndex);
2217 				oldIndex += concatRun.tupleCount;
2218 				newIndex++;
2219 				modified = true;
2220 				continue;
2221 			}
2222 
2223 			if (tuple.getOpcode() == Opcode.ASSIGN && (oldIndex + 1) < originalSize) {
2224 				Tuple nextTuple = original.get(oldIndex + 1);
2225 				// Statement assignments compile as ASSIGN followed by POP because
2226 				// ASSIGN normally leaves the assigned value on the stack for
2227 				// expression contexts such as print (a = 1). When the result is
2228 				// discarded immediately, replace both opcodes with ASSIGN_NOPUSH
2229 				// unless the POP itself is a branch target. Branches that land on
2230 				// the POP must continue to skip the assignment and only discard the
2231 				// already-computed expression result.
2232 				if (nextTuple.getOpcode() == Opcode.POP && !isAddressTarget[oldIndex + 1]) {
2233 					Tuple replacement = createAssignNoPush(tuple);
2234 					optimizedQueue.add(replacement);
2235 					mapFoldedRange(indexMapping, oldIndex, 2, newIndex);
2236 					oldIndex += 2;
2237 					newIndex++;
2238 					modified = true;
2239 					continue;
2240 				}
2241 			}
2242 
2243 			Object literal = literalValue(tuple);
2244 			if (literal != null) {
2245 				if ((oldIndex + 1) < originalSize) {
2246 					Tuple nextTuple = original.get(oldIndex + 1);
2247 					if (nextTuple.getOpcode() == Opcode.GET_INPUT_FIELD) {
2248 						// Replace PUSH literal + GET_INPUT_FIELD with the constant-field
2249 						// opcode so $1, $2, etc. do not need a stack round trip for the
2250 						// field index.
2251 						long fieldIndex = JRT.toLong(literal);
2252 						Tuple replacement = createGetInputFieldConst(
2253 								fieldIndex,
2254 								tuple.getLineNumber());
2255 						optimizedQueue.add(replacement);
2256 						mapFoldedRange(indexMapping, oldIndex, 2, newIndex);
2257 						oldIndex += 2;
2258 						newIndex++;
2259 						modified = true;
2260 						continue;
2261 					}
2262 				}
2263 				if ((oldIndex + 2) < originalSize) {
2264 					Tuple nextTuple = original.get(oldIndex + 1);
2265 					Tuple opTuple = original.get(oldIndex + 2);
2266 					Object secondLiteral = literalValue(nextTuple);
2267 					if (secondLiteral != null) {
2268 						Object folded = foldBinary(literal, secondLiteral, opTuple);
2269 						if (folded != null) {
2270 							// Fold two literal pushes followed by a pure binary operator
2271 							// into a single literal push, e.g. PUSH 1, PUSH 2, ADD ->
2272 							// PUSH 3.
2273 							Tuple replacement = createLiteralPush(folded, tuple.getLineNumber());
2274 							optimizedQueue.add(replacement);
2275 							mapFoldedRange(indexMapping, oldIndex, 3, newIndex);
2276 							oldIndex += 3;
2277 							newIndex++;
2278 							modified = true;
2279 							continue;
2280 						}
2281 					}
2282 				}
2283 				if ((oldIndex + 1) < originalSize) {
2284 					Tuple opTuple = original.get(oldIndex + 1);
2285 					Object folded = foldUnary(literal, opTuple);
2286 					if (folded != null) {
2287 						// Fold one literal push followed by a pure unary operator into a
2288 						// single literal push, e.g. PUSH 5, NEGATE -> PUSH -5.
2289 						Tuple replacement = createLiteralPush(folded, tuple.getLineNumber());
2290 						optimizedQueue.add(replacement);
2291 						mapFoldedRange(indexMapping, oldIndex, 2, newIndex);
2292 						oldIndex += 2;
2293 						newIndex++;
2294 						modified = true;
2295 						continue;
2296 					}
2297 				}
2298 			}
2299 
2300 			optimizedQueue.add(tuple);
2301 			indexMapping[oldIndex] = newIndex;
2302 			oldIndex++;
2303 			newIndex++;
2304 		}
2305 
2306 		if (!modified) {
2307 			return false;
2308 		}
2309 
2310 		for (int i = 0; i < optimizedQueue.size(); i++) {
2311 			queue.set(i, optimizedQueue.get(i));
2312 		}
2313 		for (int i = queue.size() - 1; i >= optimizedQueue.size(); i--) {
2314 			queue.remove(i);
2315 		}
2316 
2317 		remapAddresses(indexMapping);
2318 		return true;
2319 	}
2320 
2321 	private boolean[] addressTargets(List<Tuple> tuples, int tupleCount) {
2322 		boolean[] targets = new boolean[tupleCount];
2323 		for (Tuple tuple : tuples) {
2324 			for (Address address : tuple.getAddresses()) {
2325 				int index = address.index();
2326 				if (index >= 0 && index < tupleCount) {
2327 					targets[index] = true;
2328 				}
2329 			}
2330 		}
2331 		return targets;
2332 	}
2333 
2334 	private void mapFoldedRange(int[] indexMapping, int startIndex, int length, int newIndex) {
2335 		for (int idx = 0; idx < length; idx++) {
2336 			indexMapping[startIndex + idx] = newIndex;
2337 		}
2338 	}
2339 
2340 	private ConcatRun concatRun(List<Tuple> original, boolean[] isAddressTarget, int oldIndex) {
2341 		Tuple tuple = original.get(oldIndex);
2342 		if (tuple.getOpcode() != Opcode.CONCAT || isAddressTarget[oldIndex]) {
2343 			return null;
2344 		}
2345 
2346 		int itemCount = 2;
2347 		int tupleCount = 1;
2348 		int currentIndex = oldIndex + 1;
2349 		while (currentIndex < original.size()
2350 				&& original.get(currentIndex).getOpcode() == Opcode.CONCAT
2351 				&& !isAddressTarget[currentIndex]) {
2352 			itemCount++;
2353 			tupleCount++;
2354 			currentIndex++;
2355 		}
2356 
2357 		if (tupleCount < 2) {
2358 			return null;
2359 		}
2360 		return new ConcatRun(tupleCount, itemCount);
2361 	}
2362 
2363 	private Object literalValue(Tuple tuple) {
2364 		switch (tuple.getOpcode()) {
2365 		case PUSH_LONG:
2366 			return Long.valueOf(((Tuple.PushLongTuple) tuple).getValue());
2367 		case PUSH_DOUBLE:
2368 			return Double.valueOf(((Tuple.PushDoubleTuple) tuple).getValue());
2369 		case PUSH_STRING:
2370 			return ((Tuple.PushStringTuple) tuple).getValue();
2371 		default:
2372 			return null;
2373 		}
2374 	}
2375 
2376 	private Object foldBinary(Object left, Object right, Tuple operation) {
2377 		Opcode opcode = operation.getOpcode();
2378 		if (opcode == null) {
2379 			return null;
2380 		}
2381 		switch (opcode) {
2382 		case ADD: {
2383 			double d1 = JRT.toDouble(left);
2384 			double d2 = JRT.toDouble(right);
2385 			double ans = d1 + d2;
2386 			if (JRT.isActuallyLong(ans)) {
2387 				return Long.valueOf((long) Math.rint(ans));
2388 			}
2389 			return Double.valueOf(ans);
2390 		}
2391 		case SUBTRACT: {
2392 			double d1 = JRT.toDouble(left);
2393 			double d2 = JRT.toDouble(right);
2394 			double ans = d1 - d2;
2395 			if (JRT.isActuallyLong(ans)) {
2396 				return Long.valueOf((long) Math.rint(ans));
2397 			}
2398 			return Double.valueOf(ans);
2399 		}
2400 		case MULTIPLY: {
2401 			double d1 = JRT.toDouble(left);
2402 			double d2 = JRT.toDouble(right);
2403 			double ans = d1 * d2;
2404 			if (JRT.isActuallyLong(ans)) {
2405 				return Long.valueOf((long) Math.rint(ans));
2406 			}
2407 			return Double.valueOf(ans);
2408 		}
2409 		case DIVIDE: {
2410 			double d1 = JRT.toDouble(left);
2411 			double d2 = JRT.toDouble(right);
2412 			double ans = d1 / d2;
2413 			if (JRT.isActuallyLong(ans)) {
2414 				return Long.valueOf((long) Math.rint(ans));
2415 			}
2416 			return Double.valueOf(ans);
2417 		}
2418 		case MOD: {
2419 			double d1 = JRT.toDouble(left);
2420 			double d2 = JRT.toDouble(right);
2421 			double ans = d1 % d2;
2422 			if (JRT.isActuallyLong(ans)) {
2423 				return Long.valueOf((long) Math.rint(ans));
2424 			}
2425 			return Double.valueOf(ans);
2426 		}
2427 		case POW: {
2428 			double d1 = JRT.toDouble(left);
2429 			double d2 = JRT.toDouble(right);
2430 			double ans = Math.pow(d1, d2);
2431 			if (JRT.isActuallyLong(ans)) {
2432 				return Long.valueOf((long) Math.rint(ans));
2433 			}
2434 			return Double.valueOf(ans);
2435 		}
2436 		case CMP_EQ:
2437 		case CMP_LT:
2438 		case CMP_GT:
2439 			// only numeric comparisons are compile-time constants: string
2440 			// comparisons depend on the runtime IGNORECASE setting
2441 			if (!(left instanceof Number) || !(right instanceof Number)) {
2442 				return null;
2443 			}
2444 			return JRT.compare2(left, right, opcode == Opcode.CMP_EQ ? 0 : opcode == Opcode.CMP_LT ? -1 : 1) ?
2445 					Long.valueOf(1L) : Long.valueOf(0L);
2446 		case CONCAT:
2447 			if (left instanceof String && right instanceof String) {
2448 				return ((String) left) + ((String) right);
2449 			}
2450 			return null;
2451 		default:
2452 			return null;
2453 		}
2454 	}
2455 
2456 	private Object foldUnary(Object literal, Tuple operation) {
2457 		Opcode opcode = operation.getOpcode();
2458 		if (opcode == null) {
2459 			return null;
2460 		}
2461 		switch (opcode) {
2462 		case NEGATE: {
2463 			double value = JRT.toDouble(literal);
2464 			double ans = -value;
2465 			if (JRT.isActuallyLong(ans)) {
2466 				return Long.valueOf((long) Math.rint(ans));
2467 			}
2468 			return Double.valueOf(ans);
2469 		}
2470 		case UNARY_PLUS: {
2471 			double value = JRT.toDouble(literal);
2472 			if (JRT.isActuallyLong(value)) {
2473 				return Long.valueOf((long) Math.rint(value));
2474 			}
2475 			return Double.valueOf(value);
2476 		}
2477 		default:
2478 			return null;
2479 		}
2480 	}
2481 
2482 	private Tuple createLiteralPush(Object value, int lineNumber) {
2483 		Tuple tuple;
2484 		if (value instanceof Long) {
2485 			tuple = new Tuple.PushLongTuple(((Long) value).longValue());
2486 		} else if (value instanceof Integer) {
2487 			tuple = new Tuple.PushLongTuple(((Integer) value).longValue());
2488 		} else if (value instanceof Double) {
2489 			tuple = new Tuple.PushDoubleTuple(((Double) value).doubleValue());
2490 		} else if (value instanceof Number) {
2491 			double d = ((Number) value).doubleValue();
2492 			if (JRT.isActuallyLong(d)) {
2493 				tuple = new Tuple.PushLongTuple((long) Math.rint(d));
2494 			} else {
2495 				tuple = new Tuple.PushDoubleTuple(d);
2496 			}
2497 		} else if (value instanceof String) {
2498 			tuple = new Tuple.PushStringTuple((String) value);
2499 		} else {
2500 			throw new IllegalArgumentException("Unsupported literal value: " + value);
2501 		}
2502 		tuple.setLineNumber(lineNumber);
2503 		return tuple;
2504 	}
2505 
2506 	private Tuple createAssignNoPush(Tuple tuple) {
2507 		Tuple.VariableTuple variableTuple = (Tuple.VariableTuple) tuple;
2508 		Tuple replacement = new Tuple.VariableTuple(
2509 				Opcode.ASSIGN_NOPUSH,
2510 				variableTuple.getVariableOffset(),
2511 				variableTuple.isGlobal());
2512 		replacement.setLineNumber(tuple.getLineNumber());
2513 		return replacement;
2514 	}
2515 
2516 	private Tuple createGetInputFieldConst(long fieldIndex, int lineNumber) {
2517 		Tuple tuple = new Tuple.InputFieldTuple(fieldIndex);
2518 		tuple.setLineNumber(lineNumber);
2519 		return tuple;
2520 	}
2521 
2522 	private Tuple createMultiConcat(int itemCount, int lineNumber) {
2523 		Tuple tuple = new Tuple.CountTuple(Opcode.MULTI_CONCAT, itemCount);
2524 		tuple.setLineNumber(lineNumber);
2525 		return tuple;
2526 	}
2527 
2528 	private static final class ConcatRun {
2529 		private final int tupleCount;
2530 		private final int itemCount;
2531 
2532 		private ConcatRun(int tupleCount, int itemCount) {
2533 			this.tupleCount = tupleCount;
2534 			this.itemCount = itemCount;
2535 		}
2536 	}
2537 
2538 	private void remapAddresses(int[] indexMapping) {
2539 		if (indexMapping.length == 0) {
2540 			return;
2541 		}
2542 		Set<Address> processedAddresses = Collections.newSetFromMap(new IdentityHashMap<Address, Boolean>());
2543 		for (Tuple tuple : queue) {
2544 			for (Address address : tuple.getAddresses()) {
2545 				remapAddress(address, indexMapping, processedAddresses);
2546 			}
2547 		}
2548 		// Property addresses may not be referenced by any tuple (e.g. after
2549 		// jump threading rewired the loop-back GOTO), so they must be
2550 		// remapped explicitly to stay valid.
2551 		remapAddress(exitAddress, indexMapping, processedAddresses);
2552 		remapAddress(endFileAddress, indexMapping, processedAddresses);
2553 		remapAddress(nextFileAddress, indexMapping, processedAddresses);
2554 		addressManager.remapIndexes(indexMapping);
2555 	}
2556 
2557 	private static void seedPropertyAddress(
2558 			Address address,
2559 			int size,
2560 			boolean[] reachable,
2561 			Deque<Integer> worklist) {
2562 		if (address == null) {
2563 			return;
2564 		}
2565 		int targetIndex = address.index();
2566 		if (targetIndex >= 0 && targetIndex < size && !reachable[targetIndex]) {
2567 			reachable[targetIndex] = true;
2568 			worklist.addLast(targetIndex);
2569 		}
2570 	}
2571 
2572 	private static void remapAddress(Address address, int[] indexMapping, Set<Address> processedAddresses) {
2573 		if (address == null || !processedAddresses.add(address)) {
2574 			return;
2575 		}
2576 		int oldIndex = address.index();
2577 		if (oldIndex >= 0 && oldIndex < indexMapping.length) {
2578 			int mappedIndex = indexMapping[oldIndex];
2579 			if (mappedIndex < 0) {
2580 				throw new Error("Address " + address + " references removed tuple " + oldIndex);
2581 			}
2582 			address.assignIndex(mappedIndex);
2583 		}
2584 	}
2585 
2586 	private void reprocessQueue() {
2587 		assignSequentialNextPointers();
2588 		for (Tuple tuple : queue) {
2589 			tuple.touch(queue);
2590 		}
2591 	}
2592 
2593 	private boolean simplifyControlFlow() {
2594 		boolean modified = false;
2595 		boolean passModified;
2596 		do {
2597 			passModified = simplifyControlFlowPass();
2598 			if (passModified) {
2599 				reprocessQueue();
2600 			}
2601 			modified |= passModified;
2602 		} while (passModified);
2603 		return modified;
2604 	}
2605 
2606 	private boolean simplifyControlFlowPass() {
2607 		int size = queue.size();
2608 		if (size < 2) {
2609 			return false;
2610 		}
2611 
2612 		boolean modified = false;
2613 		boolean[] remove = new boolean[size];
2614 		int[] redirectTargets = new int[size];
2615 		int[] visitStamps = new int[size];
2616 		int nextVisitStamp = 1;
2617 		Arrays.fill(redirectTargets, -1);
2618 
2619 		for (int i = 0; i < size; i++) {
2620 			Tuple tuple = queue.get(i);
2621 			Address address = tuple.getAddress();
2622 			if (address != null) {
2623 				int resolvedTarget = resolveJumpEquivalentIndex(
2624 						address.index(),
2625 						size,
2626 						visitStamps,
2627 						nextVisitStamp++);
2628 				if (resolvedTarget >= 0 && resolvedTarget != address.index()) {
2629 					addressManager.reassignAddress(address, resolvedTarget);
2630 					modified = true;
2631 				}
2632 			}
2633 
2634 			switch (tuple.getOpcode()) {
2635 			case NOP: {
2636 				int redirectTarget = resolveJumpEquivalentIndex(
2637 						i + 1,
2638 						size,
2639 						visitStamps,
2640 						nextVisitStamp++);
2641 				if (redirectTarget >= 0) {
2642 					remove[i] = true;
2643 					redirectTargets[i] = redirectTarget;
2644 					modified = true;
2645 				}
2646 				break;
2647 			}
2648 			case GOTO: {
2649 				int target = resolveJumpEquivalentIndex(
2650 						tuple.getAddress().index(),
2651 						size,
2652 						visitStamps,
2653 						nextVisitStamp++);
2654 				int fallthroughTarget = resolveJumpEquivalentIndex(
2655 						i + 1,
2656 						size,
2657 						visitStamps,
2658 						nextVisitStamp++);
2659 				if (target >= 0 && target == fallthroughTarget) {
2660 					remove[i] = true;
2661 					redirectTargets[i] = fallthroughTarget;
2662 					modified = true;
2663 				}
2664 				break;
2665 			}
2666 			default:
2667 				break;
2668 			}
2669 		}
2670 
2671 		if (!modified) {
2672 			return false;
2673 		}
2674 
2675 		boolean anyRemoved = false;
2676 		for (boolean removeTuple : remove) {
2677 			if (removeTuple) {
2678 				anyRemoved = true;
2679 				break;
2680 			}
2681 		}
2682 		if (!anyRemoved) {
2683 			return true;
2684 		}
2685 
2686 		int[] indexMapping = new int[size];
2687 		Arrays.fill(indexMapping, -1);
2688 		int nextIndex = 0;
2689 		for (int i = 0; i < size; i++) {
2690 			if (!remove[i]) {
2691 				indexMapping[i] = nextIndex++;
2692 			}
2693 		}
2694 		for (int i = 0; i < size; i++) {
2695 			if (remove[i] && redirectTargets[i] >= 0) {
2696 				indexMapping[i] = indexMapping[redirectTargets[i]];
2697 			}
2698 		}
2699 
2700 		compactQueue(remove);
2701 
2702 		remapAddresses(indexMapping);
2703 		return true;
2704 	}
2705 
2706 	private int resolveJumpEquivalentIndex(int index, int size, int[] visitStamps, int stamp) {
2707 		if (index < 0 || index >= size) {
2708 			return -1;
2709 		}
2710 		int current = index;
2711 		while (current >= 0 && current < size && visitStamps[current] != stamp) {
2712 			visitStamps[current] = stamp;
2713 			Tuple tuple = queue.get(current);
2714 			switch (tuple.getOpcode()) {
2715 			case NOP:
2716 				current++;
2717 				break;
2718 			case GOTO: {
2719 				Address address = tuple.getAddress();
2720 				if (address == null) {
2721 					return current;
2722 				}
2723 				current = address.index();
2724 				break;
2725 			}
2726 			default:
2727 				return current;
2728 			}
2729 		}
2730 		return -1;
2731 	}
2732 
2733 	private void assignSequentialNextPointers() {
2734 		for (int i = 0; i < queue.size(); i++) {
2735 			Tuple nextTuple = (i + 1) < queue.size() ? queue.get(i + 1) : null;
2736 			queue.get(i).setNext(nextTuple);
2737 		}
2738 	}
2739 
2740 	private void compactQueue(boolean[] remove) {
2741 		ArrayList<Tuple> compactedQueue = new ArrayList<Tuple>(queue.size());
2742 		for (int i = 0; i < remove.length; i++) {
2743 			if (!remove[i]) {
2744 				compactedQueue.add(queue.get(i));
2745 			}
2746 		}
2747 		queue.clear();
2748 		queue.addAll(compactedQueue);
2749 	}
2750 
2751 	private void optimizeQueue() {
2752 		int size = queue.size();
2753 		if (size <= 1) {
2754 			return;
2755 		}
2756 
2757 		boolean[] reachable = new boolean[size];
2758 		int[] referencesFromReachable = new int[size];
2759 
2760 		Deque<Integer> worklist = new ArrayDeque<>();
2761 		if (!queue.isEmpty()) {
2762 			reachable[0] = true;
2763 			worklist.add(0);
2764 		}
2765 		// The property addresses are runtime jump targets (exit, nextfile,
2766 		// and the ENDFILE section it resumes at) that no tuple may reference:
2767 		// treat them as reachability roots so their sections are never
2768 		// eliminated as dead code.
2769 		seedPropertyAddress(exitAddress, size, reachable, worklist);
2770 		seedPropertyAddress(endFileAddress, size, reachable, worklist);
2771 		seedPropertyAddress(nextFileAddress, size, reachable, worklist);
2772 
2773 		while (!worklist.isEmpty()) {
2774 			int index = worklist.removeFirst();
2775 			Tuple tuple = queue.get(index);
2776 
2777 			if (fallsThrough(tuple.getOpcode())) {
2778 				Tuple nextTuple = tuple.getNext();
2779 				if (nextTuple != null) {
2780 					int nextIndex = index + 1;
2781 					if (!reachable[nextIndex]) {
2782 						reachable[nextIndex] = true;
2783 						worklist.addLast(nextIndex);
2784 					}
2785 				}
2786 			}
2787 
2788 			for (Address address : tuple.getAddresses()) {
2789 				int targetIndex = address.index();
2790 				if (targetIndex < 0 || targetIndex >= size) {
2791 					throw new Error("address " + address + " doesn't resolve to an actual list element");
2792 				}
2793 				referencesFromReachable[targetIndex]++;
2794 				if (!reachable[targetIndex]) {
2795 					reachable[targetIndex] = true;
2796 					worklist.addLast(targetIndex);
2797 				}
2798 			}
2799 		}
2800 
2801 		for (int i = 0; i < size; i++) {
2802 			if (!reachable[i] && referencesFromReachable[i] > 0) {
2803 				reachable[i] = true;
2804 				worklist.addLast(i);
2805 			}
2806 		}
2807 
2808 		while (!worklist.isEmpty()) {
2809 			int index = worklist.removeFirst();
2810 			Tuple tuple = queue.get(index);
2811 
2812 			if (fallsThrough(tuple.getOpcode())) {
2813 				Tuple nextTuple = tuple.getNext();
2814 				if (nextTuple != null) {
2815 					int nextIndex = index + 1;
2816 					if (!reachable[nextIndex]) {
2817 						reachable[nextIndex] = true;
2818 						worklist.addLast(nextIndex);
2819 					}
2820 				}
2821 			}
2822 
2823 			for (Address address : tuple.getAddresses()) {
2824 				int targetIndex = address.index();
2825 				if (targetIndex < 0 || targetIndex >= size) {
2826 					throw new Error("address " + address + " doesn't resolve to an actual list element");
2827 				}
2828 				referencesFromReachable[targetIndex]++;
2829 				if (!reachable[targetIndex]) {
2830 					reachable[targetIndex] = true;
2831 					worklist.addLast(targetIndex);
2832 				}
2833 			}
2834 		}
2835 
2836 		boolean anyRemoved = false;
2837 		boolean[] remove = new boolean[size];
2838 		for (int i = 0; i < size; i++) {
2839 			if (!reachable[i]) {
2840 				remove[i] = true;
2841 				anyRemoved = true;
2842 				continue;
2843 			}
2844 			Tuple tuple = queue.get(i);
2845 			if (tuple.getOpcode() == Opcode.NOP && referencesFromReachable[i] == 0) {
2846 				remove[i] = true;
2847 				anyRemoved = true;
2848 			}
2849 		}
2850 
2851 		if (!anyRemoved) {
2852 			return;
2853 		}
2854 
2855 		int[] indexMapping = new int[size];
2856 		int nextIndex = 0;
2857 		for (int i = 0; i < size; i++) {
2858 			if (remove[i]) {
2859 				indexMapping[i] = -1;
2860 			} else {
2861 				indexMapping[i] = nextIndex++;
2862 			}
2863 		}
2864 
2865 		compactQueue(remove);
2866 
2867 		if (!queue.isEmpty()) {
2868 			assignSequentialNextPointers();
2869 		}
2870 
2871 		remapAddresses(indexMapping);
2872 	}
2873 
2874 	private boolean fallsThrough(Opcode opcode) {
2875 		if (opcode == null) {
2876 			return true;
2877 		}
2878 		switch (opcode) {
2879 		case GOTO:
2880 		case EXIT_WITH_CODE:
2881 		case EXIT_WITHOUT_CODE:
2882 			return false;
2883 		default:
2884 			return true;
2885 		}
2886 	}
2887 
2888 	/** Map of global variables offsets */
2889 	private Map<String, Integer> globalVarOffsetMap = new HashMap<String, Integer>();
2890 
2891 	/** Map of global arrays */
2892 	private Map<String, Boolean> globalVarAarrayMap = new HashMap<String, Boolean>();
2893 
2894 	/** List of user function names */
2895 	private Set<String> functionNames = new HashSet<String>();
2896 
2897 	/** Whether metadata collections are frozen for execution. */
2898 	private boolean metadataFrozen;
2899 
2900 	/**
2901 	 * Accept a {variable_name -&gt; offset} mapping such that global variables can be
2902 	 * assigned while processing name=value and filename command-line arguments.
2903 	 *
2904 	 * @param varname Name of the global variable
2905 	 * @param offset What offset to use for the variable
2906 	 * @param isArray Whether the variable is actually an array
2907 	 */
2908 	public void addGlobalVariableNameToOffsetMapping(String varname, int offset, boolean isArray) {
2909 		ensureMetadataMutable();
2910 		if (globalVarOffsetMap.get(varname) != null) {
2911 			return;
2912 		}
2913 		globalVarOffsetMap.put(varname, offset);
2914 		globalVarAarrayMap.put(varname, isArray);
2915 	}
2916 
2917 	/**
2918 	 * Accept a set of function names from the parser. This is
2919 	 * useful for invalidating name=value assignments from the
2920 	 * command line parameters, either via -v arguments or
2921 	 * passed into ARGV.
2922 	 *
2923 	 * @param names A set of function name strings.
2924 	 */
2925 	public void setFunctionNameSet(Set<String> names) {
2926 		ensureMetadataMutable();
2927 		// setFunctionNameSet is called with a keySet from
2928 		// a HashMap as a parameter, which is Opcode.NOT
2929 		// Serializable. Creating a new HashSet around
2930 		// the parameter resolves the issue.
2931 		// Otherwise, attempting to serialize this
2932 		// object results in a NotSerializableEexception
2933 		// being thrown because of functionNames field
2934 		// being a keyset from a HashMap.
2935 		this.functionNames = new HashSet<String>(names);
2936 	}
2937 
2938 	/**
2939 	 * Freezes the tuple metadata after compilation so execution can reuse the
2940 	 * published maps and sets without creating fresh unmodifiable wrappers.
2941 	 * Repeated calls are ignored.
2942 	 */
2943 	public void freezeMetadata() {
2944 		if (metadataFrozen) {
2945 			return;
2946 		}
2947 		globalVarOffsetMap = freezeMap(globalVarOffsetMap);
2948 		globalVarAarrayMap = freezeMap(globalVarAarrayMap);
2949 		functionNames = freezeSet(functionNames);
2950 		metadataFrozen = true;
2951 	}
2952 
2953 	/**
2954 	 * <p>
2955 	 * getGlobalVariableOffsetMap.
2956 	 * </p>
2957 	 *
2958 	 * @return a {@link java.util.Map} object
2959 	 */
2960 	@SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "freezeMetadata() replaces this field with an unmodifiable snapshot before compiled tuples are exposed")
2961 	public Map<String, Integer> getGlobalVariableOffsetMap() {
2962 		return globalVarOffsetMap;
2963 	}
2964 
2965 	/**
2966 	 * <p>
2967 	 * getGlobalVariableAarrayMap.
2968 	 * </p>
2969 	 *
2970 	 * @return a {@link java.util.Map} object
2971 	 */
2972 	@SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "freezeMetadata() replaces this field with an unmodifiable snapshot before compiled tuples are exposed")
2973 	public Map<String, Boolean> getGlobalVariableAarrayMap() {
2974 		return globalVarAarrayMap;
2975 	}
2976 
2977 	/**
2978 	 * <p>
2979 	 * getFunctionNameSet.
2980 	 * </p>
2981 	 *
2982 	 * @return a {@link java.util.Set} object
2983 	 */
2984 	@SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "freezeMetadata() replaces this field with an unmodifiable snapshot before compiled tuples are exposed")
2985 	public Set<String> getFunctionNameSet() {
2986 		return functionNames;
2987 	}
2988 
2989 	private void ensureMetadataMutable() {
2990 		if (metadataFrozen) {
2991 			throw new IllegalStateException("Tuple metadata is frozen.");
2992 		}
2993 	}
2994 
2995 	private static <K, V> Map<K, V> freezeMap(Map<K, V> map) {
2996 		if (map.isEmpty()) {
2997 			return Collections.emptyMap();
2998 		}
2999 		return Collections.unmodifiableMap(new HashMap<K, V>(map));
3000 	}
3001 
3002 	private static <T> Set<T> freezeSet(Set<T> set) {
3003 		if (set.isEmpty()) {
3004 			return Collections.emptySet();
3005 		}
3006 		return Collections.unmodifiableSet(new HashSet<T>(set));
3007 	}
3008 
3009 	private boolean requiresEvalGlobalFrame(Opcode opcode) {
3010 		switch (opcode) {
3011 		case ASSIGN:
3012 		case ASSIGN_NOPUSH:
3013 		case ASSIGN_ARRAY:
3014 		case DEREFERENCE:
3015 		case PEEK_DEREFERENCE:
3016 		case PUSH_INDIRECT_ARGUMENT:
3017 		case PLUS_EQ:
3018 		case MINUS_EQ:
3019 		case MULT_EQ:
3020 		case DIV_EQ:
3021 		case MOD_EQ:
3022 		case POW_EQ:
3023 		case PLUS_EQ_ARRAY:
3024 		case MINUS_EQ_ARRAY:
3025 		case MULT_EQ_ARRAY:
3026 		case DIV_EQ_ARRAY:
3027 		case MOD_EQ_ARRAY:
3028 		case POW_EQ_ARRAY:
3029 		case CALL_FUNCTION:
3030 		case INDIRECT_CALL:
3031 			// extension calls read globals (e.g. IGNORECASE) and their
3032 			// beforeStart hooks assign gawk-owned arrays
3033 		case EXTENSION:
3034 		case SET_RETURN_RESULT:
3035 		case RETURN_FROM_FUNCTION:
3036 		case MATCH:
3037 		case DELETE_ARRAY_ELEMENT:
3038 		case DELETE_ARRAY:
3039 		case ENVIRON_OFFSET:
3040 		case ARGC_OFFSET:
3041 		case ARGV_OFFSET:
3042 		case ASSIGN_ARGC:
3043 		case PUSH_ARGC:
3044 			return true;
3045 		default:
3046 			return false;
3047 		}
3048 	}
3049 
3050 	/** linenumber stack ... */
3051 	private Deque<Integer> linenoStack = new ArrayDeque<Integer>();
3052 
3053 	/**
3054 	 * Push the current line number onto the line number stack.
3055 	 * This is called by the parser to keep track of the
3056 	 * current source line number. Keeping track of line
3057 	 * numbers this way allows the runtime to report
3058 	 * more meaningful errors by providing source line numbers
3059 	 * within error reports.
3060 	 *
3061 	 * @param lineno The current source line number.
3062 	 */
3063 	public void pushSourceLineNumber(int lineno) {
3064 		linenoStack.push(lineno);
3065 	}
3066 
3067 	/**
3068 	 * <p>
3069 	 * popSourceLineNumber.
3070 	 * </p>
3071 	 *
3072 	 * @param lineno a int
3073 	 */
3074 	public void popSourceLineNumber(int lineno) {
3075 		linenoStack.pop();
3076 	}
3077 
3078 }