1 package io.jawk.backend;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 import java.io.Closeable;
26 import java.io.IOException;
27 import java.io.PrintStream;
28 import java.util.Arrays;
29 import java.util.HashSet;
30 import java.util.LinkedHashMap;
31 import java.util.LinkedHashSet;
32 import java.util.Objects;
33 import java.util.ArrayDeque;
34 import java.util.ArrayList;
35 import java.util.Collections;
36 import java.util.Deque;
37 import java.util.Enumeration;
38 import java.util.HashMap;
39 import java.util.List;
40 import java.util.Locale;
41 import java.util.Map;
42 import java.util.Set;
43 import java.util.StringTokenizer;
44 import java.util.regex.Matcher;
45 import java.util.regex.Pattern;
46 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
47 import io.jawk.AwkExpression;
48 import io.jawk.AwkProgram;
49 import io.jawk.AwkSandboxException;
50 import io.jawk.ExitException;
51 import io.jawk.ext.AbstractExtension;
52 import io.jawk.ext.ExtensionFunction;
53 import io.jawk.ext.JawkExtension;
54 import io.jawk.intermediate.Address;
55 import io.jawk.intermediate.Opcode;
56 import io.jawk.intermediate.PositionTracker;
57 import io.jawk.intermediate.Tuple;
58 import io.jawk.intermediate.Tuple.BooleanTuple;
59 import io.jawk.intermediate.Tuple.CallFunctionTuple;
60 import io.jawk.intermediate.Tuple.ClassTuple;
61 import io.jawk.intermediate.Tuple.CountAndAppendTuple;
62 import io.jawk.intermediate.Tuple.CountTuple;
63 import io.jawk.intermediate.Tuple.DereferenceTuple;
64 import io.jawk.intermediate.Tuple.ExtensionTuple;
65 import io.jawk.intermediate.Tuple.InputFieldTuple;
66 import io.jawk.intermediate.Tuple.LongTuple;
67 import io.jawk.intermediate.Tuple.PushDoubleTuple;
68 import io.jawk.intermediate.Tuple.PushLongTuple;
69 import io.jawk.intermediate.Tuple.PushStringTuple;
70 import io.jawk.intermediate.Tuple.RegexTuple;
71 import io.jawk.intermediate.Tuple.SubstitutionVariableTuple;
72 import io.jawk.intermediate.Tuple.VariableTuple;
73 import io.jawk.intermediate.UninitializedObject;
74 import io.jawk.jrt.AssocArray;
75 import io.jawk.jrt.AwkRuntimeException;
76 import io.jawk.jrt.AwkSink;
77 import io.jawk.jrt.BlockManager;
78 import io.jawk.jrt.BlockObject;
79 import io.jawk.jrt.CharacterTokenizer;
80 import io.jawk.jrt.ConditionPair;
81 import io.jawk.jrt.InputSource;
82 import io.jawk.jrt.StreamInputSource;
83 import io.jawk.jrt.JRT;
84 import io.jawk.jrt.RegexTokenizer;
85 import io.jawk.jrt.SingleCharacterTokenizer;
86 import io.jawk.jrt.VariableManager;
87 import io.jawk.util.AwkSettings;
88 import io.jawk.jrt.BSDRandom;
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122 public class AVM implements VariableManager, Closeable {
123
124 private RuntimeStack runtimeStack = new RuntimeStack();
125
126
127 private Deque<Object> operandStack = new ArrayDeque<Object>();
128 private List<String> arguments;
129 private boolean sortedArrayKeys;
130 private final Map<String, Object> baseInitialVariables;
131 private final Map<String, Object> baseSpecialVariables;
132 private Map<String, Object> executionInitialVariables;
133 private Map<String, Object> executionSpecialVariables;
134 private JRT jrt;
135 private Map<String, JawkExtension> extensionInstances;
136
137 private static final Object NULL_OPERAND = new Object();
138
139
140
141
142 private Object pop() {
143 Object value = operandStack.pop();
144 return value == NULL_OPERAND ? null : value;
145 }
146
147 private void push(Object o) {
148 operandStack.push(o == null ? NULL_OPERAND : o);
149 }
150
151 private final AwkSettings settings;
152 private final boolean profiling;
153 private final Map<Opcode, ProfilingReport.Accumulator> tupleProfilingStats;
154 private final Map<String, ProfilingReport.Accumulator> functionProfilingStats;
155 private final Deque<ActiveFunction> activeProfilingFunctions;
156 private boolean inputSourceFilelistAssignmentsApplied;
157 private InputSource resolvedInputSource;
158 private AwkExpression installedEvalExpression;
159 private boolean mergedGlobalLayoutActive;
160
161
162
163
164
165
166
167 public AVM() {
168 this(null, Collections.<String, JawkExtension>emptyMap());
169 }
170
171
172
173
174
175
176
177
178
179 public AVM(final AwkSettings parameters,
180 final Map<String, JawkExtension> extensionInstances) {
181 this(parameters, extensionInstances, false);
182 }
183
184
185
186
187
188
189
190
191
192 public AVM(
193 final AwkSettings parameters,
194 final Map<String, JawkExtension> extensionInstances,
195 final boolean profilingEnabled) {
196 this.settings = parameters != null ? parameters : AwkSettings.DEFAULT_SETTINGS;
197 this.extensionInstances = extensionInstances == null ?
198 Collections.<String, JawkExtension>emptyMap() : extensionInstances;
199 this.profiling = profilingEnabled;
200 if (profilingEnabled) {
201 this.tupleProfilingStats = new java.util.EnumMap<Opcode, ProfilingReport.Accumulator>(Opcode.class);
202 this.functionProfilingStats = new LinkedHashMap<String, ProfilingReport.Accumulator>();
203 this.activeProfilingFunctions = new ArrayDeque<ActiveFunction>();
204 } else {
205 this.tupleProfilingStats = null;
206 this.functionProfilingStats = null;
207 this.activeProfilingFunctions = null;
208 }
209
210 arguments = Collections.emptyList();
211 sortedArrayKeys = this.settings.isUseSortedArrayKeys();
212 baseInitialVariables = new HashMap<String, Object>(this.settings.getVariables());
213 baseSpecialVariables = JRT.copySpecialVariables(baseInitialVariables);
214 executionInitialVariables = baseInitialVariables;
215 executionSpecialVariables = baseSpecialVariables;
216
217 jrt = createJrt();
218 initExtensions();
219 }
220
221 protected JRT createJrt() {
222 return new JRT(this, this.settings.getLocale(), AwkSink.NOP_SINK, null);
223 }
224
225
226
227
228
229
230 protected AwkSettings getSettings() {
231 return settings;
232 }
233
234
235
236
237
238
239 @SuppressFBWarnings("EI_EXPOSE_REP")
240 public JRT getJrt() {
241 return jrt;
242 }
243
244
245
246
247
248
249
250 public void setAwkSink(AwkSink sink) {
251 jrt.setAwkSink(Objects.requireNonNull(sink, "sink"));
252 }
253
254
255
256
257
258
259
260 public void setErrorStream(PrintStream errorStream) {
261 jrt.setErrorStream(errorStream);
262 }
263
264
265
266
267
268
269 public AwkSink getAwkSink() {
270 return jrt.getAwkSink();
271 }
272
273
274
275
276
277
278 protected Locale getLocale() {
279 return jrt.getLocale();
280 }
281
282
283
284
285
286
287
288
289
290 public Object eval(AwkExpression expression) throws IOException {
291 AwkExpression compiledExpression = Objects.requireNonNull(expression, "expression");
292 installExpressionMetadata(compiledExpression);
293
294 try {
295 executeTuples(compiledExpression.top());
296 } catch (ExitException e) {
297
298
299 throwExitException = false;
300 exitCode = 0;
301 throw new IllegalStateException("eval(AwkExpression) cannot execute EXIT opcodes.", e);
302 }
303 return operandStack.isEmpty() ? null : JRT.toJavaScalar(pop());
304 }
305
306
307
308
309
310
311
312
313
314 public Object eval(AwkExpression expression, InputSource inputSource) throws IOException {
315 return eval(expression, inputSource, null);
316 }
317
318
319
320
321
322
323
324
325
326
327
328
329 public Object eval(
330 AwkExpression expression,
331 InputSource inputSource,
332 Map<String, Object> variableOverrides)
333 throws IOException {
334 prepareForEval(inputSource, Collections.<String>emptyList(), variableOverrides);
335 return eval(expression);
336 }
337
338
339
340
341
342
343
344
345
346 public void execute(AwkProgram program, InputSource inputSource) throws ExitException, IOException {
347 execute(program, inputSource, Collections.<String>emptyList(), null);
348 }
349
350
351
352
353
354
355
356
357
358
359 public void execute(AwkProgram program, InputSource inputSource, List<String> runtimeArguments)
360 throws ExitException,
361 IOException {
362 execute(program, inputSource, runtimeArguments, null);
363 }
364
365
366
367
368
369
370
371
372
373
374
375
376
377 public void execute(
378 AwkProgram program,
379 InputSource inputSource,
380 List<String> runtimeArguments,
381 Map<String, Object> variableOverrides)
382 throws ExitException,
383 IOException {
384 AwkProgram compiledProgram = Objects.requireNonNull(program, "program");
385 InputSource resolvedSource = Objects.requireNonNull(inputSource, "inputSource");
386 resetRuntimeState(runtimeArguments, variableOverrides);
387 installProgramMetadata(compiledProgram);
388
389 jrt.prepareForExecution(settings.getFieldSeparator(), settings.getDefaultRS());
390 if (!executionSpecialVariables.isEmpty()) {
391 jrt.applySpecialVariables(executionSpecialVariables);
392 }
393 rebindResolvedInputSource(resolvedSource);
394 executeTuples(compiledProgram.top());
395 }
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410 public void executePersistingGlobals(AwkProgram program, InputSource inputSource)
411 throws ExitException,
412 IOException {
413 executePersistingGlobals(program, inputSource, Collections.<String>emptyList(), null);
414 }
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430 public void executePersistingGlobals(
431 AwkProgram program,
432 InputSource inputSource,
433 List<String> runtimeArguments)
434 throws ExitException,
435 IOException {
436 executePersistingGlobals(program, inputSource, runtimeArguments, null);
437 }
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455 public void executePersistingGlobals(
456 AwkProgram program,
457 InputSource inputSource,
458 List<String> runtimeArguments,
459 Map<String, Object> variableOverrides)
460 throws ExitException,
461 IOException {
462 AwkProgram compiledProgram = Objects.requireNonNull(program, "program");
463 InputSource resolvedSource = Objects.requireNonNull(inputSource, "inputSource");
464 mergeRuntimeState(runtimeArguments, variableOverrides, compiledProgram);
465
466 jrt.prepareForExecution(settings.getFieldSeparator(), settings.getDefaultRS());
467 if (!executionSpecialVariables.isEmpty()) {
468 jrt.applySpecialVariables(executionSpecialVariables);
469 }
470 rebindResolvedInputSource(resolvedSource);
471 executeTuples(compiledProgram.top());
472 }
473
474
475
476
477
478
479
480 public void clearPersistentGlobals() {
481 runtimeStack.clearGlobals();
482 mergedGlobalLayoutActive = false;
483 }
484
485
486
487
488
489
490
491
492
493
494 public Map<String, Object> snapshotPersistentMemory() {
495 return new LinkedHashMap<>(collectPersistentGlobalValues());
496 }
497
498
499
500
501
502
503
504
505
506
507
508 public void restorePersistentMemory(Map<String, Object> snapshot) {
509 Map<String, Object> restoredSnapshot = Objects.requireNonNull(snapshot, "snapshot");
510 Map<String, Object> restoredGlobals = filterToPersistentEligible(restoredSnapshot);
511 runtimeStack.clearGlobals();
512 if (!restoredGlobals.isEmpty()) {
513 runtimeStack.rebindGlobals(new ArrayList<>(restoredGlobals.keySet()));
514 applyGlobalsToStack(restoredGlobals);
515 }
516 mergedGlobalLayoutActive = false;
517 }
518
519 private void initExtensions() {
520 if (extensionInstances.isEmpty()) {
521 return;
522 }
523 Set<JawkExtension> initialized = new LinkedHashSet<JawkExtension>();
524 for (JawkExtension extension : extensionInstances.values()) {
525 if (initialized.add(extension)) {
526 extension.init(this, jrt, settings);
527 }
528 }
529 }
530
531
532
533 private long environOffset = NULL_OFFSET;
534 private long argcOffset = NULL_OFFSET;
535 private long argvOffset = NULL_OFFSET;
536
537 private static final Integer ZERO = Integer.valueOf(0);
538 private static final Integer ONE = Integer.valueOf(1);
539
540
541 private final BSDRandom randomNumberGenerator = new BSDRandom(1);
542
543
544
545
546
547
548
549
550
551 private int oldseed = 1;
552
553 private Address exitAddress = null;
554
555
556
557
558
559 private boolean withinEndBlocks = false;
560
561
562
563
564 private int exitCode = 0;
565
566
567
568
569 private boolean throwExitException = false;
570
571
572
573
574
575
576 private Map<String, Integer> globalVariableOffsets;
577
578
579
580
581 private Map<String, Boolean> globalVariableArrays;
582 private Set<String> functionNames = Collections.emptySet();
583 private Map<String, Integer> initializedEvalGlobalVariableOffsets;
584 private Map<String, Boolean> initializedEvalGlobalVariableArrays;
585
586
587
588
589
590
591
592
593
594
595 public boolean prepareForEval(String input) throws IOException {
596 return prepareForEval(new SingleRecordInputSource(input), Collections.<String>emptyList(), null);
597 }
598
599
600
601
602
603
604
605
606
607
608
609 public boolean prepareForEval(InputSource inputSource) throws IOException {
610 return prepareForEval(inputSource, Collections.<String>emptyList(), null);
611 }
612
613 private boolean prepareForEval(
614 InputSource inputSource,
615 List<String> runtimeArguments,
616 Map<String, Object> variableOverrides)
617 throws IOException {
618 InputSource resolvedSource = Objects.requireNonNull(inputSource, "inputSource");
619 resetRuntimeState(runtimeArguments, variableOverrides);
620 rebindResolvedInputSource(resolvedSource);
621
622 jrt.jrtCloseAll();
623 jrt.prepareForExecution(settings.getFieldSeparator(), settings.getDefaultRS());
624 if (!executionSpecialVariables.isEmpty()) {
625 jrt.applySpecialVariables(executionSpecialVariables);
626 }
627 return jrt.consumeInputForEval(resolvedInputSource);
628 }
629
630 private void resetRuntimeState(List<String> runtimeArguments, Map<String, Object> variableOverrides) {
631 resetTransientRuntimeState(runtimeArguments, variableOverrides);
632 runtimeStack.clearGlobals();
633 }
634
635 private void resetTransientRuntimeState(List<String> runtimeArguments, Map<String, Object> variableOverrides) {
636
637 operandStack.clear();
638 environOffset = NULL_OFFSET;
639 argcOffset = NULL_OFFSET;
640 argvOffset = NULL_OFFSET;
641 exitAddress = null;
642 withinEndBlocks = false;
643 exitCode = 0;
644 throwExitException = false;
645 inputSourceFilelistAssignmentsApplied = false;
646 globalVariableOffsets = null;
647 globalVariableArrays = null;
648 functionNames = Collections.emptySet();
649 initializedEvalGlobalVariableOffsets = null;
650 initializedEvalGlobalVariableArrays = null;
651 installedEvalExpression = null;
652 mergedGlobalLayoutActive = false;
653 runtimeStack.resetTransientState();
654 randomNumberGenerator.setSeed(1);
655 oldseed = 1;
656
657 prepareExecutionInputs(runtimeArguments, variableOverrides);
658 }
659
660 private void installExpressionMetadata(AwkExpression compiledExpression) {
661 if (installedEvalExpression == compiledExpression) {
662 return;
663 }
664 globalVariableOffsets = compiledExpression.getGlobalVariableOffsetMap();
665 globalVariableArrays = compiledExpression.getGlobalVariableAarrayMap();
666 functionNames = compiledExpression.getFunctionNameSet();
667 installedEvalExpression = compiledExpression;
668 }
669
670 private void installProgramMetadata(AwkProgram compiledProgram) {
671 globalVariableOffsets = compiledProgram.getGlobalVariableOffsetMap();
672 globalVariableArrays = compiledProgram.getGlobalVariableAarrayMap();
673 functionNames = compiledProgram.getFunctionNameSet();
674 }
675
676 private void rebindResolvedInputSource(InputSource resolvedSource) {
677 InputSource previousResolvedSource = resolvedInputSource;
678 if (previousResolvedSource != null && previousResolvedSource != resolvedSource) {
679 closeInputSource(previousResolvedSource);
680 }
681 resolvedInputSource = resolvedSource;
682 }
683
684 private boolean hasCompatibleEvalGlobalLayout(long numGlobals) {
685 Object[] globals = runtimeStack.getNumGlobals();
686 return globals != null
687 && globals.length == numGlobals
688 && Objects.equals(initializedEvalGlobalVariableOffsets, globalVariableOffsets)
689 && Objects.equals(initializedEvalGlobalVariableArrays, globalVariableArrays);
690 }
691
692
693
694
695
696
697
698
699
700
701 private void mergeRuntimeState(
702 List<String> runtimeArguments,
703 Map<String, Object> variableOverrides,
704 AwkProgram compiledProgram) {
705 Map<String, Object> carriedGlobals = collectPersistentGlobalValues();
706 resetTransientRuntimeState(runtimeArguments, variableOverrides);
707 installProgramMetadata(compiledProgram);
708
709 Map<String, Object> basePersistentSeeds = collectBasePersistentGlobalSeeds();
710 Map<String, Object> executionUserSeeds = collectExecutionUserGlobalSeeds(variableOverrides);
711 List<String> mergedGlobalNamesByOffset = buildMergedGlobalNamesByOffset(
712 carriedGlobals,
713 basePersistentSeeds,
714 executionUserSeeds);
715
716 runtimeStack.rebindGlobals(mergedGlobalNamesByOffset);
717 applyGlobalsToStack(carriedGlobals);
718 applyGlobalsToStack(basePersistentSeeds);
719 applyGlobalsToStack(executionUserSeeds);
720 mergedGlobalLayoutActive = true;
721 }
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736 private boolean hasCompatiblePersistentGlobalLayout(long numGlobals) {
737 Object[] globals = runtimeStack.getNumGlobals();
738 if (!mergedGlobalLayoutActive
739 || globals == null
740 || globalVariableOffsets == null
741 || globals.length < numGlobals) {
742 return false;
743 }
744 for (Map.Entry<String, Integer> entry : globalVariableOffsets.entrySet()) {
745 int offset = entry.getValue().intValue();
746 if (offset < 0 || offset >= globals.length || !entry.getKey().equals(runtimeStack.getGlobalName(offset))) {
747 return false;
748 }
749 }
750 return true;
751 }
752
753
754
755
756
757
758
759
760
761
762
763
764 private void applyExecutionInitialVariablesToGlobalSlots(boolean skipPersistentEligibleGlobals) {
765 for (Map.Entry<String, Object> entry : executionInitialVariables.entrySet()) {
766 String key = entry.getKey();
767 if (skipPersistentEligibleGlobals && isPersistentEligibleGlobal(key)) {
768 continue;
769 }
770 if (functionNames.contains(key)) {
771 throw new IllegalArgumentException("Cannot assign a scalar to a function name (" + key + ").");
772 }
773 Integer offsetObj = globalVariableOffsets.get(key);
774 Boolean arrayObj = globalVariableArrays.get(key);
775 if (offsetObj != null) {
776 Object obj = normalizeExternalVariableValue(entry.getValue());
777 if (arrayObj.booleanValue()) {
778 if (obj instanceof Map) {
779 runtimeStack.setFilelistVariable(offsetObj.intValue(), obj);
780 } else {
781 throw new IllegalArgumentException(
782 "Cannot assign a scalar to a non-scalar variable (" + key + ").");
783 }
784 } else {
785 runtimeStack.setFilelistVariable(offsetObj.intValue(), obj);
786 }
787 }
788 }
789 }
790
791
792
793
794
795
796
797
798
799
800 private void prepareExecutionInputs(
801 List<String> runtimeArguments,
802 Map<String, Object> variableOverrides) {
803 this.arguments = runtimeArguments != null ? new ArrayList<>(runtimeArguments) : Collections.<String>emptyList();
804
805 if (variableOverrides == null || variableOverrides.isEmpty()) {
806 executionInitialVariables = baseInitialVariables;
807 executionSpecialVariables = baseSpecialVariables;
808 } else {
809 executionInitialVariables = new HashMap<>(baseInitialVariables);
810 executionInitialVariables.putAll(variableOverrides);
811
812 Map<String, Object> specialOverrides = JRT.copySpecialVariables(variableOverrides);
813 if (specialOverrides.isEmpty()) {
814 executionSpecialVariables = baseSpecialVariables;
815 } else {
816 executionSpecialVariables = new HashMap<>(baseSpecialVariables);
817 executionSpecialVariables.putAll(specialOverrides);
818 }
819 }
820 }
821
822
823
824
825
826
827
828
829 private Map<String, Object> filterToPersistentEligible(Map<String, Object> source) {
830 Map<String, Object> result = new LinkedHashMap<>();
831 for (Map.Entry<String, Object> entry : source.entrySet()) {
832 if (isPersistentEligibleGlobal(entry.getKey())) {
833 result.put(entry.getKey(), entry.getValue());
834 }
835 }
836 return result;
837 }
838
839
840
841
842
843
844 private Map<String, Object> collectPersistentGlobalValues() {
845 return filterToPersistentEligible(runtimeStack.snapshotGlobalVariables());
846 }
847
848
849
850
851
852
853
854 private Map<String, Object> collectBasePersistentGlobalSeeds() {
855 Map<String, Object> basePersistentSeeds = new LinkedHashMap<>();
856 for (Map.Entry<String, Object> entry : baseInitialVariables.entrySet()) {
857 String name = entry.getKey();
858 if (isPersistentEligibleGlobal(name)) {
859 validateSeededGlobalName(name);
860 Object value = normalizeExternalVariableValue(entry.getValue());
861 validateSeededGlobalValue(name, value);
862 basePersistentSeeds.put(name, value);
863 }
864 }
865 return basePersistentSeeds;
866 }
867
868
869
870
871
872
873
874
875
876
877
878 private Map<String, Object> collectExecutionUserGlobalSeeds(Map<String, Object> variableOverrides) {
879 Map<String, Object> executionUserSeeds = new LinkedHashMap<>();
880 if (variableOverrides != null) {
881 for (Map.Entry<String, Object> entry : variableOverrides.entrySet()) {
882 String name = entry.getKey();
883 if (isPersistentEligibleGlobal(name)) {
884 validateSeededGlobalName(name);
885 Object value = normalizeExternalVariableValue(entry.getValue());
886 validateSeededGlobalValue(name, value);
887 executionUserSeeds.put(name, value);
888 }
889 }
890 }
891 return executionUserSeeds;
892 }
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907 private List<String> buildMergedGlobalNamesByOffset(
908 Map<String, Object> carriedGlobals,
909 Map<String, Object> basePersistentSeeds,
910 Map<String, Object> executionUserSeeds) {
911 LinkedHashSet<String> orderedNames = new LinkedHashSet<>();
912 List<Map.Entry<String, Integer>> compiledGlobals = new ArrayList<>(globalVariableOffsets.entrySet());
913 compiledGlobals.sort(java.util.Comparator.comparingInt(Map.Entry::getValue));
914 for (Map.Entry<String, Integer> entry : compiledGlobals) {
915 orderedNames.add(entry.getKey());
916 }
917 orderedNames.addAll(carriedGlobals.keySet());
918 orderedNames.addAll(basePersistentSeeds.keySet());
919 orderedNames.addAll(executionUserSeeds.keySet());
920 return new ArrayList<>(orderedNames);
921 }
922
923
924
925
926
927
928
929 private void applyGlobalsToStack(Map<String, Object> globals) {
930 for (Map.Entry<String, Object> entry : globals.entrySet()) {
931 runtimeStack.setGlobalVariable(entry.getKey(), entry.getValue());
932 }
933 }
934
935
936
937
938
939
940
941
942 private boolean isPersistentEligibleGlobal(String name) {
943 return name != null
944 && !JRT.isJrtManagedSpecialVariable(name)
945 && !NON_PERSISTENT_GLOBALS.contains(name);
946 }
947
948
949
950
951
952
953
954
955 private void validateSeededGlobalName(String name) {
956 if (functionNames.contains(name)) {
957 throw new IllegalArgumentException("Cannot assign a value to a function name (" + name + ").");
958 }
959 }
960
961
962
963
964
965
966
967
968 private void validateSeededGlobalValue(String name, Object value) {
969 Boolean arrayObj = globalVariableArrays.get(name);
970 if (Boolean.TRUE.equals(arrayObj) && !(value instanceof Map)) {
971 throw new IllegalArgumentException("Cannot assign a scalar to a non-scalar variable (" + name + ").");
972 }
973 }
974
975
976
977
978
979
980
981 private NameValueAssignment parseNameValueAssignment(String nameValue) {
982 int eqIdx = nameValue.indexOf('=');
983 if (eqIdx == 0) {
984 throw new IllegalArgumentException(
985 "Must have a non-blank variable name in a name=value variable assignment argument.");
986 }
987 String name = nameValue.substring(0, eqIdx);
988 String value = nameValue.substring(eqIdx + 1);
989 return new NameValueAssignment(name, jrt.toInputScalar(value));
990 }
991
992
993
994
995
996
997
998
999 private void executeTuples(PositionTracker position)
1000 throws ExitException,
1001 IOException {
1002 Map<Integer, ConditionPair> conditionPairs = null;
1003 Opcode opcode = null;
1004 long tupleStartNanos = 0L;
1005 try {
1006 while (!position.isEOF()) {
1007
1008 Tuple tuple = position.current();
1009 opcode = tuple.getOpcode();
1010 if (profiling) {
1011 tupleStartNanos = beforeProfiledTuple(tuple, opcode);
1012 }
1013
1014 switch (opcode) {
1015 case PRINT: {
1016 execPrint((CountTuple) tuple);
1017 position.next();
1018 break;
1019 }
1020 case PRINT_TO_FILE: {
1021 execPrintToFile((CountAndAppendTuple) tuple);
1022 position.next();
1023 break;
1024 }
1025 case PRINT_TO_PIPE: {
1026 execPrintToPipe((CountTuple) tuple);
1027 position.next();
1028 break;
1029 }
1030 case PRINTF: {
1031 execPrintf((CountTuple) tuple);
1032 position.next();
1033 break;
1034 }
1035 case PRINTF_TO_FILE: {
1036 execPrintfToFile((CountAndAppendTuple) tuple);
1037 position.next();
1038 break;
1039 }
1040 case PRINTF_TO_PIPE: {
1041 execPrintfToPipe((CountTuple) tuple);
1042 position.next();
1043 break;
1044 }
1045 case SPRINTF: {
1046
1047
1048
1049
1050 CountTuple countTuple = (CountTuple) tuple;
1051 long numArgs = countTuple.getCount();
1052 push(sprintfFunction(numArgs));
1053 position.next();
1054 break;
1055 }
1056 case LENGTH: {
1057 execLength((CountTuple) tuple);
1058 position.next();
1059 break;
1060 }
1061 case PUSH_LONG: {
1062
1063 PushLongTuple pushTuple = (PushLongTuple) tuple;
1064 push(pushTuple.getValue());
1065 position.next();
1066 break;
1067 }
1068 case PUSH_DOUBLE: {
1069
1070 PushDoubleTuple pushTuple = (PushDoubleTuple) tuple;
1071 push(pushTuple.getValue());
1072 position.next();
1073 break;
1074 }
1075 case PUSH_STRING: {
1076
1077 PushStringTuple pushTuple = (PushStringTuple) tuple;
1078 push(pushTuple.getValue());
1079 position.next();
1080 break;
1081 }
1082 case POP: {
1083
1084 pop();
1085 position.next();
1086 break;
1087 }
1088 case IFFALSE: {
1089
1090
1091
1092
1093
1094
1095 boolean jump = !jrt.toBoolean(pop());
1096 if (jump) {
1097 position.jump(tuple.getAddress());
1098 } else {
1099 position.next();
1100 }
1101 break;
1102 }
1103 case TO_NUMBER: {
1104
1105
1106
1107
1108
1109 boolean val = jrt.toBoolean(pop());
1110 push(val ? ONE : ZERO);
1111 position.next();
1112 break;
1113 }
1114 case IFTRUE: {
1115
1116
1117
1118
1119
1120
1121 boolean jump = jrt.toBoolean(pop());
1122 if (jump) {
1123 position.jump(tuple.getAddress());
1124 } else {
1125 position.next();
1126 }
1127 break;
1128 }
1129 case NOT: {
1130
1131
1132 Object o = pop();
1133
1134 boolean result = jrt.toBoolean(o);
1135
1136 if (result) {
1137 push(0);
1138 } else {
1139 push(1);
1140 }
1141 position.next();
1142 break;
1143 }
1144 case NEGATE: {
1145
1146
1147 double d = JRT.toDouble(pop());
1148 push(-d);
1149 position.next();
1150 break;
1151 }
1152 case UNARY_PLUS: {
1153
1154 double d = JRT.toDouble(pop());
1155 push(d);
1156 position.next();
1157 break;
1158 }
1159 case GOTO: {
1160
1161
1162 position.jump(tuple.getAddress());
1163 break;
1164 }
1165 case NOP: {
1166
1167 position.next();
1168 break;
1169 }
1170 case CONCAT: {
1171
1172
1173 String s2 = jrt.toAwkString(pop());
1174 String s1 = jrt.toAwkString(pop());
1175 String resultString = s1 + s2;
1176 push(resultString);
1177 position.next();
1178 break;
1179 }
1180 case MULTI_CONCAT: {
1181
1182
1183 CountTuple countTuple = (CountTuple) tuple;
1184 int count = (int) countTuple.getCount();
1185
1186
1187
1188
1189 String[] values = new String[count];
1190 int resultLength = 0;
1191 for (int i = count - 1; i >= 0; i--) {
1192 values[i] = jrt.toAwkString(pop());
1193 resultLength += values[i].length();
1194 }
1195 StringBuilder resultString = new StringBuilder(resultLength);
1196 for (String value : values) {
1197 resultString.append(value);
1198 }
1199 push(resultString.toString());
1200 position.next();
1201 break;
1202 }
1203 case ASSIGN:
1204 case ASSIGN_NOPUSH: {
1205
1206
1207
1208 VariableTuple variableTuple = (VariableTuple) tuple;
1209 Object value = pop();
1210 assign(
1211 variableTuple.getVariableOffset(),
1212 value,
1213 variableTuple.isGlobal(),
1214 position,
1215 opcode == Opcode.ASSIGN);
1216 position.next();
1217 break;
1218 }
1219 case ASSIGN_ARRAY: {
1220
1221
1222
1223
1224 Object arrIdx = pop();
1225 Object rhs = pop();
1226 if (rhs == null) {
1227 rhs = BLANK;
1228 }
1229 VariableTuple variableTuple = (VariableTuple) tuple;
1230 long offset = variableTuple.getVariableOffset();
1231 boolean isGlobal = variableTuple.isGlobal();
1232 assignArray(offset, arrIdx, rhs, isGlobal);
1233 position.next();
1234 break;
1235 }
1236 case ASSIGN_MAP_ELEMENT: {
1237
1238
1239
1240 Object arrIdx = pop();
1241 Map<Object, Object> array = toMap(pop());
1242 Object rhs = pop();
1243 if (rhs == null) {
1244 rhs = BLANK;
1245 }
1246 assignMapElement(array, arrIdx, rhs);
1247 position.next();
1248 break;
1249 }
1250 case PLUS_EQ_ARRAY:
1251 case MINUS_EQ_ARRAY:
1252 case MULT_EQ_ARRAY:
1253 case DIV_EQ_ARRAY:
1254 case MOD_EQ_ARRAY:
1255 case POW_EQ_ARRAY: {
1256
1257
1258
1259
1260 Object arrIdx = pop();
1261 Object rhs = pop();
1262 if (rhs == null) {
1263 rhs = BLANK;
1264 }
1265 VariableTuple variableTuple = (VariableTuple) tuple;
1266 long offset = variableTuple.getVariableOffset();
1267 boolean isGlobal = variableTuple.isGlobal();
1268
1269 double val = JRT.toDouble(rhs);
1270
1271 Map<Object, Object> array = ensureMapVariable(offset, isGlobal);
1272 checkScalar(arrIdx);
1273 Object o = array.get(arrIdx);
1274 double origVal = JRT.toDouble(o);
1275
1276 double newVal;
1277
1278 switch (opcode) {
1279 case PLUS_EQ_ARRAY:
1280 newVal = origVal + val;
1281 break;
1282 case MINUS_EQ_ARRAY:
1283 newVal = origVal - val;
1284 break;
1285 case MULT_EQ_ARRAY:
1286 newVal = origVal * val;
1287 break;
1288 case DIV_EQ_ARRAY:
1289 newVal = origVal / val;
1290 break;
1291 case MOD_EQ_ARRAY:
1292 newVal = origVal % val;
1293 break;
1294 case POW_EQ_ARRAY:
1295 newVal = Math.pow(origVal, val);
1296 break;
1297 default:
1298 throw new Error("Invalid op code here: " + opcode);
1299 }
1300
1301 assignArray(offset, arrIdx, newVal, isGlobal);
1302 position.next();
1303 break;
1304 }
1305 case PLUS_EQ_MAP_ELEMENT:
1306 case MINUS_EQ_MAP_ELEMENT:
1307 case MULT_EQ_MAP_ELEMENT:
1308 case DIV_EQ_MAP_ELEMENT:
1309 case MOD_EQ_MAP_ELEMENT:
1310 case POW_EQ_MAP_ELEMENT: {
1311
1312
1313
1314 Object arrIdx = pop();
1315 Map<Object, Object> array = toMap(pop());
1316 Object rhs = pop();
1317 if (rhs == null) {
1318 rhs = BLANK;
1319 }
1320
1321 double val = JRT.toDouble(rhs);
1322 checkScalar(arrIdx);
1323 Object o = array.get(arrIdx);
1324 double origVal = JRT.toDouble(o);
1325 double newVal;
1326
1327 switch (opcode) {
1328 case PLUS_EQ_MAP_ELEMENT:
1329 newVal = origVal + val;
1330 break;
1331 case MINUS_EQ_MAP_ELEMENT:
1332 newVal = origVal - val;
1333 break;
1334 case MULT_EQ_MAP_ELEMENT:
1335 newVal = origVal * val;
1336 break;
1337 case DIV_EQ_MAP_ELEMENT:
1338 newVal = origVal / val;
1339 break;
1340 case MOD_EQ_MAP_ELEMENT:
1341 newVal = origVal % val;
1342 break;
1343 case POW_EQ_MAP_ELEMENT:
1344 newVal = Math.pow(origVal, val);
1345 break;
1346 default:
1347 throw new Error("Invalid op code here: " + opcode);
1348 }
1349
1350 assignMapElement(array, arrIdx, newVal);
1351 position.next();
1352 break;
1353 }
1354
1355 case ASSIGN_AS_INPUT: {
1356
1357 jrt.setInputLine(pop());
1358 push(jrt.getInputLine());
1359 position.next();
1360 break;
1361 }
1362
1363 case ASSIGN_AS_INPUT_FIELD: {
1364
1365
1366 Object fieldNumObj = pop();
1367 long fieldNum = JRT.parseFieldNumber(fieldNumObj);
1368 Object value = pop();
1369 push(value);
1370 if (fieldNum == 0) {
1371 jrt.setInputLine(value);
1372 jrt.jrtParseFields();
1373 } else {
1374 jrt.jrtSetInputField(value, fieldNum);
1375 }
1376 position.next();
1377 break;
1378 }
1379 case PLUS_EQ:
1380 case MINUS_EQ:
1381 case MULT_EQ:
1382 case DIV_EQ:
1383 case MOD_EQ:
1384 case POW_EQ: {
1385
1386
1387
1388 VariableTuple variableTuple = (VariableTuple) tuple;
1389 long offset = variableTuple.getVariableOffset();
1390 boolean isGlobal = variableTuple.isGlobal();
1391 Object o1 = runtimeStack.getVariable(offset, isGlobal);
1392 if (o1 == null) {
1393 o1 = BLANK;
1394 }
1395 Object o2 = pop();
1396 double d1 = JRT.toDouble(o1);
1397 double d2 = JRT.toDouble(o2);
1398 double ans;
1399 switch (opcode) {
1400 case PLUS_EQ:
1401 ans = d1 + d2;
1402 break;
1403 case MINUS_EQ:
1404 ans = d1 - d2;
1405 break;
1406 case MULT_EQ:
1407 ans = d1 * d2;
1408 break;
1409 case DIV_EQ:
1410 ans = d1 / d2;
1411 break;
1412 case MOD_EQ:
1413 ans = d1 % d2;
1414 break;
1415 case POW_EQ:
1416 ans = Math.pow(d1, d2);
1417 break;
1418 default:
1419 throw new Error("Invalid opcode here: " + opcode);
1420 }
1421 push(ans);
1422 runtimeStack.setVariable(offset, ans, isGlobal);
1423 position.next();
1424 break;
1425 }
1426 case PLUS_EQ_INPUT_FIELD:
1427 case MINUS_EQ_INPUT_FIELD:
1428 case MULT_EQ_INPUT_FIELD:
1429 case DIV_EQ_INPUT_FIELD:
1430 case MOD_EQ_INPUT_FIELD:
1431 case POW_EQ_INPUT_FIELD: {
1432
1433
1434
1435
1436 long fieldnum = JRT.parseFieldNumber(pop());
1437 double incval = JRT.toDouble(pop());
1438
1439
1440 Object numObj = jrt.jrtGetInputField(fieldnum);
1441 double num;
1442 switch (opcode) {
1443 case PLUS_EQ_INPUT_FIELD:
1444 num = JRT.toDouble(numObj) + incval;
1445 break;
1446 case MINUS_EQ_INPUT_FIELD:
1447 num = JRT.toDouble(numObj) - incval;
1448 break;
1449 case MULT_EQ_INPUT_FIELD:
1450 num = JRT.toDouble(numObj) * incval;
1451 break;
1452 case DIV_EQ_INPUT_FIELD:
1453 num = JRT.toDouble(numObj) / incval;
1454 break;
1455 case MOD_EQ_INPUT_FIELD:
1456 num = JRT.toDouble(numObj) % incval;
1457 break;
1458 case POW_EQ_INPUT_FIELD:
1459 num = Math.pow(JRT.toDouble(numObj), incval);
1460 break;
1461 default:
1462 throw new Error("Invalid opcode here: " + opcode);
1463 }
1464 setNumOnJRT(fieldnum, num);
1465
1466
1467 push(num);
1468 position.next();
1469
1470 break;
1471 }
1472 case INC: {
1473
1474
1475 VariableTuple variableTuple = (VariableTuple) tuple;
1476 inc(variableTuple.getVariableOffset(), variableTuple.isGlobal());
1477 position.next();
1478 break;
1479 }
1480 case DEC: {
1481
1482
1483 VariableTuple variableTuple = (VariableTuple) tuple;
1484 dec(variableTuple.getVariableOffset(), variableTuple.isGlobal());
1485 position.next();
1486 break;
1487 }
1488 case POSTINC: {
1489
1490
1491 pop();
1492 VariableTuple variableTuple = (VariableTuple) tuple;
1493 push(inc(variableTuple.getVariableOffset(), variableTuple.isGlobal()));
1494 position.next();
1495 break;
1496 }
1497 case POSTDEC: {
1498
1499
1500 pop();
1501 VariableTuple variableTuple = (VariableTuple) tuple;
1502 push(dec(variableTuple.getVariableOffset(), variableTuple.isGlobal()));
1503 position.next();
1504 break;
1505 }
1506 case INC_ARRAY_REF: {
1507
1508
1509
1510 VariableTuple variableTuple = (VariableTuple) tuple;
1511 boolean isGlobal = variableTuple.isGlobal();
1512 Map<Object, Object> aa = ensureMapVariable(variableTuple.getVariableOffset(), isGlobal);
1513 Object key = pop();
1514 checkScalar(key);
1515 Object o = aa.get(key);
1516 double ans = JRT.toDouble(o) + 1;
1517 aa.put(key, ans);
1518 position.next();
1519 break;
1520 }
1521 case DEC_ARRAY_REF: {
1522
1523
1524
1525 VariableTuple variableTuple = (VariableTuple) tuple;
1526 boolean isGlobal = variableTuple.isGlobal();
1527 Map<Object, Object> aa = ensureMapVariable(variableTuple.getVariableOffset(), isGlobal);
1528 Object key = pop();
1529 checkScalar(key);
1530 Object o = aa.get(key);
1531 double ans = JRT.toDouble(o) - 1;
1532 aa.put(key, ans);
1533 position.next();
1534 break;
1535 }
1536 case INC_MAP_REF: {
1537
1538
1539 Object key = pop();
1540 checkScalar(key);
1541 Map<Object, Object> aa = toMap(pop());
1542 Object o = aa.get(key);
1543 double ans = JRT.toDouble(o) + 1;
1544 aa.put(key, ans);
1545 position.next();
1546 break;
1547 }
1548 case DEC_MAP_REF: {
1549
1550
1551 Object key = pop();
1552 checkScalar(key);
1553 Map<Object, Object> aa = toMap(pop());
1554 Object o = aa.get(key);
1555 double ans = JRT.toDouble(o) - 1;
1556 aa.put(key, ans);
1557 position.next();
1558 break;
1559 }
1560 case INC_DOLLAR_REF: {
1561
1562 long fieldnum = JRT.parseFieldNumber(pop());
1563
1564 Object numObj = jrt.jrtGetInputField(fieldnum);
1565 double original = JRT.toDouble(numObj);
1566 double num = original + 1;
1567 setNumOnJRT(fieldnum, num);
1568
1569 push(Double.valueOf(original));
1570
1571 position.next();
1572 break;
1573 }
1574 case DEC_DOLLAR_REF: {
1575
1576
1577 long fieldnum = JRT.parseFieldNumber(pop());
1578
1579 Object numObj = jrt.jrtGetInputField(fieldnum);
1580 double original = JRT.toDouble(numObj);
1581 double num = original - 1;
1582 setNumOnJRT(fieldnum, num);
1583
1584 push(Double.valueOf(original));
1585
1586 position.next();
1587 break;
1588 }
1589 case DEREFERENCE: {
1590
1591
1592 DereferenceTuple dereferenceTuple = (DereferenceTuple) tuple;
1593 boolean isGlobal = dereferenceTuple.isGlobal();
1594 long offset = dereferenceTuple.getVariableOffset();
1595 Object o = runtimeStack.getVariable(offset, isGlobal);
1596 if (o == null) {
1597 if (dereferenceTuple.isArray()) {
1598
1599 push(runtimeStack.setVariable(offset, newAwkArray(), isGlobal));
1600 } else {
1601 push(runtimeStack.setVariable(offset, BLANK, isGlobal));
1602 }
1603 } else {
1604 push(o);
1605 }
1606 position.next();
1607 break;
1608 }
1609 case DEREF_ARRAY: {
1610
1611 Object idx = pop();
1612 checkScalar(idx);
1613 Map<Object, Object> map = toMap(pop());
1614 Object o = JRT.getAwkValue(map, idx);
1615 push(o);
1616 position.next();
1617 break;
1618 }
1619 case ENSURE_ARRAY_ELEMENT: {
1620
1621
1622 Object idx = pop();
1623 Map<Object, Object> map = toMap(pop());
1624 push(ensureArrayInArray(map, idx));
1625 position.next();
1626 break;
1627 }
1628 case PEEK_ARRAY_ELEMENT: {
1629
1630 Object idx = pop();
1631 checkScalar(idx);
1632 Map<Object, Object> map = toMap(pop());
1633 if (map instanceof AssocArray && !JRT.containsAwkKey(map, idx)) {
1634 push(BLANK);
1635 } else {
1636 Object value = map.get(idx);
1637 push(value != null ? value : BLANK);
1638 }
1639 position.next();
1640 break;
1641 }
1642 case SRAND: {
1643
1644
1645 CountTuple countTuple = (CountTuple) tuple;
1646 long numArgs = countTuple.getCount();
1647 int seed;
1648 if (numArgs == 0) {
1649
1650 seed = JRT.timeSeed();
1651 } else {
1652 Object o = pop();
1653 if (o instanceof Double) {
1654 seed = ((Double) o).intValue();
1655 } else if (o instanceof Long) {
1656 seed = ((Long) o).intValue();
1657 } else if (o instanceof Integer) {
1658 seed = ((Integer) o).intValue();
1659 } else {
1660 try {
1661 seed = Integer.parseInt(o.toString());
1662 } catch (NumberFormatException nfe) {
1663 seed = 0;
1664 }
1665 }
1666 }
1667 randomNumberGenerator.setSeed(seed);
1668 push(oldseed);
1669 oldseed = seed;
1670 position.next();
1671 break;
1672 }
1673 case RAND: {
1674 push(randomNumberGenerator.nextDouble());
1675 position.next();
1676 break;
1677 }
1678 case INTFUNC: {
1679
1680 push((long) JRT.toDouble(pop()));
1681 position.next();
1682 break;
1683 }
1684 case SQRT: {
1685
1686 push(Math.sqrt(JRT.toDouble(pop())));
1687 position.next();
1688 break;
1689 }
1690 case LOG: {
1691
1692 push(Math.log(JRT.toDouble(pop())));
1693 position.next();
1694 break;
1695 }
1696 case EXP: {
1697
1698 push(Math.exp(JRT.toDouble(pop())));
1699 position.next();
1700 break;
1701 }
1702 case SIN: {
1703
1704 push(Math.sin(JRT.toDouble(pop())));
1705 position.next();
1706 break;
1707 }
1708 case COS: {
1709
1710 push(Math.cos(JRT.toDouble(pop())));
1711 position.next();
1712 break;
1713 }
1714 case ATAN2: {
1715
1716
1717 double d2 = JRT.toDouble(pop());
1718 double d1 = JRT.toDouble(pop());
1719 push(Math.atan2(d1, d2));
1720 position.next();
1721 break;
1722 }
1723 case MATCH: {
1724 execMatch();
1725 position.next();
1726 break;
1727 }
1728 case INDEX: {
1729
1730
1731 String s2 = jrt.toAwkString(pop());
1732 String s1 = jrt.toAwkString(pop());
1733 push(s1.indexOf(s2) + 1);
1734 position.next();
1735 break;
1736 }
1737 case SUB_FOR_DOLLAR_0: {
1738 execSubForDollar0((BooleanTuple) tuple);
1739 position.next();
1740 break;
1741 }
1742 case SUB_FOR_DOLLAR_REFERENCE: {
1743 execSubForDollarReference((BooleanTuple) tuple);
1744 position.next();
1745 break;
1746 }
1747 case SUB_FOR_VARIABLE: {
1748 execSubForVariable((SubstitutionVariableTuple) tuple, position);
1749 position.next();
1750 break;
1751 }
1752 case SUB_FOR_ARRAY_REFERENCE: {
1753 execSubForArrayReference((SubstitutionVariableTuple) tuple);
1754 position.next();
1755 break;
1756 }
1757 case SUB_FOR_MAP_REFERENCE: {
1758 execSubForMapReference((BooleanTuple) tuple);
1759 position.next();
1760 break;
1761 }
1762 case SPLIT: {
1763 execSplit((CountTuple) tuple, position);
1764 position.next();
1765 break;
1766 }
1767 case SUBSTR: {
1768 execSubstr((CountTuple) tuple);
1769 position.next();
1770 break;
1771 }
1772 case TOLOWER: {
1773
1774 push(jrt.toAwkString(pop()).toLowerCase());
1775 position.next();
1776 break;
1777 }
1778 case TOUPPER: {
1779
1780 push(jrt.toAwkString(pop()).toUpperCase());
1781 position.next();
1782 break;
1783 }
1784 case SYSTEM: {
1785
1786 String s = jrt.toAwkString(pop());
1787 push(jrt.jrtSystem(s));
1788 position.next();
1789 break;
1790 }
1791 case SWAP: {
1792
1793
1794 Object o1 = pop();
1795 Object o2 = pop();
1796 push(o1);
1797 push(o2);
1798 position.next();
1799 break;
1800 }
1801 case CMP_EQ: {
1802
1803
1804 Object o2 = pop();
1805 Object o1 = pop();
1806 push(JRT.compare2(o1, o2, 0) ? ONE : ZERO);
1807 position.next();
1808 break;
1809 }
1810 case CMP_LT: {
1811
1812
1813 Object o2 = pop();
1814 Object o1 = pop();
1815 push(JRT.compare2(o1, o2, -1) ? ONE : ZERO);
1816 position.next();
1817 break;
1818 }
1819 case CMP_GT: {
1820
1821
1822 Object o2 = pop();
1823 Object o1 = pop();
1824 push(JRT.compare2(o1, o2, 1) ? ONE : ZERO);
1825 position.next();
1826 break;
1827 }
1828 case MATCHES: {
1829
1830
1831 Object o2 = pop();
1832 Object o1 = pop();
1833
1834 String s = o1.toString();
1835
1836 if (o2 instanceof Pattern) {
1837 Pattern p = (Pattern) o2;
1838 Matcher m = p.matcher(s);
1839
1840
1841 boolean result = m.find();
1842 push(result ? 1 : 0);
1843 } else {
1844 String r = jrt.toAwkString(o2);
1845 boolean result = Pattern.compile(r).matcher(s).find();
1846 push(result ? 1 : 0);
1847 }
1848 position.next();
1849 break;
1850 }
1851 case ADD: {
1852
1853
1854 Object o2 = pop();
1855 Object o1 = pop();
1856 double d1 = JRT.toDouble(o1);
1857 double d2 = JRT.toDouble(o2);
1858 double ans = d1 + d2;
1859 push(ans);
1860 position.next();
1861 break;
1862 }
1863 case SUBTRACT: {
1864
1865
1866 Object o2 = pop();
1867 Object o1 = pop();
1868 double d1 = JRT.toDouble(o1);
1869 double d2 = JRT.toDouble(o2);
1870 double ans = d1 - d2;
1871 push(ans);
1872 position.next();
1873 break;
1874 }
1875 case MULTIPLY: {
1876
1877
1878 Object o2 = pop();
1879 Object o1 = pop();
1880 double d1 = JRT.toDouble(o1);
1881 double d2 = JRT.toDouble(o2);
1882 double ans = d1 * d2;
1883 push(ans);
1884 position.next();
1885 break;
1886 }
1887 case DIVIDE: {
1888
1889
1890 Object o2 = pop();
1891 Object o1 = pop();
1892 double d1 = JRT.toDouble(o1);
1893 double d2 = JRT.toDouble(o2);
1894 double ans = d1 / d2;
1895 push(ans);
1896 position.next();
1897 break;
1898 }
1899 case MOD: {
1900
1901
1902 Object o2 = pop();
1903 Object o1 = pop();
1904 double d1 = JRT.toDouble(o1);
1905 double d2 = JRT.toDouble(o2);
1906 double ans = d1 % d2;
1907 push(ans);
1908 position.next();
1909 break;
1910 }
1911 case POW: {
1912
1913
1914 Object o2 = pop();
1915 Object o1 = pop();
1916 double d1 = JRT.toDouble(o1);
1917 double d2 = JRT.toDouble(o2);
1918 double ans = Math.pow(d1, d2);
1919 push(ans);
1920 position.next();
1921 break;
1922 }
1923 case DUP: {
1924
1925 Object o = pop();
1926 push(o);
1927 push(o);
1928 position.next();
1929 break;
1930 }
1931 case KEYLIST: {
1932 Object o = pop();
1933 if (o == null || o instanceof UninitializedObject) {
1934 push(new ArrayDeque<>());
1935 position.next();
1936 break;
1937 }
1938 if (!(o instanceof Map)) {
1939 throw new AwkRuntimeException(
1940 position.lineNumber(),
1941 "Cannot get a key list (via 'in') of a non associative array. arg = " + o.getClass() + ", " + o);
1942 }
1943 @SuppressWarnings("unchecked")
1944 Map<Object, Object> map = (Map<Object, Object>) o;
1945 push(new ArrayDeque<>(map.keySet()));
1946 position.next();
1947 break;
1948 }
1949 case IS_EMPTY_KEYLIST: {
1950
1951
1952 Object o = pop();
1953 if (o == null || !(o instanceof Deque)) {
1954 throw new AwkRuntimeException(
1955 position.lineNumber(),
1956 "Cannot get a key list (via 'in') of a non associative array. arg = " + o.getClass() + ", " + o);
1957 }
1958 Deque<?> keylist = (Deque<?>) o;
1959 if (keylist.isEmpty()) {
1960 position.jump(tuple.getAddress());
1961 } else {
1962 position.next();
1963 }
1964 break;
1965 }
1966 case GET_FIRST_AND_REMOVE_FROM_KEYLIST: {
1967
1968 Object o = pop();
1969 if (o == null || !(o instanceof Deque)) {
1970 throw new AwkRuntimeException(
1971 position.lineNumber(),
1972 "Cannot get a key list (via 'in') of a non associative array. arg = " + o.getClass() + ", " + o);
1973 }
1974
1975 Deque<?> keylist = (Deque<?>) o;
1976 push(keylist.removeFirst());
1977 position.next();
1978 break;
1979 }
1980 case CHECK_CLASS: {
1981
1982
1983 ClassTuple checkTuple = (ClassTuple) tuple;
1984 Object o = pop();
1985 if (!checkTuple.getType().isInstance(o)) {
1986 throw new AwkRuntimeException(
1987 position.lineNumber(),
1988 "Verification failed. Top-of-stack = " + o.getClass() + " isn't an instance of "
1989 + checkTuple.getType());
1990 }
1991 push(o);
1992 position.next();
1993 break;
1994 }
1995 case CONSUME_INPUT: {
1996
1997
1998 applyInputSourceFilelistAssignmentsIfNeeded();
1999 if (jrt.consumeInput(resolvedInputSource)) {
2000 position.next();
2001 } else {
2002 position.jump(tuple.getAddress());
2003 }
2004 break;
2005 }
2006
2007 case GETLINE_INPUT: {
2008 applyInputSourceFilelistAssignmentsIfNeeded();
2009 push(jrt.consumeInput(resolvedInputSource) ? 1 : 0);
2010 position.next();
2011 break;
2012 }
2013 case GETLINE_INPUT_TO_TARGET: {
2014 applyInputSourceFilelistAssignmentsIfNeeded();
2015 Object input = jrt.consumeInputToTarget(resolvedInputSource);
2016 if (input != null) {
2017 push(1);
2018 push(input);
2019 } else {
2020 push(0);
2021 push("");
2022 }
2023 position.next();
2024 break;
2025 }
2026 case USE_AS_FILE_INPUT: {
2027
2028 String s = jrt.toAwkString(pop());
2029 if (jrt.jrtConsumeFileInput(s)) {
2030 push(1);
2031 push(jrt.getInputLine());
2032 } else {
2033 push(0);
2034 push("");
2035 }
2036 position.next();
2037 break;
2038 }
2039 case USE_AS_COMMAND_INPUT: {
2040
2041 String s = jrt.toAwkString(pop());
2042 if (jrt.jrtConsumeCommandInput(s)) {
2043 push(1);
2044 push(jrt.getInputLine());
2045 } else {
2046 push(0);
2047 push("");
2048 }
2049 position.next();
2050 break;
2051 }
2052 case ENVIRON_OFFSET: {
2053
2054
2055 LongTuple offsetTuple = (LongTuple) tuple;
2056 environOffset = offsetTuple.getValue();
2057
2058 Map<String, String> env = System.getenv();
2059 for (Map.Entry<String, String> var : env.entrySet()) {
2060 assignArray(environOffset, var.getKey(), jrt.toInputScalar(var.getValue()), true);
2061 pop();
2062 }
2063 position.next();
2064 break;
2065 }
2066 case ARGC_OFFSET: {
2067
2068 LongTuple offsetTuple = (LongTuple) tuple;
2069 argcOffset = offsetTuple.getValue();
2070
2071
2072 assign(argcOffset, arguments.size() + 1, true, position, false);
2073 position.next();
2074 break;
2075 }
2076 case ARGV_OFFSET: {
2077
2078 LongTuple offsetTuple = (LongTuple) tuple;
2079 argvOffset = offsetTuple.getValue();
2080
2081 int argc = (int) JRT.toDouble(runtimeStack.getVariable(argcOffset, true));
2082 assignArray(argvOffset, 0, "jawk", true);
2083 pop();
2084 for (int i = 1; i < argc; i++) {
2085
2086 assignArray(argvOffset, i, jrt.toInputScalar(arguments.get(i - 1)), true);
2087 pop();
2088 }
2089 position.next();
2090 break;
2091 }
2092 case GET_INPUT_FIELD: {
2093
2094 Object fieldNumber = pop();
2095 push(jrt.jrtGetInputField(fieldNumber));
2096 position.next();
2097 break;
2098 }
2099 case GET_INPUT_FIELD_CONST: {
2100 InputFieldTuple inputFieldTuple = (InputFieldTuple) tuple;
2101 long fieldnum = inputFieldTuple.getFieldIndex();
2102 push(jrt.jrtGetInputField(fieldnum));
2103 position.next();
2104 break;
2105 }
2106 case APPLY_RS: {
2107 jrt.applyRS(jrt.getRSVar());
2108 position.next();
2109 break;
2110 }
2111 case CALL_FUNCTION: {
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121 CallFunctionTuple callTuple = (CallFunctionTuple) tuple;
2122 Address funcAddr = callTuple.getAddress();
2123 long numFormalParams = callTuple.getNumFormalParams();
2124 long numActualParams = callTuple.getNumActualParams();
2125 runtimeStack.pushFrame(numFormalParams, position.currentIndex());
2126
2127 for (long i = numActualParams - 1; i >= 0; i--) {
2128 runtimeStack.setVariable(i, pop(), false);
2129 }
2130 position.jump(funcAddr);
2131
2132 break;
2133 }
2134 case FUNCTION: {
2135
2136
2137
2138
2139 position.next();
2140 break;
2141 }
2142 case SET_RETURN_RESULT: {
2143
2144 runtimeStack.setReturnValue(pop());
2145 position.next();
2146 break;
2147 }
2148 case RETURN_FROM_FUNCTION: {
2149 position.jump(runtimeStack.popFrame());
2150 push(runtimeStack.getReturnValue());
2151 position.next();
2152 break;
2153 }
2154 case SET_NUM_GLOBALS: {
2155 execSetNumGlobals((CountTuple) tuple);
2156 position.next();
2157 break;
2158 }
2159 case CLOSE: {
2160
2161 String s = jrt.toAwkString(pop());
2162 push(jrt.jrtClose(s));
2163 position.next();
2164 break;
2165 }
2166 case APPLY_SUBSEP: {
2167 execApplySubsep((CountTuple) tuple);
2168 position.next();
2169 break;
2170 }
2171 case DELETE_ARRAY_ELEMENT: {
2172
2173
2174
2175 VariableTuple variableTuple = (VariableTuple) tuple;
2176 long offset = variableTuple.getVariableOffset();
2177 boolean isGlobal = variableTuple.isGlobal();
2178 Map<Object, Object> aa = getMapVariable(offset, isGlobal);
2179 Object key = pop();
2180 checkScalar(key);
2181 if (aa != null) {
2182 aa.remove(key);
2183 }
2184 position.next();
2185 break;
2186 }
2187 case DELETE_MAP_ELEMENT: {
2188
2189
2190 Object key = pop();
2191 checkScalar(key);
2192 Map<Object, Object> aa = toMap(pop());
2193 aa.remove(key);
2194 position.next();
2195 break;
2196 }
2197 case DELETE_ARRAY: {
2198
2199
2200
2201 VariableTuple variableTuple = (VariableTuple) tuple;
2202 long offset = variableTuple.getVariableOffset();
2203 boolean isGlobal = variableTuple.isGlobal();
2204 Map<Object, Object> array = getMapVariable(offset, isGlobal);
2205 if (array != null) {
2206 array.clear();
2207 }
2208 position.next();
2209 break;
2210 }
2211 case SET_EXIT_ADDRESS: {
2212
2213 exitAddress = tuple.getAddress();
2214 position.next();
2215 break;
2216 }
2217 case SET_WITHIN_END_BLOCKS: {
2218
2219 BooleanTuple endBlocksTuple = (BooleanTuple) tuple;
2220 withinEndBlocks = endBlocksTuple.getValue();
2221 position.next();
2222 break;
2223 }
2224 case EXIT_WITHOUT_CODE:
2225 case EXIT_WITH_CODE: {
2226 if (opcode == Opcode.EXIT_WITH_CODE) {
2227
2228 exitCode = (int) JRT.toDouble(pop());
2229 }
2230 throwExitException = true;
2231
2232
2233 if (!withinEndBlocks && exitAddress != null) {
2234
2235 runtimeStack.popAllFrames();
2236
2237 operandStack.clear();
2238 position.jump(exitAddress);
2239 } else {
2240
2241
2242 operandStack.clear();
2243 throw new ExitException(exitCode, "The AWK script requested an exit");
2244
2245 }
2246 break;
2247 }
2248 case REGEXP: {
2249
2250 RegexTuple regexTuple = (RegexTuple) tuple;
2251 Pattern pattern = regexTuple.getPattern();
2252 push(pattern);
2253 position.next();
2254 break;
2255 }
2256 case CONDITION_PAIR: {
2257
2258
2259 if (conditionPairs == null) {
2260 conditionPairs = new HashMap<Integer, ConditionPair>();
2261 }
2262 int currentIndex = position.currentIndex();
2263 ConditionPair cp = conditionPairs.get(currentIndex);
2264 if (cp == null) {
2265 cp = new ConditionPair();
2266 conditionPairs.put(currentIndex, cp);
2267 }
2268 boolean end = jrt.toBoolean(pop());
2269 boolean start = jrt.toBoolean(pop());
2270 push(cp.update(start, end) ? ONE : ZERO);
2271 position.next();
2272 break;
2273 }
2274 case IS_IN: {
2275
2276 Object arr = pop();
2277 Object arg = pop();
2278 checkScalar(arg);
2279 if (arr == null || arr instanceof UninitializedObject) {
2280 push(ZERO);
2281 position.next();
2282 break;
2283 }
2284 if (!(arr instanceof Map)) {
2285 throw new AwkRuntimeException("Attempting to test membership on a non-associative-array.");
2286 }
2287 @SuppressWarnings("unchecked")
2288 Map<Object, Object> aa = (Map<Object, Object>) arr;
2289 boolean result = JRT.containsAwkKey(aa, arg);
2290 push(result ? ONE : ZERO);
2291 position.next();
2292 break;
2293 }
2294 case THIS: {
2295
2296
2297
2298
2299 position.next();
2300 break;
2301 }
2302 case EXTENSION: {
2303
2304
2305
2306
2307
2308
2309
2310 ExtensionTuple extensionTuple = (ExtensionTuple) tuple;
2311 ExtensionFunction function = extensionTuple.getFunction();
2312 long numArgs = extensionTuple.getArgCount();
2313 boolean isInitial = extensionTuple.isInitial();
2314
2315 Object[] args = new Object[(int) numArgs];
2316 for (int i = (int) numArgs - 1; i >= 0; i--) {
2317 args[i] = pop();
2318 }
2319
2320 String extensionClassName = function.getExtensionClassName();
2321 JawkExtension extension = extensionInstances.get(extensionClassName);
2322 if (extension == null) {
2323 throw new AwkRuntimeException(
2324 position.lineNumber(),
2325 "Extension instance for class '" + extensionClassName
2326 + "' is not registered");
2327 }
2328 if (!(extension instanceof AbstractExtension)) {
2329 throw new AwkRuntimeException(
2330 position.lineNumber(),
2331 "Extension instance for class '" + extensionClassName
2332 + "' does not extend "
2333 + AbstractExtension.class.getName());
2334 }
2335
2336 Object retval = function.invoke((AbstractExtension) extension, args);
2337
2338
2339
2340
2341 if (isInitial && retval != null && retval instanceof BlockObject) {
2342 retval = new BlockManager().block((BlockObject) retval);
2343 }
2344
2345
2346 if (retval == null) {
2347 retval = "";
2348 } else
2349 if (!(retval instanceof Integer
2350 ||
2351 retval instanceof Long
2352 ||
2353 retval instanceof Double
2354 ||
2355 retval instanceof String
2356 ||
2357 retval instanceof Map
2358 ||
2359 retval instanceof BlockObject)) {
2360
2361
2362 retval = retval.toString();
2363 }
2364 push(retval);
2365
2366 position.next();
2367 break;
2368 }
2369 case ASSIGN_NF: {
2370 Object v = pop();
2371 jrt.setNF(v);
2372 push(v);
2373 position.next();
2374 break;
2375 }
2376 case PUSH_NF: {
2377 push(jrt.getNF());
2378 position.next();
2379 break;
2380 }
2381 case ASSIGN_NR: {
2382 Object v = pop();
2383 jrt.setNR(v);
2384 push(v);
2385 position.next();
2386 break;
2387 }
2388 case PUSH_NR: {
2389 push(jrt.getNR());
2390 position.next();
2391 break;
2392 }
2393 case ASSIGN_FNR: {
2394 Object v = pop();
2395 jrt.setFNR(v);
2396 push(v);
2397 position.next();
2398 break;
2399 }
2400 case PUSH_FNR: {
2401 push(jrt.getFNR());
2402 position.next();
2403 break;
2404 }
2405 case ASSIGN_FS: {
2406 Object v = pop();
2407 jrt.setFS(v);
2408 push(v);
2409 position.next();
2410 break;
2411 }
2412 case PUSH_FS: {
2413 push(jrt.getFSVar());
2414 position.next();
2415 break;
2416 }
2417 case ASSIGN_RS: {
2418 Object v = pop();
2419 jrt.setRS(v);
2420 push(v);
2421 position.next();
2422 break;
2423 }
2424 case PUSH_RS: {
2425 push(jrt.getRSVar());
2426 position.next();
2427 break;
2428 }
2429 case ASSIGN_OFS: {
2430 Object v = pop();
2431 jrt.setOFS(v);
2432 push(v);
2433 position.next();
2434 break;
2435 }
2436 case PUSH_OFS: {
2437 push(jrt.getOFSVar());
2438 position.next();
2439 break;
2440 }
2441 case ASSIGN_ORS: {
2442 Object v = pop();
2443 jrt.setORS(v);
2444 push(v);
2445 position.next();
2446 break;
2447 }
2448 case PUSH_ORS: {
2449 push(jrt.getORSVar());
2450 position.next();
2451 break;
2452 }
2453 case ASSIGN_RSTART: {
2454 Object v = pop();
2455 jrt.setRSTART(v);
2456 push(v);
2457 position.next();
2458 break;
2459 }
2460 case PUSH_RSTART: {
2461 push(jrt.getRSTART());
2462 position.next();
2463 break;
2464 }
2465 case ASSIGN_RLENGTH: {
2466 Object v = pop();
2467 jrt.setRLENGTH(v);
2468 push(v);
2469 position.next();
2470 break;
2471 }
2472 case PUSH_RLENGTH: {
2473 push(jrt.getRLENGTH());
2474 position.next();
2475 break;
2476 }
2477 case ASSIGN_FILENAME: {
2478 Object v = pop();
2479 jrt.setFILENAMEViaJrt(v);
2480 push(v == null ? "" : v);
2481 position.next();
2482 break;
2483 }
2484 case PUSH_FILENAME: {
2485 push(jrt.getFILENAME());
2486 position.next();
2487 break;
2488 }
2489 case ASSIGN_SUBSEP: {
2490 Object v = pop();
2491 jrt.setSUBSEP(v);
2492 push(v);
2493 position.next();
2494 break;
2495 }
2496 case PUSH_SUBSEP: {
2497 push(jrt.getSUBSEPVar());
2498 position.next();
2499 break;
2500 }
2501 case ASSIGN_CONVFMT: {
2502 Object v = pop();
2503 jrt.setCONVFMT(v);
2504 push(v);
2505 position.next();
2506 break;
2507 }
2508 case PUSH_CONVFMT: {
2509 push(jrt.getCONVFMTVar());
2510 position.next();
2511 break;
2512 }
2513 case ASSIGN_OFMT: {
2514 Object v = pop();
2515 jrt.setOFMT(v);
2516 push(v);
2517 position.next();
2518 break;
2519 }
2520 case PUSH_OFMT: {
2521 push(getOFMT());
2522 position.next();
2523 break;
2524 }
2525 case ASSIGN_ARGC: {
2526 Object v = pop();
2527 if (argcOffset == NULL_OFFSET) {
2528 throw new AwkRuntimeException("ARGC is read-only (not materialized).");
2529 }
2530 runtimeStack.setVariable(argcOffset, v, true);
2531 push(v);
2532 position.next();
2533 break;
2534 }
2535 case PUSH_ARGC: {
2536 if (argcOffset == NULL_OFFSET) {
2537 push(getARGC());
2538 } else {
2539 push(runtimeStack.getVariable(argcOffset, true));
2540 }
2541 position.next();
2542 break;
2543 }
2544 default:
2545 throw new Error("invalid opcode: " + position.opcode());
2546 }
2547 if (profiling) {
2548 afterProfiledTuple(opcode, tupleStartNanos);
2549 }
2550 }
2551
2552 } catch (ExitException ee) {
2553 if (profiling && (opcode == Opcode.EXIT_WITH_CODE || opcode == Opcode.EXIT_WITHOUT_CODE)) {
2554 afterProfiledTuple(opcode, tupleStartNanos);
2555 }
2556 throw ee;
2557 } catch (IOException ioe) {
2558
2559 runtimeStack.popAllFrames();
2560
2561 operandStack.clear();
2562 throw ioe;
2563 } catch (RuntimeException re) {
2564
2565 runtimeStack.popAllFrames();
2566
2567 operandStack.clear();
2568 if (re instanceof AwkSandboxException) {
2569 throw re;
2570 }
2571 throw new AwkRuntimeException(position.lineNumber(), re.getMessage(), re);
2572 } catch (AssertionError ae) {
2573
2574 runtimeStack.popAllFrames();
2575
2576 operandStack.clear();
2577 throw ae;
2578 }
2579
2580
2581 if (throwExitException) {
2582 throw new ExitException(exitCode, "The AWK script requested an exit");
2583 }
2584 }
2585
2586
2587
2588
2589 public void resetProfiling() {
2590 if (!profiling) {
2591 return;
2592 }
2593 tupleProfilingStats.clear();
2594 functionProfilingStats.clear();
2595 activeProfilingFunctions.clear();
2596 }
2597
2598
2599
2600
2601
2602
2603 public ProfilingReport getProfilingReport() {
2604 if (!profiling) {
2605 return ProfilingReport.empty();
2606 }
2607 return new ProfilingReport(tupleProfilingStats, functionProfilingStats);
2608 }
2609
2610 private void execPrint(CountTuple tuple) throws IOException {
2611 long numArgs = tuple.getCount();
2612 jrt.printDefault(numArgs == 0 ? new Object[] { jrt.jrtGetInputField(0) } : popArguments(numArgs));
2613 }
2614
2615 private void execPrintToFile(CountAndAppendTuple tuple) throws IOException {
2616 String key = jrt.toAwkString(pop());
2617 long numArgs = tuple.getCount();
2618 jrt
2619 .printToFile(
2620 key,
2621 tuple.isAppend(),
2622 numArgs == 0 ? new Object[]
2623 { jrt.jrtGetInputField(0) } : popArguments(numArgs));
2624 }
2625
2626 private void execPrintToPipe(CountTuple tuple) throws IOException {
2627 String cmd = jrt.toAwkString(pop());
2628 long numArgs = tuple.getCount();
2629 jrt.printToProcess(cmd, numArgs == 0 ? new Object[] { jrt.jrtGetInputField(0) } : popArguments(numArgs));
2630 }
2631
2632 private void execPrintf(CountTuple tuple) throws IOException {
2633 long numArgs = tuple.getCount();
2634 Object[] values = popArguments(numArgs - 1);
2635 String format = jrt.toAwkString(pop());
2636 jrt.printfDefault(format, values);
2637 }
2638
2639 private void execPrintfToFile(CountAndAppendTuple tuple) throws IOException {
2640 String key = jrt.toAwkString(pop());
2641 long numArgs = tuple.getCount();
2642 Object[] values = popArguments(numArgs - 1);
2643 String format = jrt.toAwkString(pop());
2644 jrt.printfToFile(key, tuple.isAppend(), format, values);
2645 }
2646
2647 private void execPrintfToPipe(CountTuple tuple) throws IOException {
2648 String cmd = jrt.toAwkString(pop());
2649 long numArgs = tuple.getCount();
2650 Object[] values = popArguments(numArgs - 1);
2651 String format = jrt.toAwkString(pop());
2652 jrt.printfToProcess(cmd, format, values);
2653 }
2654
2655 private void execLength(CountTuple tuple) {
2656 long num = tuple.getCount();
2657 if (num == 0) {
2658 push(jrt.jrtGetInputField(0).toString().length());
2659 return;
2660 }
2661 Object value = pop();
2662 if (value instanceof Map) {
2663 push((long) ((Map<?, ?>) value).size());
2664 } else {
2665 push(jrt.toAwkString(value).length());
2666 }
2667 }
2668
2669 private void execMatch() {
2670 String ere = jrt.toAwkString(pop());
2671 String s = jrt.toAwkString(pop());
2672 int flags = 0;
2673 if (globalVariableOffsets.containsKey("IGNORECASE")) {
2674 Integer offsetObj = globalVariableOffsets.get("IGNORECASE");
2675 Object ignorecase = runtimeStack.getVariable(offsetObj, true);
2676 if (JRT.toDouble(ignorecase) != 0) {
2677 flags |= Pattern.CASE_INSENSITIVE;
2678 }
2679 }
2680 Pattern pattern = Pattern.compile(ere, flags);
2681 Matcher matcher = pattern.matcher(s);
2682 if (matcher.find()) {
2683 int start = matcher.start() + 1;
2684 int len = matcher.end() - matcher.start();
2685 jrt.setRSTART(start);
2686 jrt.setRLENGTH(len);
2687 push(start);
2688 } else {
2689 jrt.setRSTART(0);
2690 jrt.setRLENGTH(-1);
2691 push(0);
2692 }
2693 }
2694
2695 private void execSubForDollar0(BooleanTuple tuple) {
2696 boolean isGsub = tuple.getValue();
2697 String repl = jrt.toAwkString(pop());
2698 String ere = jrt.toAwkString(pop());
2699 String orig = jrt.toAwkString(jrt.jrtGetInputField(0));
2700 String newstring = isGsub ? replaceAll(orig, ere, repl) : replaceFirst(orig, ere, repl);
2701 jrt.setInputLine(newstring);
2702 jrt.jrtParseFields();
2703 }
2704
2705 private void execSubForDollarReference(BooleanTuple tuple) {
2706 boolean isGsub = tuple.getValue();
2707 long fieldNum = JRT.parseFieldNumber(pop());
2708 String orig = jrt.toAwkString(pop());
2709 String repl = jrt.toAwkString(pop());
2710 String ere = jrt.toAwkString(pop());
2711 String newstring = isGsub ? replaceAll(orig, ere, repl) : replaceFirst(orig, ere, repl);
2712 if (fieldNum == 0) {
2713 jrt.setInputLine(newstring);
2714 jrt.jrtParseFields();
2715 } else {
2716 jrt.jrtSetInputField(newstring, fieldNum);
2717 }
2718 }
2719
2720 private void execSubForVariable(SubstitutionVariableTuple tuple, PositionTracker position) {
2721 String newString = execSubOrGSub(tuple.isGlobalSubstitution());
2722 assign(tuple.getVariableOffset(), newString, tuple.isGlobal(), position, false);
2723 }
2724
2725 private void execSubForArrayReference(SubstitutionVariableTuple tuple) {
2726 Object arrIdx = pop();
2727 String newString = execSubOrGSub(tuple.isGlobalSubstitution());
2728 assignArray(tuple.getVariableOffset(), arrIdx, newString, tuple.isGlobal());
2729 pop();
2730 }
2731
2732 private void execSubForMapReference(BooleanTuple tuple) {
2733 Object arrIdx = pop();
2734 Map<Object, Object> array = toMap(pop());
2735 String newString = execSubOrGSub(tuple.getValue());
2736 assignMapElement(array, arrIdx, newString);
2737 pop();
2738 }
2739
2740 private void execSplit(CountTuple tuple, PositionTracker position) {
2741 long numArgs = tuple.getCount();
2742 String fsString;
2743 if (numArgs == 2) {
2744 fsString = jrt.toAwkString(jrt.getFSVar());
2745 } else if (numArgs == 3) {
2746 fsString = jrt.toAwkString(pop());
2747 } else {
2748 throw new Error("Invalid # of args. split() requires 2 or 3. Got: " + numArgs);
2749 }
2750 Object o = pop();
2751 if (!(o instanceof Map)) {
2752 throw new AwkRuntimeException(position.lineNumber(), o + " is not an array.");
2753 }
2754 String s = jrt.toAwkString(pop());
2755 Enumeration<Object> tokenizer;
2756 if (fsString.equals(" ")) {
2757 tokenizer = new StringTokenizer(s);
2758 } else if (fsString.length() == 1) {
2759 tokenizer = new SingleCharacterTokenizer(s, fsString.charAt(0));
2760 } else if (fsString.isEmpty()) {
2761 tokenizer = new CharacterTokenizer(s);
2762 } else {
2763 tokenizer = new RegexTokenizer(s, fsString);
2764 }
2765
2766 @SuppressWarnings("unchecked")
2767 Map<Object, Object> assocArray = (Map<Object, Object>) o;
2768 assocArray.clear();
2769 long cnt = 0;
2770 while (tokenizer.hasMoreElements()) {
2771 Object value = tokenizer.nextElement();
2772 assocArray.put(++cnt, jrt.toInputScalar(value));
2773 }
2774 push(cnt);
2775 }
2776
2777 private void execSubstr(CountTuple tuple) {
2778 long numArgs = tuple.getCount();
2779 int startPos, length;
2780 String s;
2781 if (numArgs == 3) {
2782 length = (int) JRT.toLong(pop());
2783 startPos = (int) JRT.toDouble(pop());
2784 s = jrt.toAwkString(pop());
2785 } else if (numArgs == 2) {
2786 startPos = (int) JRT.toDouble(pop());
2787 s = jrt.toAwkString(pop());
2788 length = s.length() - startPos + 1;
2789 } else {
2790 throw new Error("numArgs for SUBSTR must be 2 or 3. It is " + numArgs);
2791 }
2792 if (startPos <= 0) {
2793 startPos = 1;
2794 }
2795 if (length <= 0 || startPos > s.length()) {
2796 push(BLANK);
2797 } else if (startPos + length > s.length()) {
2798 push(s.substring(startPos - 1));
2799 } else {
2800 push(s.substring(startPos - 1, startPos + length - 1));
2801 }
2802 }
2803
2804 private void execSetNumGlobals(CountTuple tuple) {
2805 long numGlobals = tuple.getCount();
2806 Object[] globals = runtimeStack.getNumGlobals();
2807 if (mergedGlobalLayoutActive) {
2808 if (!hasCompatiblePersistentGlobalLayout(numGlobals)) {
2809 throw new IllegalStateException(
2810 "AVM globals are already initialized for an incompatible persistent layout.");
2811 }
2812 applyExecutionInitialVariablesToGlobalSlots(true);
2813 } else if (globals == null) {
2814 runtimeStack.setNumGlobals(numGlobals, globalVariableOffsets);
2815 initializedEvalGlobalVariableOffsets = globalVariableOffsets;
2816 initializedEvalGlobalVariableArrays = globalVariableArrays;
2817 applyExecutionInitialVariablesToGlobalSlots(false);
2818 } else if (!hasCompatibleEvalGlobalLayout(numGlobals)) {
2819 throw new IllegalStateException(
2820 "AVM globals are already initialized for a different eval layout. Call prepareForEval(...) first.");
2821 }
2822 }
2823
2824 private void execApplySubsep(CountTuple tuple) {
2825 long count = tuple.getCount();
2826 if (count == 1) {
2827 Object value = pop();
2828 checkScalar(value);
2829 push(jrt.toAwkString(value));
2830 return;
2831 }
2832 StringBuilder sb = new StringBuilder();
2833 Object value = pop();
2834 checkScalar(value);
2835 sb.append(jrt.toAwkString(value));
2836 String subsep = jrt.toAwkString(jrt.getSUBSEPVar());
2837 for (int i = 1; i < count; i++) {
2838 sb.insert(0, subsep);
2839 value = pop();
2840 checkScalar(value);
2841 sb.insert(0, jrt.toAwkString(value));
2842 }
2843 push(sb.toString());
2844 }
2845
2846 private long beforeProfiledTuple(Tuple tuple, Opcode opcode) {
2847 long now = System.nanoTime();
2848 if (opcode == Opcode.CALL_FUNCTION) {
2849 CallFunctionTuple callTuple = (CallFunctionTuple) tuple;
2850 activeProfilingFunctions.push(new ActiveFunction(callTuple.getFunctionName(), now));
2851 } else if (opcode == Opcode.EXTENSION) {
2852 ExtensionTuple extensionTuple = (ExtensionTuple) tuple;
2853 ExtensionFunction function = extensionTuple.getFunction();
2854 activeProfilingFunctions.push(new ActiveFunction(function.getKeyword(), now));
2855 }
2856 return now;
2857 }
2858
2859 private void afterProfiledTuple(Opcode opcode, long tupleStartNanos) {
2860 long now = System.nanoTime();
2861 statisticsFor(tupleProfilingStats, opcode).add(now - tupleStartNanos);
2862 if (opcode == Opcode.EXIT_WITH_CODE || opcode == Opcode.EXIT_WITHOUT_CODE) {
2863 recordAllFunctionExits(now);
2864 } else if (opcode == Opcode.EXTENSION || opcode == Opcode.RETURN_FROM_FUNCTION) {
2865 recordFunctionExit(now);
2866 }
2867 }
2868
2869 private static <K> ProfilingReport.Accumulator statisticsFor(
2870 Map<K, ProfilingReport.Accumulator> stats,
2871 K key) {
2872 ProfilingReport.Accumulator accumulator = stats.get(key);
2873 if (accumulator == null) {
2874 accumulator = new ProfilingReport.Accumulator();
2875 stats.put(key, accumulator);
2876 }
2877 return accumulator;
2878 }
2879
2880 private void recordFunctionExit(long now) {
2881 if (activeProfilingFunctions.isEmpty()) {
2882 return;
2883 }
2884 ActiveFunction function = activeProfilingFunctions.pop();
2885 statisticsFor(functionProfilingStats, function.name).add(now - function.startNanos);
2886 }
2887
2888 private void recordAllFunctionExits(long now) {
2889 while (!activeProfilingFunctions.isEmpty()) {
2890 recordFunctionExit(now);
2891 }
2892 }
2893
2894 private static final class ActiveFunction {
2895 private final String name;
2896 private final long startNanos;
2897
2898 private ActiveFunction(String name, long startNanos) {
2899 this.name = name;
2900 this.startNanos = startNanos;
2901 }
2902 }
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915 @Override
2916 public void close() throws IOException {
2917 jrt.jrtCloseAll();
2918 closeResolvedInputSource();
2919 resolvedInputSource = null;
2920 inputSourceFilelistAssignmentsApplied = false;
2921 }
2922
2923
2924
2925
2926
2927
2928
2929 private void closeResolvedInputSource() {
2930 closeInputSource(resolvedInputSource);
2931 }
2932
2933 private void closeInputSource(InputSource inputSource) {
2934 if (!(inputSource instanceof Closeable)) {
2935 return;
2936 }
2937 try {
2938 ((Closeable) inputSource).close();
2939 } catch (IOException ignored) {
2940
2941 }
2942 }
2943
2944 private Object[] popArguments(long numArgs) {
2945 Object[] args = new Object[(int) numArgs];
2946 for (int i = (int) numArgs - 1; i >= 0; i--) {
2947 args[i] = pop();
2948 }
2949 return args;
2950 }
2951
2952
2953
2954
2955 private String sprintfFunction(long numArgs) {
2956 Object[] argArray = popArguments(numArgs - 1);
2957 String fmt = jrt.toAwkString(pop());
2958 return jrt.getAwkSink().sprintf(fmt, argArray);
2959 }
2960
2961 private void setNumOnJRT(long fieldNum, double num) {
2962 String numString = jrt.toAwkString(Double.valueOf(num));
2963
2964
2965 if (fieldNum == 0) {
2966 jrt.setInputLine(numString);
2967 jrt.jrtParseFields();
2968 } else {
2969 jrt.jrtSetInputField(numString, fieldNum);
2970 }
2971 }
2972
2973 private String execSubOrGSub(boolean isGsub) {
2974 String newString;
2975
2976
2977
2978
2979 String orig = jrt.toAwkString(pop());
2980 String repl = jrt.toAwkString(pop());
2981 String ere = jrt.toAwkString(pop());
2982 if (isGsub) {
2983 newString = replaceAll(orig, ere, repl);
2984 } else {
2985 newString = replaceFirst(orig, ere, repl);
2986 }
2987
2988 return newString;
2989 }
2990
2991 private StringBuffer replaceFirstSb = new StringBuffer();
2992
2993
2994
2995
2996 private String replaceFirst(String orig, String ere, String repl) {
2997 push(RegexRuntimeSupport.replaceFirst(orig, repl, ere, replaceFirstSb));
2998 return replaceFirstSb.toString();
2999 }
3000
3001 private StringBuffer replaceAllSb = new StringBuffer();
3002
3003
3004
3005
3006 private String replaceAll(String orig, String ere, String repl) {
3007 push(RegexRuntimeSupport.replaceAll(orig, repl, ere, replaceAllSb));
3008 return replaceAllSb.toString();
3009 }
3010
3011
3012
3013
3014 private void assign(long l, Object value, boolean isGlobal, PositionTracker position, boolean push) {
3015
3016 if (runtimeStack.getVariable(l, isGlobal) instanceof Map) {
3017 throw new AwkRuntimeException(position.lineNumber(), "cannot assign anything to an unindexed associative array");
3018 }
3019 if (push) {
3020 push(value);
3021 }
3022 runtimeStack.setVariable(l, value, isGlobal);
3023
3024 }
3025
3026
3027
3028
3029 private void assignArray(long offset, Object arrIdx, Object rhs, boolean isGlobal) {
3030 assignMapElement(ensureMapVariable(offset, isGlobal), arrIdx, rhs);
3031 }
3032
3033 private void assignMapElement(Map<Object, Object> array, Object arrIdx, Object rhs) {
3034 checkScalar(arrIdx);
3035 array.put(arrIdx, rhs);
3036 push(rhs);
3037 }
3038
3039
3040
3041
3042
3043 private Object inc(long l, boolean isGlobal) {
3044 Object o = runtimeStack.getVariable(l, isGlobal);
3045 if (o == null || o instanceof UninitializedObject) {
3046 o = ZERO;
3047 runtimeStack.setVariable(l, o, isGlobal);
3048 }
3049 Object updated = JRT.inc(o);
3050 runtimeStack.setVariable(l, updated, isGlobal);
3051 return o;
3052 }
3053
3054
3055
3056
3057
3058 private Object dec(long l, boolean isGlobal) {
3059 Object o = runtimeStack.getVariable(l, isGlobal);
3060 if (o == null || o instanceof UninitializedObject) {
3061 o = ZERO;
3062 runtimeStack.setVariable(l, o, isGlobal);
3063 }
3064 Object updated = JRT.dec(o);
3065 runtimeStack.setVariable(l, updated, isGlobal);
3066 return o;
3067 }
3068
3069
3070 @Override
3071 public final Object getRS() {
3072 return jrt.getRSVar();
3073 }
3074
3075
3076 @Override
3077 public final Object getOFS() {
3078 return jrt.getOFSVar();
3079 }
3080
3081 public final Object getORS() {
3082 return jrt.getORSVar();
3083 }
3084
3085
3086 @Override
3087 public final Object getSUBSEP() {
3088 return jrt.getSUBSEPVar();
3089 }
3090
3091
3092
3093
3094
3095
3096
3097
3098 @SuppressWarnings("unused")
3099 private void setFilelistVariable(String nameValue) {
3100 NameValueAssignment assignment = parseNameValueAssignment(nameValue);
3101 String name = assignment.name;
3102 Object obj = assignment.value;
3103
3104
3105 if (functionNames.contains(name)) {
3106 throw new IllegalArgumentException("Cannot assign a scalar to a function name (" + name + ").");
3107 }
3108
3109 Integer offsetObj = globalVariableOffsets.get(name);
3110 Boolean arrayObj = globalVariableArrays.get(name);
3111
3112 if (offsetObj != null) {
3113 if (arrayObj.booleanValue()) {
3114 throw new IllegalArgumentException("Cannot assign a scalar to a non-scalar variable (" + name + ").");
3115 } else {
3116 runtimeStack.setFilelistVariable(offsetObj.intValue(), obj);
3117 }
3118 } else if (runtimeStack.hasGlobalVariable(name)) {
3119 runtimeStack.setGlobalVariable(name, obj);
3120 }
3121 }
3122
3123
3124 @Override
3125 public final void assignVariable(String name, Object obj) {
3126
3127
3128 if (globalVariableOffsets == null || globalVariableArrays == null) {
3129 Object normalized = normalizeExternalVariableValue(obj);
3130 baseInitialVariables.put(name, normalized);
3131 if (JRT.isJrtManagedSpecialVariable(name)) {
3132 baseSpecialVariables.put(name, normalized);
3133 }
3134 return;
3135 }
3136
3137
3138 if (functionNames.contains(name)) {
3139 throw new IllegalArgumentException("Cannot assign a scalar to a function name (" + name + ").");
3140 }
3141
3142 Integer offsetObj = globalVariableOffsets.get(name);
3143 Boolean arrayObj = globalVariableArrays.get(name);
3144
3145 if (offsetObj != null) {
3146 Object normalized = normalizeExternalVariableValue(obj);
3147 if (arrayObj.booleanValue()) {
3148 if (normalized instanceof Map) {
3149 runtimeStack.setFilelistVariable(offsetObj.intValue(), normalized);
3150 } else {
3151 throw new IllegalArgumentException(
3152 "Cannot assign a scalar to a non-scalar variable (" + name + ").");
3153 }
3154 } else {
3155 runtimeStack.setFilelistVariable(offsetObj.intValue(), normalized);
3156 }
3157 } else if (runtimeStack.hasGlobalVariable(name)) {
3158 Object normalized = normalizeExternalVariableValue(obj);
3159 runtimeStack.setGlobalVariable(name, normalized);
3160 }
3161 }
3162
3163 private void applyInputSourceFilelistAssignmentsIfNeeded() {
3164 if (inputSourceFilelistAssignmentsApplied || resolvedInputSource instanceof StreamInputSource) {
3165 return;
3166 }
3167 for (String argument : arguments) {
3168 if (argument.indexOf('=') > 0) {
3169 setFilelistVariable(argument);
3170 }
3171 }
3172 inputSourceFilelistAssignmentsApplied = true;
3173 }
3174
3175
3176 @Override
3177 public Object getFS() {
3178 return jrt.getFSVar();
3179 }
3180
3181
3182 @Override
3183 public Object getCONVFMT() {
3184 return jrt.getCONVFMTString();
3185 }
3186
3187
3188 @Override
3189 public void resetFNR() {
3190 jrt.setFNR(0);
3191 }
3192
3193
3194 @Override
3195 public void incFNR() {
3196 long v = jrt.getFNR();
3197 jrt.setFNR(v + 1);
3198 }
3199
3200
3201 @Override
3202 public void incNR() {
3203 long v = jrt.getNR();
3204 jrt.setNR(v + 1);
3205 }
3206
3207
3208 @Override
3209 public void setNF(Integer newNf) {
3210 jrt.setNF(newNf);
3211 }
3212
3213
3214 @Override
3215 public void setFILENAME(String filename) {
3216 jrt.setFILENAMEViaJrt(jrt.toInputScalar(filename));
3217 }
3218
3219
3220 @Override
3221 public Object getARGV() {
3222 if (argvOffset == NULL_OFFSET) {
3223 Map<Object, Object> argv = newAwkArray();
3224 argv.put(0L, "jawk");
3225 for (int i = 0; i < arguments.size(); i++) {
3226 argv.put(Long.valueOf(i + 1L), jrt.toInputScalar(arguments.get(i)));
3227 }
3228 return argv;
3229 }
3230 return runtimeStack.getVariable(argvOffset, true);
3231 }
3232
3233
3234 @Override
3235 public Object getARGC() {
3236 if (argcOffset == NULL_OFFSET) {
3237 return Long.valueOf(arguments.size() + 1);
3238 }
3239 return runtimeStack.getVariable(argcOffset, true);
3240 }
3241
3242 private String getOFMT() {
3243 return jrt.getOFMTString();
3244 }
3245
3246 private Map<Object, Object> newAwkArray() {
3247 return JRT.createAwkMap(sortedArrayKeys);
3248 }
3249
3250 private Map<Object, Object> ensureMapVariable(long offset, boolean isGlobal) {
3251 Object value = runtimeStack.getVariable(offset, isGlobal);
3252 if (value == null || value.equals(BLANK) || value instanceof UninitializedObject) {
3253 Map<Object, Object> map = newAwkArray();
3254 runtimeStack.setVariable(offset, map, isGlobal);
3255 return map;
3256 }
3257 return toMap(value);
3258 }
3259
3260 private Map<Object, Object> getMapVariable(long offset, boolean isGlobal) {
3261 Object value = runtimeStack.getVariable(offset, isGlobal);
3262 if (value == null || value.equals(BLANK) || value instanceof UninitializedObject) {
3263 return null;
3264 }
3265 return toMap(value);
3266 }
3267
3268
3269
3270
3271
3272
3273
3274
3275 private Map<Object, Object> toMap(Object value) {
3276 if (!(value instanceof Map)) {
3277 throw new AwkRuntimeException("Attempting to treat a scalar as an array.");
3278 }
3279 @SuppressWarnings("unchecked")
3280 Map<Object, Object> map = (Map<Object, Object>) value;
3281 return map;
3282 }
3283
3284
3285
3286
3287
3288
3289
3290
3291 private void checkScalar(Object value) {
3292 if (value instanceof Map) {
3293 throw new AwkRuntimeException("Attempting to use an array in a scalar context.");
3294 }
3295 }
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307 private Map<Object, Object> ensureArrayInArray(Map<Object, Object> map, Object key) {
3308 checkScalar(key);
3309 Object value = JRT.getAwkValue(map, key);
3310 if (value == null || value.equals(BLANK) || value instanceof UninitializedObject) {
3311 Map<Object, Object> nested = newAwkArray();
3312 map.put(key, nested);
3313 return nested;
3314 }
3315 if (!(value instanceof Map)) {
3316 throw new AwkRuntimeException("Attempting to use a scalar as an array.");
3317 }
3318 @SuppressWarnings("unchecked")
3319 Map<Object, Object> nested = (Map<Object, Object>) value;
3320 return nested;
3321 }
3322
3323 private Object normalizeExternalVariableValue(Object value) {
3324 if (value instanceof String) {
3325 return jrt.toInputScalar(value);
3326 }
3327 if (!(value instanceof Map) && !(value instanceof List)) {
3328 return value;
3329 }
3330 return AssocArray.normalizeValue(value, sortedArrayKeys);
3331 }
3332
3333 private static final UninitializedObject BLANK = new UninitializedObject();
3334
3335
3336
3337
3338
3339 private static final Set<String> NON_PERSISTENT_GLOBALS = new HashSet<>(
3340 Arrays.asList("ARGV", "ARGC", "ENVIRON", "RSTART", "RLENGTH", "IGNORECASE"));
3341
3342 private static final class NameValueAssignment {
3343 private final String name;
3344 private final Object value;
3345
3346 private NameValueAssignment(String name, Object value) {
3347 this.name = name;
3348 this.value = value;
3349 }
3350 }
3351
3352 private static final class SingleRecordInputSource implements InputSource {
3353
3354 private final String record;
3355 private boolean consumed;
3356
3357 private SingleRecordInputSource(String record) {
3358 this.record = record;
3359 }
3360
3361 @Override
3362 public boolean nextRecord() {
3363 if (consumed || record == null) {
3364 return false;
3365 }
3366 consumed = true;
3367 return true;
3368 }
3369
3370 @Override
3371 public String getRecordText() {
3372 return consumed ? record : null;
3373 }
3374
3375 @Override
3376 public List<String> getFields() {
3377 return null;
3378 }
3379
3380 @Override
3381 public boolean isFromFilenameList() {
3382 return false;
3383 }
3384 }
3385
3386
3387
3388
3389 public static final int NULL_OFFSET = -1;
3390
3391 }