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.AbstractSet;
29 import java.util.Iterator;
30 import java.util.Arrays;
31 import java.util.HashSet;
32 import java.util.LinkedHashMap;
33 import java.util.LinkedHashSet;
34 import java.util.Objects;
35 import java.util.ArrayDeque;
36 import java.util.ArrayList;
37 import java.util.Collections;
38 import java.util.Deque;
39 import java.util.Enumeration;
40 import java.util.HashMap;
41 import java.util.IdentityHashMap;
42 import java.util.List;
43 import java.util.Locale;
44 import java.util.Map;
45 import java.util.Set;
46 import java.util.function.BiConsumer;
47 import java.util.regex.Pattern;
48 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
49 import io.jawk.AwkExpression;
50 import io.jawk.AwkProgram;
51 import io.jawk.AwkSandboxException;
52 import io.jawk.ExitException;
53 import io.jawk.ext.AbstractExtension;
54 import io.jawk.ext.ExtensionFunction;
55 import io.jawk.ext.ForInKeyOrder;
56 import io.jawk.ext.JawkExtension;
57 import io.jawk.intermediate.Address;
58 import io.jawk.intermediate.BuiltinFunction;
59 import io.jawk.intermediate.Opcode;
60 import io.jawk.intermediate.PositionTracker;
61 import io.jawk.intermediate.Tuple;
62 import io.jawk.intermediate.Tuple.BooleanTuple;
63 import io.jawk.intermediate.Tuple.CallFunctionTuple;
64 import io.jawk.intermediate.Tuple.ClassTuple;
65 import io.jawk.intermediate.Tuple.CountAndAppendTuple;
66 import io.jawk.intermediate.Tuple.CountTuple;
67 import io.jawk.intermediate.Tuple.DereferenceTuple;
68 import io.jawk.intermediate.Tuple.ExtensionTuple;
69 import io.jawk.intermediate.Tuple.IndirectCallTuple;
70 import io.jawk.intermediate.Tuple.IndirectFunctionTarget;
71 import io.jawk.intermediate.Tuple.InputFieldTuple;
72 import io.jawk.intermediate.Tuple.LongTuple;
73 import io.jawk.intermediate.Tuple.PushDoubleTuple;
74 import io.jawk.intermediate.Tuple.PushLongTuple;
75 import io.jawk.intermediate.Tuple.PushStringTuple;
76 import io.jawk.intermediate.Tuple.RegexTuple;
77 import io.jawk.intermediate.Tuple.ScalarPopTuple;
78 import io.jawk.intermediate.Tuple.SubstitutionVariableTuple;
79 import io.jawk.intermediate.Tuple.VariableTuple;
80 import io.jawk.intermediate.UninitializedObject;
81 import io.jawk.intermediate.UntypedObject;
82 import io.jawk.jrt.AssocArray;
83 import io.jawk.jrt.AwkRuntimeException;
84 import io.jawk.jrt.AwkSink;
85 import io.jawk.jrt.BlockManager;
86 import io.jawk.jrt.BlockObject;
87 import io.jawk.jrt.ConditionPair;
88 import io.jawk.jrt.InputSource;
89 import io.jawk.jrt.JRT;
90 import io.jawk.jrt.VariableManager;
91 import io.jawk.util.AwkSettings;
92 import io.jawk.jrt.BSDRandom;
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
123
124
125
126 public class AVM implements VariableManager, Closeable {
127
128 private RuntimeStack runtimeStack = new RuntimeStack();
129
130
131 private Deque<Object> operandStack = new ArrayDeque<Object>();
132
133
134
135
136 private final Deque<IndirectArrayArgumentReference> elementArgumentReferences = new ArrayDeque<IndirectArrayArgumentReference>();
137 private List<String> arguments;
138 private boolean sortedArrayKeys;
139 private final Map<String, Object> baseInitialVariables;
140 private final Map<String, Object> baseSpecialVariables;
141 private Map<String, Object> executionInitialVariables;
142 private Map<String, Object> executionSpecialVariables;
143 private JRT jrt;
144 private Map<String, JawkExtension> extensionInstances;
145
146 private static final Object NULL_OPERAND = new Object();
147
148
149
150
151 private Object pop() {
152 Object value = operandStack.pop();
153 return value == NULL_OPERAND ? null : value;
154 }
155
156 private void push(Object o) {
157 operandStack.push(o == null ? NULL_OPERAND : o);
158 }
159
160 private final AwkSettings settings;
161 private final boolean profiling;
162 private final Map<Opcode, ProfilingReport.Accumulator> tupleProfilingStats;
163 private final Map<String, ProfilingReport.Accumulator> functionProfilingStats;
164 private final Deque<ActiveFunction> activeProfilingFunctions;
165 private InputSource resolvedInputSource;
166 private AwkExpression installedEvalExpression;
167 private boolean mergedGlobalLayoutActive;
168
169
170 private ForInKeyOrder forInKeyOrder;
171
172
173 private String sourceDescription;
174
175
176 private int currentLineNumber;
177
178
179 private boolean beforeStartHooksExecuted;
180
181
182 private long symtabOffset = NULL_OFFSET;
183
184
185
186
187
188
189
190 public AVM() {
191 this(null, Collections.<String, JawkExtension>emptyMap());
192 }
193
194
195
196
197
198
199
200
201
202 public AVM(final AwkSettings parameters,
203 final Map<String, JawkExtension> extensionInstances) {
204 this(parameters, extensionInstances, false);
205 }
206
207
208
209
210
211
212
213
214
215 public AVM(
216 final AwkSettings parameters,
217 final Map<String, JawkExtension> extensionInstances,
218 final boolean profilingEnabled) {
219 this.settings = parameters != null ? parameters : AwkSettings.DEFAULT_SETTINGS;
220 this.extensionInstances = extensionInstances == null ?
221 Collections.<String, JawkExtension>emptyMap() : extensionInstances;
222 this.profiling = profilingEnabled;
223 if (profilingEnabled) {
224 this.tupleProfilingStats = new java.util.EnumMap<Opcode, ProfilingReport.Accumulator>(Opcode.class);
225 this.functionProfilingStats = new LinkedHashMap<String, ProfilingReport.Accumulator>();
226 this.activeProfilingFunctions = new ArrayDeque<ActiveFunction>();
227 } else {
228 this.tupleProfilingStats = null;
229 this.functionProfilingStats = null;
230 this.activeProfilingFunctions = null;
231 }
232
233 arguments = Collections.emptyList();
234 sortedArrayKeys = this.settings.isUseSortedArrayKeys();
235 baseInitialVariables = new HashMap<String, Object>(this.settings.getVariables());
236 baseSpecialVariables = JRT.copySpecialVariables(baseInitialVariables);
237 executionInitialVariables = baseInitialVariables;
238 executionSpecialVariables = baseSpecialVariables;
239
240 jrt = createJrt();
241 initExtensions();
242 }
243
244 protected JRT createJrt() {
245 return new JRT(this, this.settings.getLocale(), AwkSink.NOP_SINK, null);
246 }
247
248
249
250
251
252
253 protected AwkSettings getSettings() {
254 return settings;
255 }
256
257
258
259
260
261
262 @SuppressFBWarnings("EI_EXPOSE_REP")
263 public JRT getJrt() {
264 return jrt;
265 }
266
267
268
269
270
271
272
273 public void setAwkSink(AwkSink sink) {
274 jrt.setAwkSink(Objects.requireNonNull(sink, "sink"));
275 }
276
277
278
279
280
281
282
283 public void setErrorStream(PrintStream errorStream) {
284 jrt.setErrorStream(errorStream);
285 }
286
287
288
289
290
291
292
293 public void setWarningStream(PrintStream warningStream) {
294 jrt.setWarningStream(warningStream);
295 }
296
297
298
299
300
301
302
303
304
305
306
307
308
309 public void setForInKeyOrder(ForInKeyOrder keyOrder) {
310 forInKeyOrder = keyOrder;
311 }
312
313
314
315
316
317
318 public AwkSink getAwkSink() {
319 return jrt.getAwkSink();
320 }
321
322
323
324
325
326
327 protected Locale getLocale() {
328 return jrt.getLocale();
329 }
330
331
332
333
334
335
336
337
338
339 public Object eval(AwkExpression expression) throws IOException {
340 AwkExpression compiledExpression = Objects.requireNonNull(expression, "expression");
341 installExpressionMetadata(compiledExpression);
342
343 try {
344 executeTuples(compiledExpression.top());
345 } catch (ExitException e) {
346
347
348 throwExitException = false;
349 exitCode = 0;
350 throw new IllegalStateException("eval(AwkExpression) cannot execute EXIT opcodes.", e);
351 }
352 return operandStack.isEmpty() ? null : JRT.toJavaScalar(pop());
353 }
354
355
356
357
358
359
360
361
362
363 public Object eval(AwkExpression expression, InputSource inputSource) throws IOException {
364 return eval(expression, inputSource, null);
365 }
366
367
368
369
370
371
372
373
374
375
376
377
378 public Object eval(
379 AwkExpression expression,
380 InputSource inputSource,
381 Map<String, Object> variableOverrides)
382 throws IOException {
383 prepareForEval(inputSource, Collections.<String>emptyList(), variableOverrides);
384 return eval(expression);
385 }
386
387
388
389
390
391
392
393
394
395 public void execute(AwkProgram program, InputSource inputSource) throws ExitException, IOException {
396 execute(program, inputSource, Collections.<String>emptyList(), null);
397 }
398
399
400
401
402
403
404
405
406
407
408 public void execute(AwkProgram program, InputSource inputSource, List<String> runtimeArguments)
409 throws ExitException,
410 IOException {
411 execute(program, inputSource, runtimeArguments, null);
412 }
413
414
415
416
417
418
419
420
421
422
423
424
425
426 public void execute(
427 AwkProgram program,
428 InputSource inputSource,
429 List<String> runtimeArguments,
430 Map<String, Object> variableOverrides)
431 throws ExitException,
432 IOException {
433 AwkProgram compiledProgram = Objects.requireNonNull(program, "program");
434 InputSource resolvedSource = Objects.requireNonNull(inputSource, "inputSource");
435 resetRuntimeState(runtimeArguments, variableOverrides);
436 installProgramMetadata(compiledProgram);
437
438 jrt.prepareForExecution(settings.getFieldSeparator(), settings.getDefaultRS());
439 if (!executionSpecialVariables.isEmpty()) {
440 jrt.applySpecialVariables(executionSpecialVariables);
441 }
442 rebindResolvedInputSource(resolvedSource);
443 executeTuples(compiledProgram.top());
444 }
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459 public void executePersistingGlobals(AwkProgram program, InputSource inputSource)
460 throws ExitException,
461 IOException {
462 executePersistingGlobals(program, inputSource, Collections.<String>emptyList(), null);
463 }
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479 public void executePersistingGlobals(
480 AwkProgram program,
481 InputSource inputSource,
482 List<String> runtimeArguments)
483 throws ExitException,
484 IOException {
485 executePersistingGlobals(program, inputSource, runtimeArguments, null);
486 }
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504 public void executePersistingGlobals(
505 AwkProgram program,
506 InputSource inputSource,
507 List<String> runtimeArguments,
508 Map<String, Object> variableOverrides)
509 throws ExitException,
510 IOException {
511 AwkProgram compiledProgram = Objects.requireNonNull(program, "program");
512 InputSource resolvedSource = Objects.requireNonNull(inputSource, "inputSource");
513 mergeRuntimeState(runtimeArguments, variableOverrides, compiledProgram);
514
515 jrt.prepareForExecution(settings.getFieldSeparator(), settings.getDefaultRS());
516 if (!executionSpecialVariables.isEmpty()) {
517 jrt.applySpecialVariables(executionSpecialVariables);
518 }
519 rebindResolvedInputSource(resolvedSource);
520 executeTuples(compiledProgram.top());
521 }
522
523
524
525
526
527
528
529 public void clearPersistentGlobals() {
530 runtimeStack.clearGlobals();
531 mergedGlobalLayoutActive = false;
532 }
533
534
535
536
537
538
539
540
541
542
543 public Map<String, Object> snapshotPersistentMemory() {
544 return new LinkedHashMap<>(collectPersistentGlobalValues());
545 }
546
547
548
549
550
551
552
553
554
555
556
557 public void restorePersistentMemory(Map<String, Object> snapshot) {
558 Map<String, Object> restoredSnapshot = Objects.requireNonNull(snapshot, "snapshot");
559 Map<String, Object> restoredGlobals = filterToPersistentEligible(restoredSnapshot);
560 runtimeStack.clearGlobals();
561 if (!restoredGlobals.isEmpty()) {
562 runtimeStack.rebindGlobals(new ArrayList<>(restoredGlobals.keySet()));
563 applyGlobalsToStack(restoredGlobals);
564 }
565 mergedGlobalLayoutActive = false;
566 }
567
568 private void initExtensions() {
569 if (extensionInstances.isEmpty()) {
570 return;
571 }
572 Set<JawkExtension> initialized = new LinkedHashSet<JawkExtension>();
573 for (JawkExtension extension : extensionInstances.values()) {
574 if (initialized.add(extension)) {
575 extension.init(this, jrt, settings);
576 }
577 }
578 }
579
580
581
582 private long environOffset = NULL_OFFSET;
583 private long argcOffset = NULL_OFFSET;
584 private long argvOffset = NULL_OFFSET;
585
586 private static final Integer ZERO = Integer.valueOf(0);
587 private static final Integer ONE = Integer.valueOf(1);
588
589
590 private final BSDRandom randomNumberGenerator = new BSDRandom(1);
591
592
593
594
595
596 private Address exitAddress = null;
597
598
599
600
601
602 private Address endFileAddress = null;
603
604
605
606
607
608
609
610 private Address nextFileAddress = null;
611
612
613
614
615
616 private boolean withinBeginFileBlocks = false;
617
618
619
620
621
622 private boolean withinEndFileBlocks = false;
623
624
625
626
627
628 private boolean inputFileLoopStarted = false;
629
630
631
632
633
634 private boolean withinEndBlocks = false;
635
636
637
638
639 private int exitCode = 0;
640
641
642
643
644 private boolean throwExitException = false;
645
646
647
648
649
650
651 private Map<String, Integer> globalVariableOffsets;
652
653
654
655
656 private Map<String, Boolean> globalVariableArrays;
657 private Set<String> functionNames = Collections.emptySet();
658 private Map<String, Integer> initializedEvalGlobalVariableOffsets;
659 private Map<String, Boolean> initializedEvalGlobalVariableArrays;
660
661
662
663
664
665
666
667
668
669
670 public boolean prepareForEval(String input) throws IOException {
671 return prepareForEval(new SingleRecordInputSource(input), Collections.<String>emptyList(), null);
672 }
673
674
675
676
677
678
679
680
681
682
683
684 public boolean prepareForEval(InputSource inputSource) throws IOException {
685 return prepareForEval(inputSource, Collections.<String>emptyList(), null);
686 }
687
688 private boolean prepareForEval(
689 InputSource inputSource,
690 List<String> runtimeArguments,
691 Map<String, Object> variableOverrides)
692 throws IOException {
693 InputSource resolvedSource = Objects.requireNonNull(inputSource, "inputSource");
694 resetRuntimeState(runtimeArguments, variableOverrides);
695 rebindResolvedInputSource(resolvedSource);
696
697 jrt.jrtCloseAll();
698 jrt.prepareForExecution(settings.getFieldSeparator(), settings.getDefaultRS());
699 if (!executionSpecialVariables.isEmpty()) {
700 jrt.applySpecialVariables(executionSpecialVariables);
701 }
702 return jrt.consumeInputForEval(resolvedInputSource);
703 }
704
705 private void resetRuntimeState(List<String> runtimeArguments, Map<String, Object> variableOverrides) {
706 resetTransientRuntimeState(runtimeArguments, variableOverrides);
707 runtimeStack.clearGlobals();
708 }
709
710 private void resetTransientRuntimeState(List<String> runtimeArguments, Map<String, Object> variableOverrides) {
711
712 operandStack.clear();
713 elementArgumentReferences.clear();
714 environOffset = NULL_OFFSET;
715 argcOffset = NULL_OFFSET;
716 argvOffset = NULL_OFFSET;
717 symtabOffset = NULL_OFFSET;
718 exitAddress = null;
719 endFileAddress = null;
720 nextFileAddress = null;
721 withinBeginFileBlocks = false;
722 withinEndFileBlocks = false;
723 inputFileLoopStarted = false;
724 withinEndBlocks = false;
725 exitCode = 0;
726 throwExitException = false;
727 globalVariableOffsets = null;
728 globalVariableArrays = null;
729 functionNames = Collections.emptySet();
730 initializedEvalGlobalVariableOffsets = null;
731 initializedEvalGlobalVariableArrays = null;
732 installedEvalExpression = null;
733 mergedGlobalLayoutActive = false;
734 runtimeStack.resetTransientState();
735 randomNumberGenerator.setSeed(1);
736
737 prepareExecutionInputs(runtimeArguments, variableOverrides);
738 }
739
740 private void installExpressionMetadata(AwkExpression compiledExpression) {
741 if (installedEvalExpression == compiledExpression) {
742 return;
743 }
744 globalVariableOffsets = compiledExpression.getGlobalVariableOffsetMap();
745 globalVariableArrays = compiledExpression.getGlobalVariableAarrayMap();
746 functionNames = compiledExpression.getFunctionNameSet();
747 installedEvalExpression = compiledExpression;
748 }
749
750 private void installProgramMetadata(AwkProgram compiledProgram) {
751 globalVariableOffsets = compiledProgram.getGlobalVariableOffsetMap();
752 globalVariableArrays = compiledProgram.getGlobalVariableAarrayMap();
753 functionNames = compiledProgram.getFunctionNameSet();
754 sourceDescription = compiledProgram.getSourceDescription();
755 exitAddress = compiledProgram.getExitAddress();
756 endFileAddress = compiledProgram.getEndFileAddress();
757 nextFileAddress = compiledProgram.getNextFileAddress();
758 }
759
760 private void rebindResolvedInputSource(InputSource resolvedSource) {
761 InputSource previousResolvedSource = resolvedInputSource;
762 if (previousResolvedSource != null && previousResolvedSource != resolvedSource) {
763 closeInputSource(previousResolvedSource);
764 }
765 resolvedInputSource = resolvedSource;
766 }
767
768 private boolean hasCompatibleEvalGlobalLayout(long numGlobals) {
769 Object[] globals = runtimeStack.getNumGlobals();
770 return globals != null
771 && globals.length == numGlobals
772 && Objects.equals(initializedEvalGlobalVariableOffsets, globalVariableOffsets)
773 && Objects.equals(initializedEvalGlobalVariableArrays, globalVariableArrays);
774 }
775
776
777
778
779
780
781
782
783
784
785 private void mergeRuntimeState(
786 List<String> runtimeArguments,
787 Map<String, Object> variableOverrides,
788 AwkProgram compiledProgram) {
789 Map<String, Object> carriedGlobals = collectPersistentGlobalValues();
790 resetTransientRuntimeState(runtimeArguments, variableOverrides);
791 installProgramMetadata(compiledProgram);
792
793 Map<String, Object> basePersistentSeeds = collectBasePersistentGlobalSeeds();
794 Map<String, Object> executionUserSeeds = collectExecutionUserGlobalSeeds(variableOverrides);
795 List<String> mergedGlobalNamesByOffset = buildMergedGlobalNamesByOffset(
796 carriedGlobals,
797 basePersistentSeeds,
798 executionUserSeeds);
799
800 runtimeStack.rebindGlobals(mergedGlobalNamesByOffset);
801 applyGlobalsToStack(carriedGlobals);
802 applyGlobalsToStack(basePersistentSeeds);
803 applyGlobalsToStack(executionUserSeeds);
804 mergedGlobalLayoutActive = true;
805 }
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820 private boolean hasCompatiblePersistentGlobalLayout(long numGlobals) {
821 Object[] globals = runtimeStack.getNumGlobals();
822 if (!mergedGlobalLayoutActive
823 || globals == null
824 || globalVariableOffsets == null
825 || globals.length < numGlobals) {
826 return false;
827 }
828 for (Map.Entry<String, Integer> entry : globalVariableOffsets.entrySet()) {
829 int offset = entry.getValue().intValue();
830 if (offset < 0 || offset >= globals.length || !entry.getKey().equals(runtimeStack.getGlobalName(offset))) {
831 return false;
832 }
833 }
834 return true;
835 }
836
837
838
839
840
841
842
843
844
845
846
847
848 private void applyExecutionInitialVariablesToGlobalSlots(boolean skipPersistentEligibleGlobals) {
849 for (Map.Entry<String, Object> entry : executionInitialVariables.entrySet()) {
850 String key = entry.getKey();
851 if (skipPersistentEligibleGlobals && isPersistentEligibleGlobal(key)) {
852 continue;
853 }
854 if (functionNames.contains(key)) {
855 throw new IllegalArgumentException("Cannot assign a scalar to a function name (" + key + ").");
856 }
857 Integer offsetObj = globalVariableOffsets.get(key);
858 Boolean arrayObj = globalVariableArrays.get(key);
859 if (offsetObj != null) {
860 Object obj = normalizeExternalVariableValue(entry.getValue());
861 if (arrayObj.booleanValue()) {
862 if (obj instanceof Map) {
863 runtimeStack.setFilelistVariable(offsetObj.intValue(), obj);
864 } else {
865 throw new IllegalArgumentException(
866 "Cannot assign a scalar to a non-scalar variable (" + key + ").");
867 }
868 } else {
869 runtimeStack.setFilelistVariable(offsetObj.intValue(), obj);
870 }
871 }
872 }
873 }
874
875
876
877
878
879
880
881
882
883
884 private void prepareExecutionInputs(
885 List<String> runtimeArguments,
886 Map<String, Object> variableOverrides) {
887 this.arguments = runtimeArguments != null ? new ArrayList<>(runtimeArguments) : Collections.<String>emptyList();
888
889 if (variableOverrides == null || variableOverrides.isEmpty()) {
890 executionInitialVariables = baseInitialVariables;
891 executionSpecialVariables = baseSpecialVariables;
892 } else {
893 executionInitialVariables = new HashMap<>(baseInitialVariables);
894 executionInitialVariables.putAll(variableOverrides);
895
896 Map<String, Object> specialOverrides = JRT.copySpecialVariables(variableOverrides);
897 if (specialOverrides.isEmpty()) {
898 executionSpecialVariables = baseSpecialVariables;
899 } else {
900 executionSpecialVariables = new HashMap<>(baseSpecialVariables);
901 executionSpecialVariables.putAll(specialOverrides);
902 }
903 }
904 }
905
906
907
908
909
910
911
912
913 private Map<String, Object> filterToPersistentEligible(Map<String, Object> source) {
914 Map<String, Object> result = new LinkedHashMap<>();
915 for (Map.Entry<String, Object> entry : source.entrySet()) {
916 if (isPersistentEligibleGlobal(entry.getKey())) {
917 result.put(entry.getKey(), entry.getValue());
918 }
919 }
920 return result;
921 }
922
923
924
925
926
927
928 private Map<String, Object> collectPersistentGlobalValues() {
929 return filterToPersistentEligible(runtimeStack.snapshotGlobalVariables());
930 }
931
932
933
934
935
936
937
938 private Map<String, Object> collectBasePersistentGlobalSeeds() {
939 Map<String, Object> basePersistentSeeds = new LinkedHashMap<>();
940 for (Map.Entry<String, Object> entry : baseInitialVariables.entrySet()) {
941 String name = entry.getKey();
942 if (isPersistentEligibleGlobal(name)) {
943 validateSeededGlobalName(name);
944 Object value = normalizeExternalVariableValue(entry.getValue());
945 validateSeededGlobalValue(name, value);
946 basePersistentSeeds.put(name, value);
947 }
948 }
949 return basePersistentSeeds;
950 }
951
952
953
954
955
956
957
958
959
960
961
962 private Map<String, Object> collectExecutionUserGlobalSeeds(Map<String, Object> variableOverrides) {
963 Map<String, Object> executionUserSeeds = new LinkedHashMap<>();
964 if (variableOverrides != null) {
965 for (Map.Entry<String, Object> entry : variableOverrides.entrySet()) {
966 String name = entry.getKey();
967 if (isPersistentEligibleGlobal(name)) {
968 validateSeededGlobalName(name);
969 Object value = normalizeExternalVariableValue(entry.getValue());
970 validateSeededGlobalValue(name, value);
971 executionUserSeeds.put(name, value);
972 }
973 }
974 }
975 return executionUserSeeds;
976 }
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991 private List<String> buildMergedGlobalNamesByOffset(
992 Map<String, Object> carriedGlobals,
993 Map<String, Object> basePersistentSeeds,
994 Map<String, Object> executionUserSeeds) {
995 LinkedHashSet<String> orderedNames = new LinkedHashSet<>();
996 List<Map.Entry<String, Integer>> compiledGlobals = new ArrayList<>(globalVariableOffsets.entrySet());
997 compiledGlobals.sort(java.util.Comparator.comparingInt(Map.Entry::getValue));
998 for (Map.Entry<String, Integer> entry : compiledGlobals) {
999 orderedNames.add(entry.getKey());
1000 }
1001 orderedNames.addAll(carriedGlobals.keySet());
1002 orderedNames.addAll(basePersistentSeeds.keySet());
1003 orderedNames.addAll(executionUserSeeds.keySet());
1004 return new ArrayList<>(orderedNames);
1005 }
1006
1007
1008
1009
1010
1011
1012
1013 private void applyGlobalsToStack(Map<String, Object> globals) {
1014 for (Map.Entry<String, Object> entry : globals.entrySet()) {
1015 runtimeStack.setGlobalVariable(entry.getKey(), entry.getValue());
1016 }
1017 }
1018
1019
1020
1021
1022
1023
1024
1025
1026 private boolean isPersistentEligibleGlobal(String name) {
1027 return name != null
1028 && !isManagedSpecialVariable(name)
1029 && !NON_PERSISTENT_GLOBALS.contains(name);
1030 }
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043 private boolean isManagedSpecialVariable(String name) {
1044 if (!JRT.isJrtManagedSpecialVariable(name)) {
1045 return false;
1046 }
1047 if (JRT.isGawkOnlySpecialVariable(name)) {
1048 return !settings.isPosix();
1049 }
1050 return true;
1051 }
1052
1053
1054
1055
1056
1057
1058
1059
1060 private void validateSeededGlobalName(String name) {
1061 if (functionNames.contains(name)) {
1062 throw new IllegalArgumentException("Cannot assign a value to a function name (" + name + ").");
1063 }
1064 }
1065
1066
1067
1068
1069
1070
1071
1072
1073 private void validateSeededGlobalValue(String name, Object value) {
1074 Boolean arrayObj = globalVariableArrays.get(name);
1075 if (Boolean.TRUE.equals(arrayObj) && !(value instanceof Map)) {
1076 throw new IllegalArgumentException("Cannot assign a scalar to a non-scalar variable (" + name + ").");
1077 }
1078 }
1079
1080
1081
1082
1083
1084
1085
1086
1087 private void executeTuples(PositionTracker position)
1088 throws ExitException,
1089 IOException {
1090 Map<Long, ConditionPair> conditionPairs = null;
1091 Opcode opcode = null;
1092 long tupleStartNanos = 0L;
1093 try {
1094 while (!position.isEOF()) {
1095
1096 Tuple tuple = position.current();
1097 opcode = tuple.getOpcode();
1098 if (profiling) {
1099 tupleStartNanos = beforeProfiledTuple(tuple, opcode);
1100 }
1101
1102 switch (opcode) {
1103 case PRINT: {
1104 execPrint((CountTuple) tuple);
1105 position.next();
1106 break;
1107 }
1108 case PRINT_TO_FILE: {
1109 execPrintToFile((CountAndAppendTuple) tuple);
1110 position.next();
1111 break;
1112 }
1113 case PRINT_TO_PIPE: {
1114 execPrintToPipe((CountTuple) tuple);
1115 position.next();
1116 break;
1117 }
1118 case PRINTF: {
1119 execPrintf((CountTuple) tuple);
1120 position.next();
1121 break;
1122 }
1123 case PRINTF_TO_FILE: {
1124 execPrintfToFile((CountAndAppendTuple) tuple);
1125 position.next();
1126 break;
1127 }
1128 case PRINTF_TO_PIPE: {
1129 execPrintfToPipe((CountTuple) tuple);
1130 position.next();
1131 break;
1132 }
1133 case SPRINTF: {
1134
1135
1136
1137
1138 CountTuple countTuple = (CountTuple) tuple;
1139 long numArgs = countTuple.getCount();
1140 push(sprintfFunction(numArgs));
1141 position.next();
1142 break;
1143 }
1144 case LENGTH: {
1145 execLength((CountTuple) tuple);
1146 position.next();
1147 break;
1148 }
1149 case PUSH_LONG: {
1150
1151 PushLongTuple pushTuple = (PushLongTuple) tuple;
1152 push(pushTuple.getValue());
1153 position.next();
1154 break;
1155 }
1156 case PUSH_DOUBLE: {
1157
1158 PushDoubleTuple pushTuple = (PushDoubleTuple) tuple;
1159 push(pushTuple.getValue());
1160 position.next();
1161 break;
1162 }
1163 case PUSH_STRING: {
1164
1165 PushStringTuple pushTuple = (PushStringTuple) tuple;
1166 push(pushTuple.getValue());
1167 position.next();
1168 break;
1169 }
1170 case POP: {
1171
1172 Object discarded = pop();
1173 if (tuple instanceof ScalarPopTuple) {
1174 checkScalar(discarded);
1175 }
1176 position.next();
1177 break;
1178 }
1179 case IFFALSE: {
1180
1181
1182
1183
1184
1185
1186 boolean jump = !jrt.toBoolean(pop());
1187 if (jump) {
1188 position.jump(tuple.getAddress());
1189 } else {
1190 position.next();
1191 }
1192 break;
1193 }
1194 case TO_NUMBER: {
1195
1196
1197
1198
1199
1200 boolean val = jrt.toBoolean(pop());
1201 push(val ? ONE : ZERO);
1202 position.next();
1203 break;
1204 }
1205 case IFTRUE: {
1206
1207
1208
1209
1210
1211
1212 boolean jump = jrt.toBoolean(pop());
1213 if (jump) {
1214 position.jump(tuple.getAddress());
1215 } else {
1216 position.next();
1217 }
1218 break;
1219 }
1220 case NOT: {
1221
1222
1223 Object o = pop();
1224
1225 boolean result = jrt.toBoolean(o);
1226
1227 if (result) {
1228 push(0);
1229 } else {
1230 push(1);
1231 }
1232 position.next();
1233 break;
1234 }
1235 case NEGATE: {
1236
1237
1238 double d = JRT.toDouble(pop());
1239 push(-d);
1240 position.next();
1241 break;
1242 }
1243 case UNARY_PLUS: {
1244
1245 double d = JRT.toDouble(pop());
1246 push(d);
1247 position.next();
1248 break;
1249 }
1250 case GOTO: {
1251
1252
1253 position.jump(tuple.getAddress());
1254 break;
1255 }
1256 case NOP: {
1257
1258 position.next();
1259 break;
1260 }
1261 case CONCAT: {
1262
1263
1264 String s2 = jrt.toAwkString(pop());
1265 String s1 = jrt.toAwkString(pop());
1266 String resultString = s1 + s2;
1267 push(resultString);
1268 position.next();
1269 break;
1270 }
1271 case MULTI_CONCAT: {
1272
1273
1274 CountTuple countTuple = (CountTuple) tuple;
1275 int count = (int) countTuple.getCount();
1276
1277
1278
1279
1280 String[] values = new String[count];
1281 int resultLength = 0;
1282 for (int i = count - 1; i >= 0; i--) {
1283 values[i] = jrt.toAwkString(pop());
1284 resultLength += values[i].length();
1285 }
1286 StringBuilder resultString = new StringBuilder(resultLength);
1287 for (String value : values) {
1288 resultString.append(value);
1289 }
1290 push(resultString.toString());
1291 position.next();
1292 break;
1293 }
1294 case ASSIGN:
1295 case ASSIGN_NOPUSH: {
1296
1297
1298
1299 VariableTuple variableTuple = (VariableTuple) tuple;
1300 Object value = pop();
1301 assign(
1302 variableTuple.getVariableOffset(),
1303 value,
1304 variableTuple.isGlobal(),
1305 position,
1306 opcode == Opcode.ASSIGN);
1307 position.next();
1308 break;
1309 }
1310 case ASSIGN_ARRAY: {
1311
1312
1313
1314
1315 Object arrIdx = pop();
1316 Object rhs = pop();
1317 if (rhs == null) {
1318 rhs = BLANK;
1319 }
1320 VariableTuple variableTuple = (VariableTuple) tuple;
1321 long offset = variableTuple.getVariableOffset();
1322 boolean isGlobal = variableTuple.isGlobal();
1323 assignArray(offset, arrIdx, rhs, isGlobal);
1324 position.next();
1325 break;
1326 }
1327 case ASSIGN_MAP_ELEMENT: {
1328
1329
1330
1331 Object arrIdx = pop();
1332 Map<Object, Object> array = toMap(pop());
1333 Object rhs = pop();
1334 if (rhs == null) {
1335 rhs = BLANK;
1336 }
1337 assignMapElement(array, arrIdx, rhs);
1338 position.next();
1339 break;
1340 }
1341 case PLUS_EQ_ARRAY:
1342 case MINUS_EQ_ARRAY:
1343 case MULT_EQ_ARRAY:
1344 case DIV_EQ_ARRAY:
1345 case MOD_EQ_ARRAY:
1346 case POW_EQ_ARRAY: {
1347
1348
1349
1350
1351 Object arrIdx = pop();
1352 Object rhs = pop();
1353 if (rhs == null) {
1354 rhs = BLANK;
1355 }
1356 VariableTuple variableTuple = (VariableTuple) tuple;
1357 long offset = variableTuple.getVariableOffset();
1358 boolean isGlobal = variableTuple.isGlobal();
1359
1360 double val = JRT.toDouble(rhs);
1361
1362 Map<Object, Object> array = ensureMapVariable(offset, isGlobal);
1363 checkScalar(arrIdx);
1364 Object o = array.get(arrIdx);
1365 double origVal = JRT.toDouble(o);
1366
1367 double newVal;
1368
1369 switch (opcode) {
1370 case PLUS_EQ_ARRAY:
1371 newVal = origVal + val;
1372 break;
1373 case MINUS_EQ_ARRAY:
1374 newVal = origVal - val;
1375 break;
1376 case MULT_EQ_ARRAY:
1377 newVal = origVal * val;
1378 break;
1379 case DIV_EQ_ARRAY:
1380 newVal = origVal / val;
1381 break;
1382 case MOD_EQ_ARRAY:
1383 newVal = origVal % val;
1384 break;
1385 case POW_EQ_ARRAY:
1386 newVal = Math.pow(origVal, val);
1387 break;
1388 default:
1389 throw new Error("Invalid op code here: " + opcode);
1390 }
1391
1392 assignArray(offset, arrIdx, newVal, isGlobal);
1393 position.next();
1394 break;
1395 }
1396 case PLUS_EQ_MAP_ELEMENT:
1397 case MINUS_EQ_MAP_ELEMENT:
1398 case MULT_EQ_MAP_ELEMENT:
1399 case DIV_EQ_MAP_ELEMENT:
1400 case MOD_EQ_MAP_ELEMENT:
1401 case POW_EQ_MAP_ELEMENT: {
1402
1403
1404
1405 Object arrIdx = pop();
1406 Map<Object, Object> array = toMap(pop());
1407 Object rhs = pop();
1408 if (rhs == null) {
1409 rhs = BLANK;
1410 }
1411
1412 double val = JRT.toDouble(rhs);
1413 checkScalar(arrIdx);
1414 Object o = array.get(arrIdx);
1415 double origVal = JRT.toDouble(o);
1416 double newVal;
1417
1418 switch (opcode) {
1419 case PLUS_EQ_MAP_ELEMENT:
1420 newVal = origVal + val;
1421 break;
1422 case MINUS_EQ_MAP_ELEMENT:
1423 newVal = origVal - val;
1424 break;
1425 case MULT_EQ_MAP_ELEMENT:
1426 newVal = origVal * val;
1427 break;
1428 case DIV_EQ_MAP_ELEMENT:
1429 newVal = origVal / val;
1430 break;
1431 case MOD_EQ_MAP_ELEMENT:
1432 newVal = origVal % val;
1433 break;
1434 case POW_EQ_MAP_ELEMENT:
1435 newVal = Math.pow(origVal, val);
1436 break;
1437 default:
1438 throw new Error("Invalid op code here: " + opcode);
1439 }
1440
1441 assignMapElement(array, arrIdx, newVal);
1442 position.next();
1443 break;
1444 }
1445
1446 case ASSIGN_AS_INPUT: {
1447
1448 jrt.setInputLine(pop());
1449 push(jrt.getInputLine());
1450 position.next();
1451 break;
1452 }
1453
1454 case ASSIGN_AS_INPUT_FIELD: {
1455
1456
1457 Object fieldNumObj = pop();
1458 long fieldNum = JRT.parseFieldNumber(fieldNumObj);
1459 Object value = pop();
1460 push(value);
1461 if (fieldNum == 0) {
1462 jrt.setInputLine(value);
1463 jrt.jrtParseFields();
1464 } else {
1465 jrt.jrtSetInputField(value, fieldNum);
1466 }
1467 position.next();
1468 break;
1469 }
1470 case PLUS_EQ:
1471 case MINUS_EQ:
1472 case MULT_EQ:
1473 case DIV_EQ:
1474 case MOD_EQ:
1475 case POW_EQ: {
1476
1477
1478
1479 VariableTuple variableTuple = (VariableTuple) tuple;
1480 long offset = variableTuple.getVariableOffset();
1481 boolean isGlobal = variableTuple.isGlobal();
1482 Object o1 = resolveVariable(offset, isGlobal, false);
1483 Object o2 = pop();
1484 double d1 = JRT.toDouble(o1);
1485 double d2 = JRT.toDouble(o2);
1486 double ans;
1487 switch (opcode) {
1488 case PLUS_EQ:
1489 ans = d1 + d2;
1490 break;
1491 case MINUS_EQ:
1492 ans = d1 - d2;
1493 break;
1494 case MULT_EQ:
1495 ans = d1 * d2;
1496 break;
1497 case DIV_EQ:
1498 ans = d1 / d2;
1499 break;
1500 case MOD_EQ:
1501 ans = d1 % d2;
1502 break;
1503 case POW_EQ:
1504 ans = Math.pow(d1, d2);
1505 break;
1506 default:
1507 throw new Error("Invalid opcode here: " + opcode);
1508 }
1509 push(ans);
1510 runtimeStack.setVariable(offset, ans, isGlobal);
1511 position.next();
1512 break;
1513 }
1514 case PLUS_EQ_INPUT_FIELD:
1515 case MINUS_EQ_INPUT_FIELD:
1516 case MULT_EQ_INPUT_FIELD:
1517 case DIV_EQ_INPUT_FIELD:
1518 case MOD_EQ_INPUT_FIELD:
1519 case POW_EQ_INPUT_FIELD: {
1520
1521
1522
1523
1524 long fieldnum = JRT.parseFieldNumber(pop());
1525 double incval = JRT.toDouble(pop());
1526
1527
1528 Object numObj = jrt.jrtGetInputField(fieldnum);
1529 double num;
1530 switch (opcode) {
1531 case PLUS_EQ_INPUT_FIELD:
1532 num = JRT.toDouble(numObj) + incval;
1533 break;
1534 case MINUS_EQ_INPUT_FIELD:
1535 num = JRT.toDouble(numObj) - incval;
1536 break;
1537 case MULT_EQ_INPUT_FIELD:
1538 num = JRT.toDouble(numObj) * incval;
1539 break;
1540 case DIV_EQ_INPUT_FIELD:
1541 num = JRT.toDouble(numObj) / incval;
1542 break;
1543 case MOD_EQ_INPUT_FIELD:
1544 num = JRT.toDouble(numObj) % incval;
1545 break;
1546 case POW_EQ_INPUT_FIELD:
1547 num = Math.pow(JRT.toDouble(numObj), incval);
1548 break;
1549 default:
1550 throw new Error("Invalid opcode here: " + opcode);
1551 }
1552 setNumOnJRT(fieldnum, num);
1553
1554
1555 push(num);
1556 position.next();
1557
1558 break;
1559 }
1560 case INC: {
1561
1562
1563 VariableTuple variableTuple = (VariableTuple) tuple;
1564 inc(variableTuple.getVariableOffset(), variableTuple.isGlobal());
1565 position.next();
1566 break;
1567 }
1568 case DEC: {
1569
1570
1571 VariableTuple variableTuple = (VariableTuple) tuple;
1572 dec(variableTuple.getVariableOffset(), variableTuple.isGlobal());
1573 position.next();
1574 break;
1575 }
1576 case POSTINC: {
1577
1578
1579 pop();
1580 VariableTuple variableTuple = (VariableTuple) tuple;
1581 push(inc(variableTuple.getVariableOffset(), variableTuple.isGlobal()));
1582 position.next();
1583 break;
1584 }
1585 case POSTDEC: {
1586
1587
1588 pop();
1589 VariableTuple variableTuple = (VariableTuple) tuple;
1590 push(dec(variableTuple.getVariableOffset(), variableTuple.isGlobal()));
1591 position.next();
1592 break;
1593 }
1594 case INC_ARRAY_REF: {
1595
1596
1597
1598 VariableTuple variableTuple = (VariableTuple) tuple;
1599 boolean isGlobal = variableTuple.isGlobal();
1600 Map<Object, Object> aa = ensureMapVariable(variableTuple.getVariableOffset(), isGlobal);
1601 Object key = pop();
1602 checkScalar(key);
1603 Object o = aa.get(key);
1604 double ans = JRT.toDouble(o) + 1;
1605 aa.put(key, ans);
1606 position.next();
1607 break;
1608 }
1609 case DEC_ARRAY_REF: {
1610
1611
1612
1613 VariableTuple variableTuple = (VariableTuple) tuple;
1614 boolean isGlobal = variableTuple.isGlobal();
1615 Map<Object, Object> aa = ensureMapVariable(variableTuple.getVariableOffset(), isGlobal);
1616 Object key = pop();
1617 checkScalar(key);
1618 Object o = aa.get(key);
1619 double ans = JRT.toDouble(o) - 1;
1620 aa.put(key, ans);
1621 position.next();
1622 break;
1623 }
1624 case INC_MAP_REF: {
1625
1626
1627 Object key = pop();
1628 checkScalar(key);
1629 Map<Object, Object> aa = toMap(pop());
1630 Object o = aa.get(key);
1631 double ans = JRT.toDouble(o) + 1;
1632 aa.put(key, ans);
1633 position.next();
1634 break;
1635 }
1636 case DEC_MAP_REF: {
1637
1638
1639 Object key = pop();
1640 checkScalar(key);
1641 Map<Object, Object> aa = toMap(pop());
1642 Object o = aa.get(key);
1643 double ans = JRT.toDouble(o) - 1;
1644 aa.put(key, ans);
1645 position.next();
1646 break;
1647 }
1648 case INC_DOLLAR_REF: {
1649
1650 long fieldnum = JRT.parseFieldNumber(pop());
1651
1652 Object numObj = jrt.jrtGetInputField(fieldnum);
1653 double original = JRT.toDouble(numObj);
1654 double num = original + 1;
1655 setNumOnJRT(fieldnum, num);
1656
1657 push(Double.valueOf(original));
1658
1659 position.next();
1660 break;
1661 }
1662 case DEC_DOLLAR_REF: {
1663
1664
1665 long fieldnum = JRT.parseFieldNumber(pop());
1666
1667 Object numObj = jrt.jrtGetInputField(fieldnum);
1668 double original = JRT.toDouble(numObj);
1669 double num = original - 1;
1670 setNumOnJRT(fieldnum, num);
1671
1672 push(Double.valueOf(original));
1673
1674 position.next();
1675 break;
1676 }
1677 case DEREFERENCE: {
1678
1679
1680 DereferenceTuple dereferenceTuple = (DereferenceTuple) tuple;
1681 boolean isGlobal = dereferenceTuple.isGlobal();
1682 long offset = dereferenceTuple.getVariableOffset();
1683 push(resolveVariable(offset, isGlobal, dereferenceTuple.isArray()));
1684 position.next();
1685 break;
1686 }
1687 case PEEK_DEREFERENCE: {
1688 VariableTuple variableTuple = (VariableTuple) tuple;
1689 Object value = runtimeStack
1690 .getVariable(variableTuple.getVariableOffset(), variableTuple.isGlobal());
1691 push(
1692 value instanceof ArgumentReference ?
1693 resolveRawArgumentReference((ArgumentReference) value) : value);
1694 position.next();
1695 break;
1696 }
1697 case PUSH_INDIRECT_ARGUMENT: {
1698 VariableTuple variableTuple = (VariableTuple) tuple;
1699 Object scalarValue = runtimeStack
1700 .getVariable(variableTuple.getVariableOffset(), variableTuple.isGlobal());
1701
1702
1703 if (scalarValue instanceof ArgumentReference) {
1704 push(resolveUserFunctionArgument(scalarValue));
1705 } else if (isUntyped(scalarValue)) {
1706 push(
1707 new IndirectArgumentReference(
1708 runtimeStack.getVariableFrame(variableTuple.isGlobal()),
1709 variableTuple.getVariableOffset(),
1710 scalarValue));
1711 } else {
1712 push(scalarValue);
1713 }
1714 position.next();
1715 break;
1716 }
1717 case PUSH_INDIRECT_ARRAY_ARGUMENT: {
1718 Object idx = pop();
1719 checkScalar(idx);
1720 Map<Object, Object> map = toMap(pop());
1721 Object scalarValue = JRT.getAssocArrayValue(map, idx);
1722
1723
1724 push(
1725 isUntyped(scalarValue) ?
1726 new IndirectArrayArgumentReference(map, idx, scalarValue) : scalarValue);
1727 position.next();
1728 break;
1729 }
1730 case DEREF_ARRAY: {
1731
1732 Object idx = pop();
1733 checkScalar(idx);
1734 Map<Object, Object> map = toMap(pop());
1735 Object o = JRT.getAssocArrayValue(map, idx);
1736 push(o);
1737 position.next();
1738 break;
1739 }
1740 case ENSURE_ARRAY_ELEMENT: {
1741
1742
1743 Object idx = pop();
1744 Map<Object, Object> map = toMap(pop());
1745 push(ensureArrayInArray(map, idx));
1746 position.next();
1747 break;
1748 }
1749 case PEEK_ARRAY_ELEMENT: {
1750
1751 Object idx = pop();
1752 checkScalar(idx);
1753 Map<Object, Object> map = toMap(pop());
1754 if (map instanceof AssocArray && !JRT.containsAwkKey(map, idx)) {
1755 push(AssocArray.UNTYPED);
1756 } else {
1757 Object value = map.get(idx);
1758 push(value != null ? value : AssocArray.UNTYPED);
1759 }
1760 position.next();
1761 break;
1762 }
1763 case SRAND: {
1764
1765
1766 CountTuple countTuple = (CountTuple) tuple;
1767 long numArgs = countTuple.getCount();
1768 int seed;
1769 if (numArgs == 0) {
1770
1771 seed = JRT.timeSeed();
1772 } else {
1773 seed = (int) JRT.toDouble(pop());
1774 }
1775 int previousSeed = randomNumberGenerator.getSeed();
1776 randomNumberGenerator.setSeed(seed);
1777 push(previousSeed);
1778 position.next();
1779 break;
1780 }
1781 case RAND: {
1782 push(randomNumberGenerator.nextDouble());
1783 position.next();
1784 break;
1785 }
1786 case INTFUNC: {
1787
1788 push((long) JRT.toDouble(pop()));
1789 position.next();
1790 break;
1791 }
1792 case SQRT: {
1793
1794 push(Math.sqrt(JRT.toDouble(pop())));
1795 position.next();
1796 break;
1797 }
1798 case LOG: {
1799
1800 push(Math.log(JRT.toDouble(pop())));
1801 position.next();
1802 break;
1803 }
1804 case EXP: {
1805
1806 push(Math.exp(JRT.toDouble(pop())));
1807 position.next();
1808 break;
1809 }
1810 case SIN: {
1811
1812 push(Math.sin(JRT.toDouble(pop())));
1813 position.next();
1814 break;
1815 }
1816 case COS: {
1817
1818 push(Math.cos(JRT.toDouble(pop())));
1819 position.next();
1820 break;
1821 }
1822 case ATAN2: {
1823
1824
1825 double d2 = JRT.toDouble(pop());
1826 double d1 = JRT.toDouble(pop());
1827 push(Math.atan2(d1, d2));
1828 position.next();
1829 break;
1830 }
1831 case MATCH: {
1832 execMatch();
1833 position.next();
1834 break;
1835 }
1836 case INDEX: {
1837
1838
1839 String s2 = jrt.toAwkString(pop());
1840 String s1 = jrt.toAwkString(pop());
1841 push(jrt.index(s1, s2));
1842 position.next();
1843 break;
1844 }
1845 case SUB_FOR_DOLLAR_0: {
1846 execSubForDollar0((BooleanTuple) tuple);
1847 position.next();
1848 break;
1849 }
1850 case SUB_FOR_DOLLAR_REFERENCE: {
1851 execSubForDollarReference((BooleanTuple) tuple);
1852 position.next();
1853 break;
1854 }
1855 case SUB_FOR_VARIABLE: {
1856 execSubForVariable((SubstitutionVariableTuple) tuple, position);
1857 position.next();
1858 break;
1859 }
1860 case SUB_FOR_ARRAY_REFERENCE: {
1861 execSubForArrayReference((SubstitutionVariableTuple) tuple);
1862 position.next();
1863 break;
1864 }
1865 case SUB_FOR_MAP_REFERENCE: {
1866 execSubForMapReference((BooleanTuple) tuple);
1867 position.next();
1868 break;
1869 }
1870 case SPLIT: {
1871 execSplit((CountTuple) tuple, position);
1872 position.next();
1873 break;
1874 }
1875 case SUBSTR: {
1876 execSubstr((CountTuple) tuple);
1877 position.next();
1878 break;
1879 }
1880 case TOLOWER: {
1881
1882 push(jrt.toAwkString(pop()).toLowerCase());
1883 position.next();
1884 break;
1885 }
1886 case TOUPPER: {
1887
1888 push(jrt.toAwkString(pop()).toUpperCase());
1889 position.next();
1890 break;
1891 }
1892 case SYSTEM: {
1893
1894 String s = jrt.toAwkString(pop());
1895 push(jrt.jrtSystem(s));
1896 position.next();
1897 break;
1898 }
1899 case SWAP: {
1900
1901
1902 Object o1 = pop();
1903 Object o2 = pop();
1904 push(o1);
1905 push(o2);
1906 position.next();
1907 break;
1908 }
1909 case CMP_EQ: {
1910
1911
1912 Object o2 = pop();
1913 Object o1 = pop();
1914 push(JRT.compare2(o1, o2, 0, jrt.isIgnoreCase()) ? ONE : ZERO);
1915 position.next();
1916 break;
1917 }
1918 case CMP_LT: {
1919
1920
1921 Object o2 = pop();
1922 Object o1 = pop();
1923 push(JRT.compare2(o1, o2, -1, jrt.isIgnoreCase()) ? ONE : ZERO);
1924 position.next();
1925 break;
1926 }
1927 case CMP_GT: {
1928
1929
1930 Object o2 = pop();
1931 Object o1 = pop();
1932 push(JRT.compare2(o1, o2, 1, jrt.isIgnoreCase()) ? ONE : ZERO);
1933 position.next();
1934 break;
1935 }
1936 case MATCHES: {
1937
1938
1939 Object o2 = pop();
1940 Object o1 = pop();
1941 push(jrt.matches(o1.toString(), o2) ? 1 : 0);
1942 position.next();
1943 break;
1944 }
1945 case ADD: {
1946
1947
1948 Object o2 = pop();
1949 Object o1 = pop();
1950 double d1 = JRT.toDouble(o1);
1951 double d2 = JRT.toDouble(o2);
1952 double ans = d1 + d2;
1953 push(ans);
1954 position.next();
1955 break;
1956 }
1957 case SUBTRACT: {
1958
1959
1960 Object o2 = pop();
1961 Object o1 = pop();
1962 double d1 = JRT.toDouble(o1);
1963 double d2 = JRT.toDouble(o2);
1964 double ans = d1 - d2;
1965 push(ans);
1966 position.next();
1967 break;
1968 }
1969 case MULTIPLY: {
1970
1971
1972 Object o2 = pop();
1973 Object o1 = pop();
1974 double d1 = JRT.toDouble(o1);
1975 double d2 = JRT.toDouble(o2);
1976 double ans = d1 * d2;
1977 push(ans);
1978 position.next();
1979 break;
1980 }
1981 case DIVIDE: {
1982
1983
1984 Object o2 = pop();
1985 Object o1 = pop();
1986 double d1 = JRT.toDouble(o1);
1987 double d2 = JRT.toDouble(o2);
1988 double ans = d1 / d2;
1989 push(ans);
1990 position.next();
1991 break;
1992 }
1993 case MOD: {
1994
1995
1996 Object o2 = pop();
1997 Object o1 = pop();
1998 double d1 = JRT.toDouble(o1);
1999 double d2 = JRT.toDouble(o2);
2000 double ans = d1 % d2;
2001 push(ans);
2002 position.next();
2003 break;
2004 }
2005 case POW: {
2006
2007
2008 Object o2 = pop();
2009 Object o1 = pop();
2010 double d1 = JRT.toDouble(o1);
2011 double d2 = JRT.toDouble(o2);
2012 double ans = Math.pow(d1, d2);
2013 push(ans);
2014 position.next();
2015 break;
2016 }
2017 case DUP: {
2018
2019 Object o = pop();
2020 push(o);
2021 push(o);
2022 position.next();
2023 break;
2024 }
2025 case KEYLIST: {
2026 Object o = pop();
2027 if (isUntyped(o)) {
2028 push(new ArrayDeque<>());
2029 position.next();
2030 break;
2031 }
2032 if (!(o instanceof Map)) {
2033 throw new AwkRuntimeException("Attempting to use a scalar as an array.");
2034 }
2035 @SuppressWarnings("unchecked")
2036 Map<Object, Object> map = (Map<Object, Object>) o;
2037 push(new ArrayDeque<>(forInKeyOrder == null ? map.keySet() : forInKeyOrder.order(map)));
2038 position.next();
2039 break;
2040 }
2041 case IS_EMPTY_KEYLIST: {
2042
2043
2044 Object o = pop();
2045 if (o == null || !(o instanceof Deque)) {
2046 throw new AwkRuntimeException(
2047 position.lineNumber(),
2048 "Cannot get a key list (via 'in') of a non associative array. arg = " + o.getClass() + ", " + o);
2049 }
2050 Deque<?> keylist = (Deque<?>) o;
2051 if (keylist.isEmpty()) {
2052 position.jump(tuple.getAddress());
2053 } else {
2054 position.next();
2055 }
2056 break;
2057 }
2058 case GET_FIRST_AND_REMOVE_FROM_KEYLIST: {
2059
2060 Object o = pop();
2061 if (o == null || !(o instanceof Deque)) {
2062 throw new AwkRuntimeException(
2063 position.lineNumber(),
2064 "Cannot get a key list (via 'in') of a non associative array. arg = " + o.getClass() + ", " + o);
2065 }
2066
2067 Deque<?> keylist = (Deque<?>) o;
2068 push(keylist.removeFirst());
2069 position.next();
2070 break;
2071 }
2072 case CHECK_CLASS: {
2073
2074
2075 ClassTuple checkTuple = (ClassTuple) tuple;
2076 Object o = pop();
2077 if (!checkTuple.getType().isInstance(o)) {
2078 throw new AwkRuntimeException(
2079 position.lineNumber(),
2080 "Verification failed. Top-of-stack = " + o.getClass() + " isn't an instance of "
2081 + checkTuple.getType());
2082 }
2083 push(o);
2084 position.next();
2085 break;
2086 }
2087 case CONSUME_INPUT: {
2088
2089
2090 if (jrt.consumeInput(resolvedInputSource)) {
2091 position.next();
2092 } else {
2093 position.jump(tuple.getAddress());
2094 }
2095 break;
2096 }
2097 case CONSUME_FILE_INPUT: {
2098
2099
2100 withinBeginFileBlocks = false;
2101 if (jrt.consumeCurrentFileInput(resolvedInputSource)) {
2102 position.next();
2103 } else {
2104 withinEndFileBlocks = true;
2105 position.jump(tuple.getAddress());
2106 }
2107 break;
2108 }
2109 case NEXT_FILE: {
2110
2111 inputFileLoopStarted = true;
2112 withinEndFileBlocks = false;
2113 if (jrt.advanceToNextFile(resolvedInputSource)) {
2114 withinBeginFileBlocks = true;
2115 position.next();
2116 } else {
2117 position.jump(tuple.getAddress());
2118 }
2119 break;
2120 }
2121 case EXEC_NEXTFILE: {
2122 executeNextfile(position);
2123 break;
2124 }
2125
2126 case GETLINE_INPUT: {
2127 checkGetlineAllowed(position);
2128 boolean consumed = isMainInputFileBounded() ?
2129 jrt.consumeCurrentFileInput(resolvedInputSource) : jrt.consumeInput(resolvedInputSource);
2130 push(consumed ? 1 : 0);
2131 position.next();
2132 break;
2133 }
2134 case GETLINE_INPUT_TO_TARGET: {
2135 checkGetlineAllowed(position);
2136 Object input = isMainInputFileBounded() ?
2137 jrt.consumeCurrentFileInputToTarget(resolvedInputSource) : jrt.consumeInputToTarget(resolvedInputSource);
2138 if (input != null) {
2139 push(1);
2140 push(input);
2141 } else {
2142 push(0);
2143 push("");
2144 }
2145 position.next();
2146 break;
2147 }
2148 case USE_AS_FILE_INPUT: {
2149
2150 String s = jrt.toAwkString(pop());
2151 if (jrt.jrtConsumeFileInput(s)) {
2152 push(1);
2153 push(jrt.getInputLine());
2154 } else {
2155 push(0);
2156 push("");
2157 }
2158 position.next();
2159 break;
2160 }
2161 case USE_AS_COMMAND_INPUT: {
2162
2163 String s = jrt.toAwkString(pop());
2164 if (jrt.jrtConsumeCommandInput(s)) {
2165 push(1);
2166 push(jrt.getInputLine());
2167 } else {
2168 push(0);
2169 push("");
2170 }
2171 position.next();
2172 break;
2173 }
2174 case ENVIRON_OFFSET: {
2175
2176
2177 populateEnviron(((LongTuple) tuple).getValue());
2178 position.next();
2179 break;
2180 }
2181 case ARGC_OFFSET: {
2182
2183 populateArgc(((LongTuple) tuple).getValue());
2184 position.next();
2185 break;
2186 }
2187 case ARGV_OFFSET: {
2188
2189 populateArgv(((LongTuple) tuple).getValue());
2190 position.next();
2191 break;
2192 }
2193 case GET_INPUT_FIELD: {
2194
2195 Object fieldNumber = pop();
2196 push(jrt.jrtGetInputField(fieldNumber));
2197 position.next();
2198 break;
2199 }
2200 case GET_INPUT_FIELD_CONST: {
2201 InputFieldTuple inputFieldTuple = (InputFieldTuple) tuple;
2202 long fieldnum = inputFieldTuple.getFieldIndex();
2203 push(jrt.jrtGetInputField(fieldnum));
2204 position.next();
2205 break;
2206 }
2207 case APPLY_RS: {
2208 jrt.applyRS(jrt.getRSVar());
2209 position.next();
2210 break;
2211 }
2212 case CALL_FUNCTION: {
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222 CallFunctionTuple callTuple = (CallFunctionTuple) tuple;
2223 Address funcAddr = callTuple.getAddress();
2224 long numFormalParams = callTuple.getNumFormalParams();
2225 long numActualParams = callTuple.getNumActualParams();
2226 runtimeStack.pushFrame(numFormalParams, position.currentIndex());
2227
2228 for (long i = numActualParams - 1; i >= 0; i--) {
2229 Object argument = pop();
2230 adoptElementArgumentReference(argument);
2231 runtimeStack.setVariable(i, argument, false);
2232 }
2233 position.jump(funcAddr);
2234
2235 break;
2236 }
2237 case INDIRECT_CALL: {
2238 IndirectCallTuple callTuple = (IndirectCallTuple) tuple;
2239 Object[] actualArguments = popArguments(callTuple.getNumActualParams());
2240 String requestedName = jrt.toAwkString(pop());
2241 String qualifiedName = normalizeIndirectFunctionName(requestedName);
2242 IndirectFunctionTarget target = callTuple.getUserFunctions().get(qualifiedName);
2243 if (target != null) {
2244 long formalCount = target.getNumFormalParams();
2245 if (actualArguments.length > formalCount) {
2246 jrt
2247 .printWarning(
2248 "gawk: "
2249 + callTuple.getSourceName()
2250 + ":"
2251 + callTuple.getSourceLine()
2252 + ": warning: function `"
2253 + qualifiedName
2254 + "' called with more arguments than declared");
2255 }
2256 if (profiling) {
2257 activeProfilingFunctions.push(new ActiveFunction(qualifiedName, tupleStartNanos));
2258 }
2259 runtimeStack.pushFrame(formalCount, position.currentIndex());
2260 adoptElementArgumentReferences(actualArguments);
2261 int copiedArgumentCount = Math.min(actualArguments.length, (int) formalCount);
2262 for (int i = 0; i < copiedArgumentCount; i++) {
2263 runtimeStack.setVariable(i, actualArguments[i], false);
2264 }
2265 position.jump(target.getAddress());
2266 break;
2267 }
2268
2269 String awkName = requestedName.startsWith("awk::") ?
2270 requestedName.substring("awk::".length()) : requestedName;
2271 BuiltinFunction builtin = BuiltinFunction.of(awkName);
2272 if (builtin != null) {
2273 resolveIndirectArguments(actualArguments, builtin);
2274 push(invokeIndirectBuiltin(builtin, actualArguments, position.lineNumber()));
2275 position.next();
2276 break;
2277 }
2278 ExtensionFunction extensionFunction = callTuple.getExtensionFunctions().get(awkName);
2279 if (extensionFunction != null) {
2280 resolveIndirectArguments(actualArguments, extensionFunction);
2281 if (profiling) {
2282 activeProfilingFunctions.push(new ActiveFunction(awkName, tupleStartNanos));
2283 }
2284 try {
2285 push(
2286 invokeExtension(
2287 extensionFunction,
2288 actualArguments,
2289 position.lineNumber(),
2290 true));
2291 } finally {
2292 if (profiling) {
2293 recordFunctionExit(System.nanoTime());
2294 }
2295 }
2296 position.next();
2297 break;
2298 }
2299 throw new AwkRuntimeException(
2300 position.lineNumber(),
2301 "function `" + qualifiedName + "' is not defined");
2302 }
2303 case FUNCTION: {
2304
2305
2306
2307
2308 position.next();
2309 break;
2310 }
2311 case WARNING: {
2312 jrt.printWarning(((Tuple.WarningTuple) tuple).getMessage());
2313 position.next();
2314 break;
2315 }
2316 case SET_RETURN_RESULT: {
2317
2318 runtimeStack.setReturnValue(pop());
2319 position.next();
2320 break;
2321 }
2322 case RETURN_FROM_FUNCTION: {
2323 releaseElementArgumentReferences();
2324 position.jump(runtimeStack.popFrame());
2325 push(runtimeStack.getReturnValue());
2326 position.next();
2327 break;
2328 }
2329 case SET_NUM_GLOBALS: {
2330 execSetNumGlobals((CountTuple) tuple);
2331 position.next();
2332 break;
2333 }
2334 case BEFORE_START_HOOKS: {
2335 runBeforeStartHooks();
2336 position.next();
2337 break;
2338 }
2339 case UPDATE_SYMTAB: {
2340 execUpdateSymtab(((LongTuple) tuple).getValue());
2341 position.next();
2342 break;
2343 }
2344 case UPDATE_FUNCTAB: {
2345 execUpdateFunctab(((LongTuple) tuple).getValue());
2346 position.next();
2347 break;
2348 }
2349 case CLOSE: {
2350
2351 String s = jrt.toAwkString(pop());
2352 push(jrt.jrtClose(s));
2353 position.next();
2354 break;
2355 }
2356 case APPLY_SUBSEP: {
2357 execApplySubsep((CountTuple) tuple);
2358 position.next();
2359 break;
2360 }
2361 case DELETE_ARRAY_ELEMENT: {
2362
2363
2364
2365 VariableTuple variableTuple = (VariableTuple) tuple;
2366 long offset = variableTuple.getVariableOffset();
2367 boolean isGlobal = variableTuple.isGlobal();
2368 Map<Object, Object> aa = getMapVariable(offset, isGlobal);
2369 Object key = pop();
2370 checkScalar(key);
2371 if (aa != null) {
2372 aa.remove(key);
2373 detachMissingArrayArgumentReferences(aa);
2374 }
2375 position.next();
2376 break;
2377 }
2378 case DELETE_MAP_ELEMENT: {
2379
2380
2381 Object key = pop();
2382 checkScalar(key);
2383 Map<Object, Object> aa = toMap(pop());
2384 aa.remove(key);
2385 detachMissingArrayArgumentReferences(aa);
2386 position.next();
2387 break;
2388 }
2389 case DELETE_ARRAY: {
2390
2391
2392
2393 VariableTuple variableTuple = (VariableTuple) tuple;
2394 long offset = variableTuple.getVariableOffset();
2395 boolean isGlobal = variableTuple.isGlobal();
2396 Map<Object, Object> array = getMapVariable(offset, isGlobal);
2397 if (array != null) {
2398 array.clear();
2399 detachMissingArrayArgumentReferences(array);
2400 }
2401 position.next();
2402 break;
2403 }
2404 case SET_WITHIN_END_BLOCKS: {
2405
2406 BooleanTuple endBlocksTuple = (BooleanTuple) tuple;
2407 withinEndBlocks = endBlocksTuple.getValue();
2408 position.next();
2409 break;
2410 }
2411 case EXIT_WITHOUT_CODE:
2412 case EXIT_WITH_CODE: {
2413 if (opcode == Opcode.EXIT_WITH_CODE) {
2414
2415 exitCode = (int) JRT.toDouble(pop());
2416 }
2417 throwExitException = true;
2418 withinBeginFileBlocks = false;
2419 withinEndFileBlocks = false;
2420
2421
2422 if (!withinEndBlocks && exitAddress != null) {
2423 resetCallState();
2424 position.jump(exitAddress);
2425 } else {
2426
2427
2428 operandStack.clear();
2429 throw new ExitException(exitCode, "The AWK script requested an exit");
2430
2431 }
2432 break;
2433 }
2434 case REGEXP: {
2435
2436 RegexTuple regexTuple = (RegexTuple) tuple;
2437 Pattern pattern = regexTuple.getPattern();
2438 push(pattern);
2439 position.next();
2440 break;
2441 }
2442 case CONDITION_PAIR: {
2443
2444
2445
2446 if (conditionPairs == null) {
2447 conditionPairs = new HashMap<Long, ConditionPair>();
2448 }
2449 long currentIndex = position.currentIndex();
2450 ConditionPair cp = conditionPairs.get(currentIndex);
2451 if (cp == null) {
2452 cp = new ConditionPair();
2453 conditionPairs.put(currentIndex, cp);
2454 }
2455 boolean end = jrt.toBoolean(pop());
2456 boolean start = jrt.toBoolean(pop());
2457 push(cp.update(start, end) ? ONE : ZERO);
2458 position.next();
2459 break;
2460 }
2461 case CONDITION_PAIR_IN_RANGE: {
2462
2463 if (conditionPairs == null) {
2464 conditionPairs = new HashMap<Long, ConditionPair>();
2465 }
2466 long id = ((LongTuple) tuple).getValue();
2467 ConditionPair cp = conditionPairs.get(id);
2468 if (cp == null) {
2469 cp = new ConditionPair();
2470 conditionPairs.put(id, cp);
2471 }
2472 push(cp.isWithin() ? ONE : ZERO);
2473 position.next();
2474 break;
2475 }
2476 case CONDITION_PAIR_ENTER: {
2477
2478
2479
2480 conditionPairs.get(((LongTuple) tuple).getValue()).enter();
2481 position.next();
2482 break;
2483 }
2484 case CONDITION_PAIR_LEAVE: {
2485
2486 conditionPairs.get(((LongTuple) tuple).getValue()).leave();
2487 position.next();
2488 break;
2489 }
2490 case IS_IN: {
2491
2492 Object arr = pop();
2493 Object arg = pop();
2494 checkScalar(arg);
2495 if (isUntyped(arr)) {
2496 push(ZERO);
2497 position.next();
2498 break;
2499 }
2500 if (!(arr instanceof Map)) {
2501 throw new AwkRuntimeException("Attempting to use a scalar as an array.");
2502 }
2503 @SuppressWarnings("unchecked")
2504 Map<Object, Object> aa = (Map<Object, Object>) arr;
2505 boolean result = JRT.containsAwkKey(aa, arg);
2506 push(result ? ONE : ZERO);
2507 position.next();
2508 break;
2509 }
2510 case THIS: {
2511
2512
2513
2514
2515 position.next();
2516 break;
2517 }
2518 case EXTENSION: {
2519
2520
2521
2522
2523
2524
2525
2526 ExtensionTuple extensionTuple = (ExtensionTuple) tuple;
2527 ExtensionFunction function = extensionTuple.getFunction();
2528 long numArgs = extensionTuple.getArgCount();
2529 boolean isInitial = extensionTuple.isInitial();
2530 push(
2531 invokeExtension(
2532 function,
2533 popArguments(numArgs),
2534 position.lineNumber(),
2535 isInitial));
2536
2537 position.next();
2538 break;
2539 }
2540 case ASSIGN_NF: {
2541 Object v = pop();
2542 jrt.setNF(v);
2543 push(v);
2544 position.next();
2545 break;
2546 }
2547 case PUSH_NF: {
2548 push(jrt.getNF());
2549 position.next();
2550 break;
2551 }
2552 case ASSIGN_NR: {
2553 Object v = pop();
2554 jrt.setNR(v);
2555 push(v);
2556 position.next();
2557 break;
2558 }
2559 case PUSH_NR: {
2560 push(jrt.getNR());
2561 position.next();
2562 break;
2563 }
2564 case ASSIGN_FNR: {
2565 Object v = pop();
2566 jrt.setFNR(v);
2567 push(v);
2568 position.next();
2569 break;
2570 }
2571 case PUSH_FNR: {
2572 push(jrt.getFNR());
2573 position.next();
2574 break;
2575 }
2576 case ASSIGN_FS: {
2577 Object v = pop();
2578 jrt.setFS(v);
2579 push(v);
2580 position.next();
2581 break;
2582 }
2583 case ASSIGN_IGNORECASE: {
2584 Object v = pop();
2585 jrt.setIGNORECASE(v);
2586 push(v);
2587 position.next();
2588 break;
2589 }
2590 case PUSH_IGNORECASE: {
2591 push(jrt.getIGNORECASEVar());
2592 position.next();
2593 break;
2594 }
2595 case PUSH_FS: {
2596 push(jrt.getFSVar());
2597 position.next();
2598 break;
2599 }
2600 case ASSIGN_RS: {
2601 Object v = pop();
2602 jrt.setRS(v);
2603 push(v);
2604 position.next();
2605 break;
2606 }
2607 case PUSH_RS: {
2608 push(jrt.getRSVar());
2609 position.next();
2610 break;
2611 }
2612 case ASSIGN_OFS: {
2613 Object v = pop();
2614 jrt.setOFS(v);
2615 push(v);
2616 position.next();
2617 break;
2618 }
2619 case PUSH_OFS: {
2620 push(jrt.getOFSVar());
2621 position.next();
2622 break;
2623 }
2624 case ASSIGN_ORS: {
2625 Object v = pop();
2626 jrt.setORS(v);
2627 push(v);
2628 position.next();
2629 break;
2630 }
2631 case PUSH_ORS: {
2632 push(jrt.getORSVar());
2633 position.next();
2634 break;
2635 }
2636 case ASSIGN_RSTART: {
2637 Object v = pop();
2638 jrt.setRSTART(v);
2639 push(v);
2640 position.next();
2641 break;
2642 }
2643 case PUSH_RSTART: {
2644 push(jrt.getRSTART());
2645 position.next();
2646 break;
2647 }
2648 case ASSIGN_RLENGTH: {
2649 Object v = pop();
2650 jrt.setRLENGTH(v);
2651 push(v);
2652 position.next();
2653 break;
2654 }
2655 case PUSH_RLENGTH: {
2656 push(jrt.getRLENGTH());
2657 position.next();
2658 break;
2659 }
2660 case ASSIGN_FILENAME: {
2661 Object v = pop();
2662 jrt.setFILENAMEViaJrt(v);
2663 push(v == null ? "" : v);
2664 position.next();
2665 break;
2666 }
2667 case PUSH_FILENAME: {
2668 push(jrt.getFILENAME());
2669 position.next();
2670 break;
2671 }
2672 case ASSIGN_ERRNO: {
2673 Object v = pop();
2674 jrt.setERRNO(v);
2675 push(v == null ? "" : v);
2676 position.next();
2677 break;
2678 }
2679 case PUSH_ERRNO: {
2680 push(jrt.getERRNO());
2681 position.next();
2682 break;
2683 }
2684 case ASSIGN_ARGIND: {
2685 Object v = pop();
2686 jrt.setARGIND(v);
2687 push(v == null ? ZERO : v);
2688 position.next();
2689 break;
2690 }
2691 case PUSH_ARGIND: {
2692 push(jrt.getARGIND());
2693 position.next();
2694 break;
2695 }
2696 case ASSIGN_SUBSEP: {
2697 Object v = pop();
2698 jrt.setSUBSEP(v);
2699 push(v);
2700 position.next();
2701 break;
2702 }
2703 case PUSH_SUBSEP: {
2704 push(jrt.getSUBSEPVar());
2705 position.next();
2706 break;
2707 }
2708 case ASSIGN_CONVFMT: {
2709 Object v = pop();
2710 jrt.setCONVFMT(v);
2711 push(v);
2712 position.next();
2713 break;
2714 }
2715 case PUSH_CONVFMT: {
2716 push(jrt.getCONVFMTVar());
2717 position.next();
2718 break;
2719 }
2720 case ASSIGN_OFMT: {
2721 Object v = pop();
2722 jrt.setOFMT(v);
2723 push(v);
2724 position.next();
2725 break;
2726 }
2727 case PUSH_OFMT: {
2728 push(getOFMT());
2729 position.next();
2730 break;
2731 }
2732 case ASSIGN_ARGC: {
2733 Object v = pop();
2734 if (argcOffset == NULL_OFFSET) {
2735 throw new AwkRuntimeException("ARGC is read-only (not materialized).");
2736 }
2737 runtimeStack.setVariable(argcOffset, v, true);
2738 push(v);
2739 position.next();
2740 break;
2741 }
2742 case PUSH_ARGC: {
2743 if (argcOffset == NULL_OFFSET) {
2744 push(getARGC());
2745 } else {
2746 push(runtimeStack.getVariable(argcOffset, true));
2747 }
2748 position.next();
2749 break;
2750 }
2751 default:
2752 throw new Error("invalid opcode: " + position.opcode());
2753 }
2754 if (profiling) {
2755 afterProfiledTuple(opcode, tupleStartNanos);
2756 }
2757 }
2758
2759 } catch (ExitException ee) {
2760 if (profiling && (opcode == Opcode.EXIT_WITH_CODE || opcode == Opcode.EXIT_WITHOUT_CODE)) {
2761 afterProfiledTuple(opcode, tupleStartNanos);
2762 }
2763 throw ee;
2764 } catch (IOException ioe) {
2765 resetCallState();
2766 throw ioe;
2767 } catch (RuntimeException re) {
2768 resetCallState();
2769 if (re instanceof AwkSandboxException) {
2770 throw re;
2771 }
2772 throw new AwkRuntimeException(position.lineNumber(), re.getMessage(), re);
2773 } catch (AssertionError ae) {
2774 resetCallState();
2775 throw ae;
2776 }
2777
2778
2779 if (throwExitException) {
2780 throw new ExitException(exitCode, "The AWK script requested an exit");
2781 }
2782 }
2783
2784
2785
2786
2787 public void resetProfiling() {
2788 if (!profiling) {
2789 return;
2790 }
2791 tupleProfilingStats.clear();
2792 functionProfilingStats.clear();
2793 activeProfilingFunctions.clear();
2794 }
2795
2796
2797
2798
2799
2800
2801 public ProfilingReport getProfilingReport() {
2802 if (!profiling) {
2803 return ProfilingReport.empty();
2804 }
2805 return new ProfilingReport(tupleProfilingStats, functionProfilingStats);
2806 }
2807
2808 private void execPrint(CountTuple tuple) throws IOException {
2809 long numArgs = tuple.getCount();
2810 jrt.printDefault(numArgs == 0 ? new Object[] { jrt.jrtGetInputField(0) } : popArguments(numArgs));
2811 }
2812
2813 private void execPrintToFile(CountAndAppendTuple tuple) throws IOException {
2814 String key = jrt.toAwkString(pop());
2815 long numArgs = tuple.getCount();
2816 jrt
2817 .printToFile(
2818 key,
2819 tuple.isAppend(),
2820 numArgs == 0 ? new Object[]
2821 { jrt.jrtGetInputField(0) } : popArguments(numArgs));
2822 }
2823
2824 private void execPrintToPipe(CountTuple tuple) throws IOException {
2825 String cmd = jrt.toAwkString(pop());
2826 long numArgs = tuple.getCount();
2827 jrt.printToProcess(cmd, numArgs == 0 ? new Object[] { jrt.jrtGetInputField(0) } : popArguments(numArgs));
2828 }
2829
2830 private void execPrintf(CountTuple tuple) throws IOException {
2831 long numArgs = tuple.getCount();
2832 Object[] values = popArguments(numArgs - 1);
2833 String format = jrt.toAwkString(pop());
2834 jrt.printfDefault(format, values);
2835 }
2836
2837 private void execPrintfToFile(CountAndAppendTuple tuple) throws IOException {
2838 String key = jrt.toAwkString(pop());
2839 long numArgs = tuple.getCount();
2840 Object[] values = popArguments(numArgs - 1);
2841 String format = jrt.toAwkString(pop());
2842 jrt.printfToFile(key, tuple.isAppend(), format, values);
2843 }
2844
2845 private void execPrintfToPipe(CountTuple tuple) throws IOException {
2846 String cmd = jrt.toAwkString(pop());
2847 long numArgs = tuple.getCount();
2848 Object[] values = popArguments(numArgs - 1);
2849 String format = jrt.toAwkString(pop());
2850 jrt.printfToProcess(cmd, format, values);
2851 }
2852
2853 private void execLength(CountTuple tuple) {
2854 long num = tuple.getCount();
2855 if (num == 0) {
2856 push(jrt.jrtGetInputField(0).toString().length());
2857 return;
2858 }
2859 Object value = pop();
2860 if (value instanceof ArgumentReference) {
2861 value = resolveLengthArgumentReference((ArgumentReference) value);
2862 }
2863 push(lengthOf(value));
2864 }
2865
2866 private Object lengthOf(Object value) {
2867 return value instanceof Map ?
2868 Long.valueOf(((Map<?, ?>) value).size()) : Integer.valueOf(jrt.toAwkString(value).length());
2869 }
2870
2871 private String normalizeIndirectFunctionName(String functionName) {
2872 return functionName.startsWith("awk::") ? functionName.substring("awk::".length()) : functionName;
2873 }
2874
2875
2876
2877
2878
2879 private void resetCallState() {
2880 runtimeStack.popAllFrames();
2881 elementArgumentReferences.clear();
2882 operandStack.clear();
2883 }
2884
2885
2886
2887
2888
2889
2890
2891 private void adoptElementArgumentReferences(Object[] actualArguments) {
2892 for (Object argument : actualArguments) {
2893 adoptElementArgumentReference(argument);
2894 }
2895 }
2896
2897 private void adoptElementArgumentReference(Object argument) {
2898 if (argument instanceof IndirectArrayArgumentReference) {
2899 IndirectArrayArgumentReference reference = (IndirectArrayArgumentReference) argument;
2900 if (reference.ownerFrame < 0) {
2901 reference.ownerFrame = runtimeStack.frameCount();
2902 elementArgumentReferences.push(reference);
2903 }
2904 }
2905 }
2906
2907
2908
2909
2910
2911
2912 private void releaseElementArgumentReferences() {
2913 int depth = runtimeStack.frameCount();
2914 while (!elementArgumentReferences.isEmpty()
2915 && elementArgumentReferences.peek().ownerFrame == depth) {
2916 elementArgumentReferences.pop();
2917 }
2918 }
2919
2920 private Object resolveUserFunctionArgument(Object argument) {
2921 if (!(argument instanceof ArgumentReference)) {
2922 return argument;
2923 }
2924 ArgumentReference reference = (ArgumentReference) argument;
2925 Object snapshot = reference.snapshot();
2926 if (snapshot instanceof ArgumentReference) {
2927 return resolveUserFunctionArgument(snapshot);
2928 }
2929 return isUntyped(snapshot) ? reference : snapshot;
2930 }
2931
2932 private void resolveIndirectArguments(
2933 Object[] actualArgumentsParam,
2934 BuiltinFunction builtin) {
2935 for (int index = 0; index < actualArgumentsParam.length; index++) {
2936 boolean arrayArgument = builtin == BuiltinFunction.SPLIT && index == 1;
2937 actualArgumentsParam[index] = resolveIndirectArgument(
2938 actualArgumentsParam[index],
2939 arrayArgument,
2940 false);
2941 }
2942 }
2943
2944 private void resolveIndirectArguments(
2945 Object[] actualArgumentsParam,
2946 ExtensionFunction function) {
2947 boolean[] arrayArguments = new boolean[actualArgumentsParam.length];
2948 boolean[] rawValueArguments = new boolean[actualArgumentsParam.length];
2949 for (int index : function.collectAssocArrayIndexes(actualArgumentsParam.length)) {
2950 arrayArguments[index] = true;
2951 }
2952 for (int index : function.collectRawValueIndexes(actualArgumentsParam.length)) {
2953 rawValueArguments[index] = true;
2954 }
2955 for (int index = 0; index < actualArgumentsParam.length; index++) {
2956 actualArgumentsParam[index] = resolveIndirectArgument(
2957 actualArgumentsParam[index],
2958 arrayArguments[index],
2959 rawValueArguments[index]);
2960 }
2961 }
2962
2963 private Object resolveIndirectArgument(
2964 Object argument,
2965 boolean arrayArgument,
2966 boolean rawValueArgument) {
2967 if (!(argument instanceof ArgumentReference)) {
2968 return argument;
2969 }
2970 ArgumentReference reference = (ArgumentReference) argument;
2971 if (rawValueArgument) {
2972 return resolveRawArgumentReference(reference);
2973 }
2974 return resolveArgumentReference(reference, arrayArgument);
2975 }
2976
2977 private Object invokeIndirectBuiltin(
2978 BuiltinFunction builtin,
2979 Object[] args,
2980 int lineNumber) {
2981 switch (builtin) {
2982 case ATAN2:
2983 requireIndirectArgumentCount(builtin, args, 2, 2, lineNumber);
2984 return Math.atan2(JRT.toDouble(args[0]), JRT.toDouble(args[1]));
2985 case CLOSE:
2986 requireIndirectArgumentCount(builtin, args, 1, 1, lineNumber);
2987 return jrt.jrtClose(jrt.toAwkString(args[0]));
2988 case COS:
2989 requireIndirectArgumentCount(builtin, args, 1, 1, lineNumber);
2990 return Math.cos(JRT.toDouble(args[0]));
2991 case EXP:
2992 requireIndirectArgumentCount(builtin, args, 1, 1, lineNumber);
2993 return Math.exp(JRT.toDouble(args[0]));
2994 case GSUB:
2995 case SUB:
2996 requireIndirectArgumentCount(builtin, args, 2, 2, lineNumber);
2997 return substituteInputLine(
2998 builtin == BuiltinFunction.GSUB,
2999 args[0],
3000 args[1]);
3001 case INDEX:
3002 requireIndirectArgumentCount(builtin, args, 2, 2, lineNumber);
3003 return jrt.index(jrt.toAwkString(args[0]), jrt.toAwkString(args[1]));
3004 case INT:
3005 requireIndirectArgumentCount(builtin, args, 1, 1, lineNumber);
3006 return Long.valueOf((long) JRT.toDouble(args[0]));
3007 case LENGTH:
3008 requireIndirectArgumentCount(builtin, args, 0, 1, lineNumber);
3009 return args.length == 0 ? Integer.valueOf(jrt.jrtGetInputField(0).toString().length()) : lengthOf(args[0]);
3010 case LOG:
3011 requireIndirectArgumentCount(builtin, args, 1, 1, lineNumber);
3012 return Math.log(JRT.toDouble(args[0]));
3013 case MATCH:
3014 requireIndirectArgumentCount(builtin, args, 2, 2, lineNumber);
3015 return jrt.matchPosition(jrt.toAwkString(args[0]), jrt.toAwkString(args[1]));
3016 case RAND:
3017 requireIndirectArgumentCount(builtin, args, 0, 0, lineNumber);
3018 return randomNumberGenerator.nextDouble();
3019 case SIN:
3020 requireIndirectArgumentCount(builtin, args, 1, 1, lineNumber);
3021 return Math.sin(JRT.toDouble(args[0]));
3022 case SPLIT:
3023 requireIndirectArgumentCount(builtin, args, 2, 3, lineNumber);
3024 return splitIntoArray(
3025 args[0],
3026 args[1],
3027 args.length == 3 ? args[2] : jrt.getFSVar(),
3028 lineNumber);
3029 case SPRINTF:
3030 requireIndirectArgumentCount(builtin, args, 1, Integer.MAX_VALUE, lineNumber);
3031 return jrt
3032 .getAwkSink()
3033 .sprintf(
3034 jrt.toAwkString(args[0]),
3035 Arrays.copyOfRange(args, 1, args.length));
3036 case SQRT:
3037 requireIndirectArgumentCount(builtin, args, 1, 1, lineNumber);
3038 return Math.sqrt(JRT.toDouble(args[0]));
3039 case SRAND:
3040 requireIndirectArgumentCount(builtin, args, 0, 1, lineNumber);
3041 int seed = args.length == 0 ? JRT.timeSeed() : (int) JRT.toDouble(args[0]);
3042 int previousSeed = randomNumberGenerator.getSeed();
3043 randomNumberGenerator.setSeed(seed);
3044 return Integer.valueOf(previousSeed);
3045 case SUBSTR:
3046 requireIndirectArgumentCount(builtin, args, 2, 3, lineNumber);
3047 return substring(
3048 args[0],
3049 args[1],
3050 args.length == 3 ? args[2] : null);
3051 case SYSTEM:
3052 requireIndirectArgumentCount(builtin, args, 1, 1, lineNumber);
3053 return jrt.jrtSystem(jrt.toAwkString(args[0]));
3054 case TOLOWER:
3055 requireIndirectArgumentCount(builtin, args, 1, 1, lineNumber);
3056 return jrt.toAwkString(args[0]).toLowerCase();
3057 case TOUPPER:
3058 requireIndirectArgumentCount(builtin, args, 1, 1, lineNumber);
3059 return jrt.toAwkString(args[0]).toUpperCase();
3060 default:
3061 throw new AwkRuntimeException(
3062 lineNumber,
3063 "indirect calls are not implemented for builtin `" + builtin.getAwkName() + "'");
3064 }
3065 }
3066
3067 private void requireIndirectArgumentCount(
3068 BuiltinFunction builtin,
3069 Object[] args,
3070 int minimum,
3071 int maximum,
3072 int lineNumber) {
3073 if (args.length < minimum || args.length > maximum) {
3074 String expected = minimum == maximum ? Integer.toString(minimum) : minimum + " to " + maximum;
3075 throw new AwkRuntimeException(
3076 lineNumber,
3077 builtin.getAwkName() + " requires " + expected + " argument(s), not " + args.length);
3078 }
3079 }
3080
3081 private Object invokeExtension(
3082 ExtensionFunction function,
3083 Object[] args,
3084 int lineNumber,
3085 boolean blockResult) {
3086
3087 currentLineNumber = lineNumber;
3088 String extensionClassName = function.getExtensionClassName();
3089 JawkExtension extension = extensionInstances.get(extensionClassName);
3090 if (extension == null) {
3091 throw new AwkRuntimeException(
3092 lineNumber,
3093 "Extension instance for class '" + extensionClassName + "' is not registered");
3094 }
3095 if (!(extension instanceof AbstractExtension)) {
3096 throw new AwkRuntimeException(
3097 lineNumber,
3098 "Extension instance for class '" + extensionClassName
3099 + "' does not extend "
3100 + AbstractExtension.class.getName());
3101 }
3102 Map<IndirectArrayArgumentReference, Object> attachedValues = captureAttachedArrayArgumentValues();
3103 Object result;
3104 try {
3105 result = function.invoke((AbstractExtension) extension, args);
3106 } finally {
3107 detachReplacedArrayArgumentReferences(attachedValues);
3108 }
3109 if (blockResult && result instanceof BlockObject) {
3110 result = new BlockManager().block((BlockObject) result);
3111 }
3112 if (result == null) {
3113 return "";
3114 }
3115 if (result instanceof Number
3116 || result instanceof String
3117 || result instanceof Map
3118 || result instanceof BlockObject) {
3119 return result;
3120 }
3121 return jrt.toAwkString(result);
3122 }
3123
3124 private void execMatch() {
3125 String ere = jrt.toAwkString(pop());
3126 String s = jrt.toAwkString(pop());
3127 push(jrt.matchPosition(s, ere));
3128 }
3129
3130 private void execSubForDollar0(BooleanTuple tuple) {
3131 Object replacement = pop();
3132 Object ere = pop();
3133 push(substituteInputLine(tuple.getValue(), ere, replacement));
3134 }
3135
3136 private Object substituteInputLine(boolean global, Object ere, Object replacement) {
3137 String orig = jrt.toAwkString(jrt.jrtGetInputField(0));
3138 Object replacements = global ?
3139 jrt.replaceAll(orig, jrt.toAwkString(replacement), jrt.toAwkString(ere)) :
3140 jrt.replaceFirst(orig, jrt.toAwkString(replacement), jrt.toAwkString(ere));
3141 jrt.setInputLine(jrt.getReplaceResult());
3142 jrt.jrtParseFields();
3143 return replacements;
3144 }
3145
3146 private void execSubForDollarReference(BooleanTuple tuple) {
3147 boolean isGsub = tuple.getValue();
3148 long fieldNum = JRT.parseFieldNumber(pop());
3149 String orig = jrt.toAwkString(pop());
3150 String repl = jrt.toAwkString(pop());
3151 String ere = jrt.toAwkString(pop());
3152 push(isGsub ? jrt.replaceAll(orig, repl, ere) : jrt.replaceFirst(orig, repl, ere));
3153 String newstring = jrt.getReplaceResult();
3154 if (fieldNum == 0) {
3155 jrt.setInputLine(newstring);
3156 jrt.jrtParseFields();
3157 } else {
3158 jrt.jrtSetInputField(newstring, fieldNum);
3159 }
3160 }
3161
3162 private void execSubForVariable(SubstitutionVariableTuple tuple, PositionTracker position) {
3163 String newString = execSubOrGSub(tuple.isGlobalSubstitution());
3164 assign(tuple.getVariableOffset(), newString, tuple.isGlobal(), position, false);
3165 }
3166
3167 private void execSubForArrayReference(SubstitutionVariableTuple tuple) {
3168 Object arrIdx = pop();
3169 String newString = execSubOrGSub(tuple.isGlobalSubstitution());
3170 assignArray(tuple.getVariableOffset(), arrIdx, newString, tuple.isGlobal());
3171 pop();
3172 }
3173
3174 private void execSubForMapReference(BooleanTuple tuple) {
3175 Object arrIdx = pop();
3176 Map<Object, Object> array = toMap(pop());
3177 String newString = execSubOrGSub(tuple.getValue());
3178 assignMapElement(array, arrIdx, newString);
3179 pop();
3180 }
3181
3182 private void execSplit(CountTuple tuple, PositionTracker position) {
3183 long numArgs = tuple.getCount();
3184 Object fs;
3185 if (numArgs == 2) {
3186 fs = jrt.getFSVar();
3187 } else if (numArgs == 3) {
3188
3189
3190 fs = pop();
3191 } else {
3192 throw new Error("Invalid # of args. split() requires 2 or 3. Got: " + numArgs);
3193 }
3194 Object target = pop();
3195 Object source = pop();
3196 push(splitIntoArray(source, target, fs, position.lineNumber()));
3197 }
3198
3199 private Object splitIntoArray(Object source, Object target, Object separator, int lineNumber) {
3200 if (!(target instanceof Map)) {
3201 throw new AwkRuntimeException(lineNumber, target + " is not an array.");
3202 }
3203 Enumeration<Object> tokenizer = jrt.splitTokenizer(jrt.toAwkString(source), separator);
3204 @SuppressWarnings("unchecked")
3205 Map<Object, Object> assocArray = (Map<Object, Object>) target;
3206 assocArray.clear();
3207 detachMissingArrayArgumentReferences(assocArray);
3208 long cnt = 0;
3209 while (tokenizer.hasMoreElements()) {
3210 Object value = tokenizer.nextElement();
3211 assocArray.put(++cnt, jrt.toInputScalar(value));
3212 }
3213 return Long.valueOf(cnt);
3214 }
3215
3216 private void execSubstr(CountTuple tuple) {
3217 long numArgs = tuple.getCount();
3218 Object length = null;
3219 if (numArgs == 3) {
3220 length = pop();
3221 } else if (numArgs != 2) {
3222 throw new Error("numArgs for SUBSTR must be 2 or 3. It is " + numArgs);
3223 }
3224 Object start = pop();
3225 Object value = pop();
3226 push(substring(value, start, length));
3227 }
3228
3229 private Object substring(Object value, Object start, Object requestedLength) {
3230 String s = jrt.toAwkString(value);
3231 int startPos = (int) JRT.toDouble(start);
3232 int length = requestedLength == null ?
3233 s.length() - startPos + 1 : (int) JRT.toLong(requestedLength);
3234 if (startPos <= 0) {
3235 startPos = 1;
3236 }
3237 if (length <= 0 || startPos > s.length()) {
3238 return BLANK;
3239 }
3240 return startPos + length > s.length() ?
3241 s.substring(startPos - 1) : s.substring(startPos - 1, startPos + length - 1);
3242 }
3243
3244 private void execSetNumGlobals(CountTuple tuple) {
3245 long numGlobals = tuple.getCount();
3246 Object[] globals = runtimeStack.getNumGlobals();
3247 if (mergedGlobalLayoutActive) {
3248 if (!hasCompatiblePersistentGlobalLayout(numGlobals)) {
3249 throw new IllegalStateException(
3250 "AVM globals are already initialized for an incompatible persistent layout.");
3251 }
3252 applyExecutionInitialVariablesToGlobalSlots(true);
3253 } else if (globals == null) {
3254 runtimeStack.setNumGlobals(numGlobals, globalVariableOffsets);
3255 initializedEvalGlobalVariableOffsets = globalVariableOffsets;
3256 initializedEvalGlobalVariableArrays = globalVariableArrays;
3257 applyExecutionInitialVariablesToGlobalSlots(false);
3258 } else if (!hasCompatibleEvalGlobalLayout(numGlobals)) {
3259 throw new IllegalStateException(
3260 "AVM globals are already initialized for a different eval layout. Call prepareForEval(...) first.");
3261 }
3262 }
3263
3264 private void populateEnviron(long offset) {
3265 environOffset = offset;
3266 for (Map.Entry<String, String> var : System.getenv().entrySet()) {
3267 assignArray(environOffset, var.getKey(), jrt.toInputScalar(var.getValue()), true);
3268 pop();
3269 }
3270 }
3271
3272 private void populateArgc(long offset) {
3273 argcOffset = offset;
3274
3275 runtimeStack.setVariable(argcOffset, Integer.valueOf(arguments.size() + 1), true);
3276 }
3277
3278 private void populateArgv(long offset) {
3279 argvOffset = offset;
3280
3281
3282 Object existing = runtimeStack.getVariable(argvOffset, true);
3283 if (existing instanceof Map && !((Map<?, ?>) existing).isEmpty()) {
3284 return;
3285 }
3286 forEachArgvEntry((index, value) -> {
3287 assignArray(argvOffset, index, value, true);
3288 pop();
3289 });
3290 }
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301 private void forEachArgvEntry(BiConsumer<Long, Object> consumer) {
3302 consumer.accept(Long.valueOf(0L), "jawk");
3303 for (int i = 1; i <= arguments.size(); i++) {
3304 consumer.accept(Long.valueOf(i), jrt.toInputScalar(arguments.get(i - 1)));
3305 }
3306 }
3307
3308
3309
3310
3311
3312
3313 private void runBeforeStartHooks() {
3314 if (beforeStartHooksExecuted || extensionInstances.isEmpty()) {
3315 return;
3316 }
3317 beforeStartHooksExecuted = true;
3318 Set<JawkExtension> started = new LinkedHashSet<JawkExtension>();
3319 for (JawkExtension extension : extensionInstances.values()) {
3320 if (started.add(extension)) {
3321 extension.beforeStart(this, jrt);
3322 }
3323 }
3324 }
3325
3326
3327
3328
3329
3330
3331
3332
3333 private void execUpdateSymtab(long offset) {
3334 symtabOffset = offset;
3335 if (runtimeStack.getVariable(offset, true) != null) {
3336
3337 return;
3338 }
3339 SymtabArray symtab = new SymtabArray();
3340 for (String name : executionInitialVariables.keySet()) {
3341 symtab.put(name, getVariable(name));
3342 }
3343 for (String name : getGlobalVariableNames()) {
3344
3345 if ("SYMTAB".equals(name) || "FUNCTAB".equals(name)) {
3346 continue;
3347 }
3348 symtab.put(name, getVariable(name));
3349 }
3350
3351
3352 for (String name : getSpecialVariableNames()) {
3353 symtab.put(name, getVariable(name));
3354 }
3355 symtab.activate();
3356 runtimeStack.setVariable(offset, symtab, true);
3357 }
3358
3359 private final class SymtabArray extends java.util.AbstractMap<Object, Object> implements AssocArray {
3360 private final Map<Object, Object> entries = newAwkArray();
3361 private final Set<String> assignableNames = new HashSet<String>();
3362 private boolean active;
3363
3364 private void activate() {
3365 active = true;
3366 }
3367
3368
3369 @Override
3370 public Object get(Object key) {
3371 String name = key == null ? "" : key.toString();
3372 if (active && !isMetaTableName(name) && isLiveSpecialVariable(name)) {
3373 return getVariable(name);
3374 }
3375 Integer offset = globalVariableOffsets == null ? null : globalVariableOffsets.get(name);
3376 if (active && !isMetaTableName(name) && offset != null) {
3377 return runtimeStack.getVariable(offset.intValue(), true);
3378 }
3379 return entries.get(key);
3380 }
3381
3382
3383 @Override
3384 public boolean containsKey(Object key) {
3385 String name = key == null ? "" : key.toString();
3386 return active
3387 && !isMetaTableName(name)
3388 && (isLiveSpecialVariable(name)
3389 || (globalVariableOffsets != null && globalVariableOffsets.containsKey(name)))
3390 || entries.containsKey(key);
3391 }
3392
3393
3394 @Override
3395 public Object put(Object key, Object value) {
3396 String name = key == null ? "" : key.toString();
3397 if (!active) {
3398 if (key != null) {
3399 assignableNames.add(name);
3400 }
3401 return entries.put(key, value);
3402 }
3403 if (!assignableNames.contains(name)) {
3404 throw new AwkRuntimeException("Cannot assign to an arbitrary element of SYMTAB.");
3405 }
3406 validateGlobalType(name, value);
3407 Object previous = entries.put(key, value);
3408 if (isMetaTableName(name)) {
3409 return previous;
3410 }
3411 if (applyLiveSpecialVariable(name, value)) {
3412 return previous;
3413 }
3414 Integer offset = globalVariableOffsets == null ? null : globalVariableOffsets.get(name);
3415 if (offset != null) {
3416 runtimeStack.setVariable(offset.intValue(), value, true);
3417 }
3418 return previous;
3419 }
3420
3421 private Object putRuntimeVariable(String name, Object value) {
3422 assignableNames.add(name);
3423 return put(name, value);
3424 }
3425
3426 private boolean applyLiveSpecialVariable(String name, Object value) {
3427 if ("ARGC".equals(name)) {
3428 if (argcOffset == NULL_OFFSET) {
3429 throw new AwkRuntimeException("ARGC is read-only (not materialized).");
3430 }
3431 runtimeStack.setVariable(argcOffset, value, true);
3432 return true;
3433 }
3434 if ("RSTART".equals(name)) {
3435 jrt.setRSTART(value);
3436 return true;
3437 }
3438 if ("RLENGTH".equals(name)) {
3439 jrt.setRLENGTH(value);
3440 return true;
3441 }
3442 return isManagedSpecialVariable(name) && jrt.applySpecialVariable(name, value);
3443 }
3444
3445 private boolean isLiveSpecialVariable(String name) {
3446 return isManagedSpecialVariable(name)
3447 || "RSTART".equals(name)
3448 || "RLENGTH".equals(name);
3449 }
3450
3451
3452 @Override
3453 public Object remove(Object key) {
3454 if (active) {
3455 throw new AwkRuntimeException("Cannot delete an element from SYMTAB.");
3456 }
3457 return entries.remove(key);
3458 }
3459
3460
3461 @Override
3462 public void clear() {
3463 if (active) {
3464 throw new AwkRuntimeException("Cannot delete SYMTAB.");
3465 }
3466 entries.clear();
3467 }
3468
3469 private void validateGlobalType(String name, Object value) {
3470 Boolean array = globalVariableArrays == null ? null : globalVariableArrays.get(name);
3471 if (Boolean.TRUE.equals(array) && !(value instanceof Map)) {
3472 throw new AwkRuntimeException(
3473 "Attempting to use array `" + name + "' in a scalar context.");
3474 }
3475 if (Boolean.FALSE.equals(array) && value instanceof Map) {
3476 throw new AwkRuntimeException(
3477 "Attempting to use scalar `" + name + "' as an array.");
3478 }
3479 }
3480
3481
3482 @Override
3483 public Set<Map.Entry<Object, Object>> entrySet() {
3484 return new AbstractSet<Map.Entry<Object, Object>>() {
3485
3486 @Override
3487 public Iterator<Map.Entry<Object, Object>> iterator() {
3488 final Iterator<Map.Entry<Object, Object>> iterator = entries.entrySet().iterator();
3489 return new Iterator<Map.Entry<Object, Object>>() {
3490
3491 @Override
3492 public boolean hasNext() {
3493 return iterator.hasNext();
3494 }
3495
3496 @Override
3497 public Map.Entry<Object, Object> next() {
3498 return new LiveSymtabEntry(iterator.next().getKey());
3499 }
3500
3501 @Override
3502 public void remove() {
3503 if (active) {
3504 throw new AwkRuntimeException("Cannot delete an element from SYMTAB.");
3505 }
3506 iterator.remove();
3507 }
3508 };
3509 }
3510
3511 @Override
3512 public int size() {
3513 return entries.size();
3514 }
3515 };
3516 }
3517
3518 private boolean isMetaTableName(String name) {
3519 return "SYMTAB".equals(name) || "FUNCTAB".equals(name);
3520 }
3521
3522 private final class LiveSymtabEntry implements Map.Entry<Object, Object> {
3523
3524 private final Object key;
3525
3526 private LiveSymtabEntry(Object key) {
3527 this.key = key;
3528 }
3529
3530 @Override
3531 public Object getKey() {
3532 return key;
3533 }
3534
3535 @Override
3536 public Object getValue() {
3537 return SymtabArray.this.get(key);
3538 }
3539
3540 @Override
3541 public Object setValue(Object value) {
3542 return SymtabArray.this.put(key, value);
3543 }
3544
3545 @Override
3546 public boolean equals(Object obj) {
3547 if (!(obj instanceof Map.Entry)) {
3548 return false;
3549 }
3550 Map.Entry<?, ?> other = (Map.Entry<?, ?>) obj;
3551 return Objects.equals(key, other.getKey()) && Objects.equals(getValue(), other.getValue());
3552 }
3553
3554 @Override
3555 public int hashCode() {
3556 return Objects.hashCode(key) ^ Objects.hashCode(getValue());
3557 }
3558 }
3559 }
3560
3561
3562
3563
3564
3565
3566
3567 private void execUpdateFunctab(long offset) {
3568 if (runtimeStack.getVariable(offset, true) != null) {
3569 return;
3570 }
3571 Map<Object, Object> functab = newAwkArray();
3572 for (String name : BuiltinFunction.names()) {
3573 functab.put(name, name);
3574 }
3575 for (String name : functionNames) {
3576 functab.put(name, name);
3577 }
3578 Set<JawkExtension> seen = new LinkedHashSet<JawkExtension>();
3579 for (JawkExtension extension : extensionInstances.values()) {
3580 if (seen.add(extension)) {
3581 for (String keyword : extension.getExtensionFunctions().keySet()) {
3582 functab.put(keyword, keyword);
3583 }
3584 }
3585 }
3586 runtimeStack.setVariable(offset, new ReadOnlyArray("FUNCTAB", functab), true);
3587 }
3588
3589 private static final class ReadOnlyArray extends java.util.AbstractMap<Object, Object> implements AssocArray {
3590 private final String name;
3591 private final Map<Object, Object> entries;
3592
3593 private ReadOnlyArray(String nameParam, Map<Object, Object> entriesParam) {
3594 name = nameParam;
3595 entries = entriesParam;
3596 }
3597
3598
3599 @Override
3600 public Object get(Object key) {
3601 return JRT.containsAwkKey(entries, key) ? entries.get(key) : BLANK;
3602 }
3603
3604
3605 @Override
3606 public boolean containsKey(Object key) {
3607 return JRT.containsAwkKey(entries, key);
3608 }
3609
3610
3611 @Override
3612 public Set<Map.Entry<Object, Object>> entrySet() {
3613 return Collections.unmodifiableMap(entries).entrySet();
3614 }
3615
3616
3617 @Override
3618 public Object put(Object key, Object value) {
3619 throw readOnlyError();
3620 }
3621
3622
3623 @Override
3624 public Object remove(Object key) {
3625 throw readOnlyError();
3626 }
3627
3628
3629 @Override
3630 public void clear() {
3631 throw readOnlyError();
3632 }
3633
3634 private AwkRuntimeException readOnlyError() {
3635 return new AwkRuntimeException(name + " is read-only.");
3636 }
3637 }
3638
3639
3640 private void updateSymtabEntry(String name, Object value) {
3641 if (symtabOffset == NULL_OFFSET) {
3642 return;
3643 }
3644 Object symtab = runtimeStack.getVariable(symtabOffset, true);
3645 if (symtab instanceof SymtabArray) {
3646 ((SymtabArray) symtab).putRuntimeVariable(name, value);
3647 } else if (symtab instanceof Map) {
3648 @SuppressWarnings("unchecked")
3649 Map<Object, Object> symtabMap = (Map<Object, Object>) symtab;
3650 symtabMap.put(name, value);
3651 }
3652 }
3653
3654 private void execApplySubsep(CountTuple tuple) {
3655 long count = tuple.getCount();
3656 if (count == 1) {
3657 Object value = pop();
3658 checkScalar(value);
3659 push(jrt.toAwkString(value));
3660 return;
3661 }
3662 StringBuilder sb = new StringBuilder();
3663 Object value = pop();
3664 checkScalar(value);
3665 sb.append(jrt.toAwkString(value));
3666 String subsep = jrt.toAwkString(jrt.getSUBSEPVar());
3667 for (int i = 1; i < count; i++) {
3668 sb.insert(0, subsep);
3669 value = pop();
3670 checkScalar(value);
3671 sb.insert(0, jrt.toAwkString(value));
3672 }
3673 push(sb.toString());
3674 }
3675
3676 private long beforeProfiledTuple(Tuple tuple, Opcode opcode) {
3677 long now = System.nanoTime();
3678 if (opcode == Opcode.CALL_FUNCTION) {
3679 CallFunctionTuple callTuple = (CallFunctionTuple) tuple;
3680 activeProfilingFunctions.push(new ActiveFunction(callTuple.getFunctionName(), now));
3681 } else if (opcode == Opcode.EXTENSION) {
3682 ExtensionTuple extensionTuple = (ExtensionTuple) tuple;
3683 ExtensionFunction function = extensionTuple.getFunction();
3684 activeProfilingFunctions.push(new ActiveFunction(function.getKeyword(), now));
3685 }
3686 return now;
3687 }
3688
3689 private void afterProfiledTuple(Opcode opcode, long tupleStartNanos) {
3690 long now = System.nanoTime();
3691 statisticsFor(tupleProfilingStats, opcode).add(now - tupleStartNanos);
3692 if (opcode == Opcode.EXIT_WITH_CODE || opcode == Opcode.EXIT_WITHOUT_CODE) {
3693 recordAllFunctionExits(now);
3694 } else if (opcode == Opcode.EXTENSION || opcode == Opcode.RETURN_FROM_FUNCTION) {
3695 recordFunctionExit(now);
3696 }
3697 }
3698
3699 private static <K> ProfilingReport.Accumulator statisticsFor(
3700 Map<K, ProfilingReport.Accumulator> stats,
3701 K key) {
3702 ProfilingReport.Accumulator accumulator = stats.get(key);
3703 if (accumulator == null) {
3704 accumulator = new ProfilingReport.Accumulator();
3705 stats.put(key, accumulator);
3706 }
3707 return accumulator;
3708 }
3709
3710 private void recordFunctionExit(long now) {
3711 if (activeProfilingFunctions.isEmpty()) {
3712 return;
3713 }
3714 ActiveFunction function = activeProfilingFunctions.pop();
3715 statisticsFor(functionProfilingStats, function.name).add(now - function.startNanos);
3716 }
3717
3718 private void recordAllFunctionExits(long now) {
3719 while (!activeProfilingFunctions.isEmpty()) {
3720 recordFunctionExit(now);
3721 }
3722 }
3723
3724 private static final class ActiveFunction {
3725 private final String name;
3726 private final long startNanos;
3727
3728 private ActiveFunction(String name, long startNanos) {
3729 this.name = name;
3730 this.startNanos = startNanos;
3731 }
3732 }
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745 @Override
3746 public void close() throws IOException {
3747 jrt.jrtCloseAll();
3748 closeResolvedInputSource();
3749 resolvedInputSource = null;
3750 }
3751
3752
3753
3754
3755
3756
3757
3758 private void closeResolvedInputSource() {
3759 closeInputSource(resolvedInputSource);
3760 }
3761
3762 private void closeInputSource(InputSource inputSource) {
3763 if (!(inputSource instanceof Closeable)) {
3764 return;
3765 }
3766 try {
3767 ((Closeable) inputSource).close();
3768 } catch (IOException ignored) {
3769
3770 }
3771 }
3772
3773 private Object[] popArguments(long numArgs) {
3774 Object[] args = new Object[(int) numArgs];
3775 for (int i = (int) numArgs - 1; i >= 0; i--) {
3776 args[i] = pop();
3777 }
3778 return args;
3779 }
3780
3781
3782
3783
3784 private String sprintfFunction(long numArgs) {
3785 Object[] argArray = popArguments(numArgs - 1);
3786 String fmt = jrt.toAwkString(pop());
3787 return jrt.getAwkSink().sprintf(fmt, argArray);
3788 }
3789
3790 private void setNumOnJRT(long fieldNum, double num) {
3791 String numString = jrt.toAwkString(Double.valueOf(num));
3792
3793
3794 if (fieldNum == 0) {
3795 jrt.setInputLine(numString);
3796 jrt.jrtParseFields();
3797 } else {
3798 jrt.jrtSetInputField(numString, fieldNum);
3799 }
3800 }
3801
3802 private String execSubOrGSub(boolean isGsub) {
3803 String newString;
3804
3805
3806
3807
3808 String orig = jrt.toAwkString(pop());
3809 String repl = jrt.toAwkString(pop());
3810 String ere = jrt.toAwkString(pop());
3811 push(isGsub ? jrt.replaceAll(orig, repl, ere) : jrt.replaceFirst(orig, repl, ere));
3812 newString = jrt.getReplaceResult();
3813
3814 return newString;
3815 }
3816
3817
3818
3819
3820 private void assign(long l, Object value, boolean isGlobal, PositionTracker position, boolean push) {
3821 value = JRT.untypedToBlank(value);
3822 checkScalar(value);
3823
3824 if (resolveVariable(l, isGlobal, false) instanceof Map) {
3825 throw new AwkRuntimeException(position.lineNumber(), "Attempting to use an array in a scalar context.");
3826 }
3827 if (push) {
3828 push(value);
3829 }
3830 runtimeStack.setVariable(l, value, isGlobal);
3831
3832 }
3833
3834
3835
3836
3837 private void assignArray(long offset, Object arrIdx, Object rhs, boolean isGlobal) {
3838 assignMapElement(ensureMapVariable(offset, isGlobal), arrIdx, rhs);
3839 }
3840
3841 private void assignMapElement(Map<Object, Object> array, Object arrIdx, Object rhs) {
3842 checkScalar(arrIdx);
3843 rhs = JRT.untypedToBlank(rhs);
3844 checkScalar(rhs);
3845 if (JRT.containsAwkKey(array, arrIdx)
3846 && JRT.getAssocArrayValue(array, arrIdx) instanceof Map) {
3847 throw new AwkRuntimeException("Attempting to use an array in a scalar context.");
3848 }
3849 array.put(arrIdx, rhs);
3850 push(rhs);
3851 }
3852
3853
3854
3855
3856
3857 private Object inc(long l, boolean isGlobal) {
3858 Object o = resolveVariable(l, isGlobal, false);
3859 if (o instanceof UninitializedObject) {
3860 o = ZERO;
3861 runtimeStack.setVariable(l, o, isGlobal);
3862 }
3863 Object updated = JRT.inc(o);
3864 runtimeStack.setVariable(l, updated, isGlobal);
3865 return o;
3866 }
3867
3868
3869
3870
3871
3872 private Object dec(long l, boolean isGlobal) {
3873 Object o = resolveVariable(l, isGlobal, false);
3874 if (o instanceof UninitializedObject) {
3875 o = ZERO;
3876 runtimeStack.setVariable(l, o, isGlobal);
3877 }
3878 Object updated = JRT.dec(o);
3879 runtimeStack.setVariable(l, updated, isGlobal);
3880 return o;
3881 }
3882
3883
3884 @Override
3885 public final Object getRS() {
3886 return jrt.getRSVar();
3887 }
3888
3889
3890 @Override
3891 public final Object getOFS() {
3892 return jrt.getOFSVar();
3893 }
3894
3895
3896 @Override
3897 public final Object getORS() {
3898 return jrt.getORSVar();
3899 }
3900
3901
3902 @Override
3903 public final Object getSUBSEP() {
3904 return jrt.getSUBSEPVar();
3905 }
3906
3907
3908
3909
3910
3911
3912
3913
3914 public Set<String> getGlobalVariableNames() {
3915 return globalVariableOffsets == null ?
3916 Collections.<String>emptySet() : Collections.unmodifiableSet(globalVariableOffsets.keySet());
3917 }
3918
3919
3920
3921
3922
3923
3924
3925 public Set<String> getFunctionNames() {
3926 return functionNames == null ? Collections.<String>emptySet() : Collections.unmodifiableSet(functionNames);
3927 }
3928
3929
3930
3931
3932
3933
3934 private static final Set<String> SPECIAL_VARIABLE_NAMES = Collections
3935 .unmodifiableSet(
3936 new LinkedHashSet<String>(
3937 Arrays
3938 .asList(
3939 "FS",
3940 "RS",
3941 "OFS",
3942 "ORS",
3943 "FILENAME",
3944 "SUBSEP",
3945 "CONVFMT",
3946 "OFMT",
3947 "NF",
3948 "NR",
3949 "FNR",
3950 "RSTART",
3951 "RLENGTH",
3952 "IGNORECASE",
3953 "ERRNO",
3954 "ARGIND",
3955 "ARGC",
3956 "ARGV")));
3957
3958
3959
3960
3961
3962
3963
3964 public Set<String> getSpecialVariableNames() {
3965 return SPECIAL_VARIABLE_NAMES;
3966 }
3967
3968
3969 @Override
3970 public final Object getVariable(String name) {
3971 if (name == null) {
3972 return null;
3973 }
3974 switch (name) {
3975 case "FS":
3976 return getFS();
3977 case "RS":
3978 return getRS();
3979 case "OFS":
3980 return getOFS();
3981 case "ORS":
3982 return getORS();
3983 case "FILENAME":
3984 return jrt.getFILENAME();
3985 case "SUBSEP":
3986 return getSUBSEP();
3987 case "CONVFMT":
3988 return getCONVFMT();
3989 case "OFMT":
3990 return jrt.getOFMTString();
3991 case "NF":
3992 return jrt.getNF();
3993 case "NR":
3994 return jrt.getNR();
3995 case "FNR":
3996 return jrt.getFNR();
3997 case "RSTART":
3998 return jrt.getRSTART();
3999 case "RLENGTH":
4000 return jrt.getRLENGTH();
4001 case "IGNORECASE":
4002 return jrt.getIGNORECASEVar();
4003 case "ERRNO":
4004 if (isManagedSpecialVariable(name)) {
4005 return jrt.getERRNO();
4006 }
4007
4008 break;
4009 case "ARGIND":
4010 if (isManagedSpecialVariable(name)) {
4011 return jrt.getARGIND();
4012 }
4013
4014 break;
4015
4016 case "ARGC":
4017 return getARGC();
4018 case "ARGV":
4019 return getARGV();
4020 default:
4021 break;
4022 }
4023 if (globalVariableOffsets == null) {
4024 return executionInitialVariables.get(name);
4025 }
4026 Integer offsetObj = globalVariableOffsets.get(name);
4027 if (offsetObj != null) {
4028 return runtimeStack.getVariable(offsetObj.intValue(), true);
4029 }
4030
4031
4032
4033 return executionInitialVariables == null ? null : executionInitialVariables.get(name);
4034 }
4035
4036
4037
4038
4039
4040
4041
4042 public String getSourceDescription() {
4043 return sourceDescription;
4044 }
4045
4046
4047
4048
4049
4050
4051
4052 public int getCurrentLineNumber() {
4053 return currentLineNumber;
4054 }
4055
4056
4057 @Override
4058 public final void assignVariable(String name, Object obj) {
4059
4060
4061 if (globalVariableOffsets == null || globalVariableArrays == null) {
4062 Object normalized = normalizeExternalVariableValue(obj);
4063 baseInitialVariables.put(name, normalized);
4064 if (isManagedSpecialVariable(name)) {
4065 baseSpecialVariables.put(name, normalized);
4066 }
4067 return;
4068 }
4069
4070
4071 if (functionNames.contains(name)) {
4072 throw new IllegalArgumentException("Cannot assign a scalar to a function name (" + name + ").");
4073 }
4074
4075 Object normalized = normalizeExternalVariableValue(obj);
4076
4077
4078
4079
4080 if (!"ARGC".equals(name) && isManagedSpecialVariable(name)) {
4081 jrt.applySpecialVariable(name, normalized);
4082 updateSymtabEntry(name, normalized);
4083 return;
4084 }
4085
4086 Integer offsetObj = globalVariableOffsets.get(name);
4087 Boolean arrayObj = globalVariableArrays.get(name);
4088
4089 if (offsetObj != null) {
4090 if (arrayObj.booleanValue() && !(normalized instanceof Map)) {
4091 throw new IllegalArgumentException(
4092 "Cannot assign a scalar to a non-scalar variable (" + name + ").");
4093 }
4094 runtimeStack.setFilelistVariable(offsetObj.intValue(), normalized);
4095 } else if (runtimeStack.hasGlobalVariable(name)) {
4096 runtimeStack.setGlobalVariable(name, normalized);
4097 }
4098
4099 updateSymtabEntry(name, normalized);
4100 }
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118 private void executeNextfile(PositionTracker position) {
4119 if (nextFileAddress == null || !inputFileLoopStarted) {
4120 throw new AwkRuntimeException(
4121 position.lineNumber(),
4122 "`nextfile' cannot be called from a BEGIN rule");
4123 }
4124 if (withinEndBlocks) {
4125 throw new AwkRuntimeException(
4126 position.lineNumber(),
4127 "`nextfile' cannot be called from an END rule");
4128 }
4129 if (withinEndFileBlocks) {
4130 throw new AwkRuntimeException(
4131 position.lineNumber(),
4132 "`nextfile' cannot be called from an ENDFILE rule");
4133 }
4134
4135 runtimeStack.popAllFrames();
4136 operandStack.clear();
4137 if (endFileAddress == null
4138 || withinBeginFileBlocks && jrt.hasPendingInputFileError(resolvedInputSource)) {
4139
4140
4141
4142 withinBeginFileBlocks = false;
4143 position.jump(nextFileAddress);
4144 } else {
4145 withinBeginFileBlocks = false;
4146 withinEndFileBlocks = true;
4147 position.jump(endFileAddress);
4148 }
4149 }
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163 private boolean isMainInputFileBounded() {
4164 return endFileAddress != null && inputFileLoopStarted && !withinEndBlocks;
4165 }
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181 private void checkGetlineAllowed(PositionTracker position) {
4182 if (withinBeginFileBlocks || withinEndFileBlocks) {
4183 throw new AwkRuntimeException(
4184 position.lineNumber(),
4185 "non-redirected `getline' invalid inside `"
4186 + (withinBeginFileBlocks ? "BEGINFILE" : "ENDFILE")
4187 + "' rule");
4188 }
4189 }
4190
4191
4192 @Override
4193 public Object getFS() {
4194 return jrt.getFSVar();
4195 }
4196
4197
4198 @Override
4199 public Object getCONVFMT() {
4200 return jrt.getCONVFMTString();
4201 }
4202
4203
4204 @Override
4205 public void resetFNR() {
4206 jrt.setFNR(0);
4207 }
4208
4209
4210 @Override
4211 public void incFNR() {
4212 long v = jrt.getFNR();
4213 jrt.setFNR(v + 1);
4214 }
4215
4216
4217 @Override
4218 public void incNR() {
4219 long v = jrt.getNR();
4220 jrt.setNR(v + 1);
4221 }
4222
4223
4224 @Override
4225 public void setNF(Integer newNf) {
4226 jrt.setNF(newNf);
4227 }
4228
4229
4230 @Override
4231 public void setFILENAME(String filename) {
4232 jrt.setFILENAMEViaJrt(jrt.toInputScalar(filename));
4233 }
4234
4235
4236 @Override
4237 public Object getARGV() {
4238 if (argvOffset == NULL_OFFSET) {
4239 Map<Object, Object> argv = newAwkArray();
4240 forEachArgvEntry(argv::put);
4241 return argv;
4242 }
4243 return runtimeStack.getVariable(argvOffset, true);
4244 }
4245
4246
4247 @Override
4248 public Object getARGC() {
4249 if (argcOffset == NULL_OFFSET) {
4250 return Long.valueOf(arguments.size() + 1);
4251 }
4252 return runtimeStack.getVariable(argcOffset, true);
4253 }
4254
4255 private String getOFMT() {
4256 return jrt.getOFMTString();
4257 }
4258
4259 private Map<Object, Object> newAwkArray() {
4260 return JRT.createAwkMap(sortedArrayKeys);
4261 }
4262
4263 private Map<Object, Object> ensureMapVariable(long offset, boolean isGlobal) {
4264 return toMap(resolveVariable(offset, isGlobal, true));
4265 }
4266
4267 private Map<Object, Object> getMapVariable(long offset, boolean isGlobal) {
4268 return toMap(resolveVariable(offset, isGlobal, true));
4269 }
4270
4271 private Object resolveVariable(long offset, boolean isGlobal, boolean arrayContext) {
4272 Object value = runtimeStack.getVariable(offset, isGlobal);
4273
4274
4275 if (!isGlobal && value instanceof ArgumentReference) {
4276 value = resolveArgumentReference((ArgumentReference) value, arrayContext);
4277 runtimeStack.setVariable(offset, value, isGlobal);
4278 return value;
4279 }
4280 if (!isUntyped(value)) {
4281 return value;
4282 }
4283 value = arrayContext ? newAwkArray() : BLANK;
4284 runtimeStack.setVariable(offset, value, isGlobal);
4285 return value;
4286 }
4287
4288 private Object resolveRawArgumentReference(ArgumentReference reference) {
4289 Object currentValue = readCurrentArgumentValue(reference);
4290 if (currentValue instanceof Map
4291 || currentValue instanceof UninitializedObject
4292 && !(currentValue instanceof UntypedObject)) {
4293 return currentValue;
4294 }
4295 Object value = reference.snapshot();
4296 return value instanceof ArgumentReference ?
4297 resolveRawArgumentReference((ArgumentReference) value) : value;
4298 }
4299
4300 private Object readCurrentArgumentValue(ArgumentReference reference) {
4301 Object value = reference.currentValue();
4302 return value instanceof ArgumentReference ?
4303 readCurrentArgumentValue((ArgumentReference) value) : value;
4304 }
4305
4306 private Object resolveArgumentReference(ArgumentReference reference, boolean arrayContext) {
4307 if (!arrayContext) {
4308 checkScalar(readCurrentArgumentValue(reference));
4309 }
4310 Object value = arrayContext ? reference.currentValue() : reference.snapshot();
4311 if (value instanceof ArgumentReference) {
4312 value = resolveArgumentReference((ArgumentReference) value, arrayContext);
4313 if (arrayContext) {
4314 reference.setValue(value);
4315 } else {
4316 reference.setScalarValue(value);
4317 }
4318 return value;
4319 }
4320 if (!isUntyped(value)) {
4321 return value;
4322 }
4323 value = arrayContext ? newAwkArray() : BLANK;
4324 if (arrayContext) {
4325 reference.setValue(value);
4326 } else {
4327 reference.setScalarValue(value);
4328 }
4329 return value;
4330 }
4331
4332 private Object resolveLengthArgumentReference(ArgumentReference reference) {
4333 Object currentValue = readCurrentArgumentValue(reference);
4334 return currentValue instanceof Map ? currentValue : resolveArgumentReference(reference, false);
4335 }
4336
4337 private Map<IndirectArrayArgumentReference, Object> captureAttachedArrayArgumentValues() {
4338 if (elementArgumentReferences.isEmpty()) {
4339 return Collections.emptyMap();
4340 }
4341 Map<IndirectArrayArgumentReference, Object> values = new IdentityHashMap<IndirectArrayArgumentReference, Object>();
4342 for (IndirectArrayArgumentReference reference : elementArgumentReferences) {
4343 if (reference.isAttached()) {
4344 values.put(reference, reference.currentValue());
4345 }
4346 }
4347 return values;
4348 }
4349
4350 private void detachReplacedArrayArgumentReferences(
4351 Map<IndirectArrayArgumentReference, Object> attachedValues) {
4352 for (Map.Entry<IndirectArrayArgumentReference, Object> entry : attachedValues.entrySet()) {
4353 entry.getKey().detachIfReplaced(entry.getValue());
4354 }
4355 }
4356
4357 private void detachMissingArrayArgumentReferences(Map<Object, Object> map) {
4358 if (elementArgumentReferences.isEmpty()) {
4359 return;
4360 }
4361 for (IndirectArrayArgumentReference reference : elementArgumentReferences) {
4362 reference.detachIfMissing(map);
4363 }
4364 }
4365
4366 private static boolean isUntyped(Object value) {
4367 return value == null || value instanceof UntypedObject;
4368 }
4369
4370
4371
4372
4373
4374
4375
4376
4377 private Map<Object, Object> toMap(Object value) {
4378 if (!(value instanceof Map)) {
4379 throw new AwkRuntimeException("Attempting to use a scalar as an array.");
4380 }
4381 @SuppressWarnings("unchecked")
4382 Map<Object, Object> map = (Map<Object, Object>) value;
4383 return map;
4384 }
4385
4386
4387
4388
4389
4390
4391
4392
4393 private void checkScalar(Object value) {
4394 if (value instanceof Map) {
4395 throw new AwkRuntimeException("Attempting to use an array in a scalar context.");
4396 }
4397 }
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409 private Map<Object, Object> ensureArrayInArray(Map<Object, Object> map, Object key) {
4410 checkScalar(key);
4411 boolean existingKey = JRT.containsAwkKey(map, key);
4412 Object value = JRT.getAssocArrayValue(map, key);
4413 if (!existingKey || value == null || value instanceof UntypedObject) {
4414 Map<Object, Object> nested = newAwkArray();
4415 map.put(key, nested);
4416 return nested;
4417 }
4418 if (!(value instanceof Map)) {
4419 throw new AwkRuntimeException("Attempting to use a scalar as an array.");
4420 }
4421 @SuppressWarnings("unchecked")
4422 Map<Object, Object> nested = (Map<Object, Object>) value;
4423 return nested;
4424 }
4425
4426 private Object normalizeExternalVariableValue(Object value) {
4427 if (value instanceof String) {
4428 return jrt.toInputScalar(value);
4429 }
4430 if (!(value instanceof Map) && !(value instanceof List)) {
4431 return value;
4432 }
4433 return AssocArray.normalizeValue(value, sortedArrayKeys);
4434 }
4435
4436 private static final UninitializedObject BLANK = new UninitializedObject();
4437
4438 private interface ArgumentReference {
4439
4440 Object snapshot();
4441
4442 Object currentValue();
4443
4444 void setValue(Object value);
4445
4446 void setScalarValue(Object value);
4447 }
4448
4449 private static final class IndirectArgumentReference implements ArgumentReference {
4450 private final Object[] frame;
4451 private final long offset;
4452 private final Object scalarValue;
4453
4454 private IndirectArgumentReference(Object[] frameParam, long offsetParam, Object scalarValueParam) {
4455 frame = frameParam;
4456 offset = offsetParam;
4457 scalarValue = scalarValueParam;
4458 }
4459
4460 @Override
4461 public Object snapshot() {
4462 return scalarValue;
4463 }
4464
4465 @Override
4466 public Object currentValue() {
4467 return frame[(int) offset];
4468 }
4469
4470 @Override
4471 public void setValue(Object value) {
4472 frame[(int) offset] = value;
4473 }
4474
4475 @Override
4476 public void setScalarValue(Object value) {
4477 if (isUntyped(currentValue())) {
4478 setValue(value);
4479 }
4480 }
4481 }
4482
4483 private static final class IndirectArrayArgumentReference implements ArgumentReference {
4484 private final Map<Object, Object> map;
4485 private final Object key;
4486 private Object detachedValue;
4487 private boolean detached;
4488
4489
4490 private int ownerFrame = -1;
4491
4492 private IndirectArrayArgumentReference(
4493 Map<Object, Object> mapParam,
4494 Object keyParam,
4495 Object scalarValueParam) {
4496 map = mapParam;
4497 key = keyParam;
4498 detachedValue = scalarValueParam;
4499 detached = !JRT.containsAwkKey(map, key);
4500 }
4501
4502 @Override
4503 public Object snapshot() {
4504 return detachedValue;
4505 }
4506
4507 @Override
4508 public Object currentValue() {
4509 return !detached && JRT.containsAwkKey(map, key) ?
4510 JRT.getAssocArrayValue(map, key) : detachedValue;
4511 }
4512
4513 @Override
4514 public void setValue(Object value) {
4515 if (!detached && JRT.containsAwkKey(map, key)) {
4516 map.put(key, value);
4517 } else {
4518 detachedValue = value;
4519 }
4520 }
4521
4522 @Override
4523 public void setScalarValue(Object value) {
4524 if (detached || !JRT.containsAwkKey(map, key)) {
4525 detachedValue = value;
4526 } else if (isUntyped(JRT.getAssocArrayValue(map, key))) {
4527 map.put(key, value);
4528 }
4529 }
4530
4531 private void detachIfMissing(Map<Object, Object> candidateMap) {
4532 if (!detached && map == candidateMap && !JRT.containsAwkKey(map, key)) {
4533 detached = true;
4534 }
4535 }
4536
4537 private boolean isAttached() {
4538 return !detached && JRT.containsAwkKey(map, key);
4539 }
4540
4541 private void detachIfReplaced(Object previousValue) {
4542 if (!detached
4543 && (!JRT.containsAwkKey(map, key)
4544 || JRT.getAssocArrayValue(map, key) != previousValue)) {
4545 detached = true;
4546 }
4547 }
4548 }
4549
4550
4551
4552
4553
4554 private static final Set<String> NON_PERSISTENT_GLOBALS = new HashSet<>(
4555 Arrays
4556 .asList(
4557 "ARGV",
4558 "ARGC",
4559 "ENVIRON",
4560 "RSTART",
4561 "RLENGTH",
4562 "IGNORECASE",
4563 "SYMTAB",
4564 "FUNCTAB"));
4565
4566 private static final class SingleRecordInputSource implements InputSource {
4567
4568 private final String record;
4569 private boolean consumed;
4570
4571 private SingleRecordInputSource(String record) {
4572 this.record = record;
4573 }
4574
4575 @Override
4576 public boolean nextRecord() {
4577 if (consumed || record == null) {
4578 return false;
4579 }
4580 consumed = true;
4581 return true;
4582 }
4583
4584 @Override
4585 public String getRecordText() {
4586 return consumed ? record : null;
4587 }
4588
4589 @Override
4590 public List<String> getFields() {
4591 return null;
4592 }
4593
4594 @Override
4595 public boolean isFromFilenameList() {
4596 return false;
4597 }
4598 }
4599
4600
4601
4602
4603 public static final int NULL_OFFSET = -1;
4604
4605 }