1 package org.metricshub.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.IOException;
26 import java.io.PrintStream;
27 import java.io.StringReader;
28 import java.math.BigDecimal;
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.Enumeration;
32 import java.util.HashMap;
33 import java.util.LinkedList;
34 import java.util.List;
35 import java.util.Locale;
36 import java.util.Map;
37 import java.util.Set;
38 import java.util.LinkedHashSet;
39 import java.util.StringTokenizer;
40 import java.util.Deque;
41 import java.util.regex.Matcher;
42 import java.util.regex.Pattern;
43 import org.metricshub.jawk.AwkSandboxException;
44 import org.metricshub.jawk.ExitException;
45 import org.metricshub.jawk.ext.AbstractExtension;
46 import org.metricshub.jawk.ext.ExtensionFunction;
47 import org.metricshub.jawk.ext.JawkExtension;
48 import org.metricshub.jawk.frontend.AstNode;
49 import org.metricshub.jawk.intermediate.Address;
50 import org.metricshub.jawk.intermediate.AwkTuples;
51 import org.metricshub.jawk.intermediate.Opcode;
52 import org.metricshub.jawk.intermediate.PositionTracker;
53 import org.metricshub.jawk.intermediate.UninitializedObject;
54 import org.metricshub.jawk.jrt.AssocArray;
55 import org.metricshub.jawk.jrt.AwkRuntimeException;
56 import org.metricshub.jawk.jrt.BlockManager;
57 import org.metricshub.jawk.jrt.BlockObject;
58 import org.metricshub.jawk.jrt.CharacterTokenizer;
59 import org.metricshub.jawk.jrt.ConditionPair;
60 import org.metricshub.jawk.jrt.JRT;
61 import java.util.ArrayDeque;
62 import org.metricshub.jawk.jrt.RegexTokenizer;
63 import org.metricshub.jawk.jrt.SingleCharacterTokenizer;
64 import org.metricshub.jawk.jrt.VariableManager;
65 import org.metricshub.jawk.util.AwkSettings;
66 import org.metricshub.jawk.util.ScriptSource;
67 import org.metricshub.jawk.jrt.BSDRandom;
68 import org.metricshub.printf4j.Printf4J;
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97 public class AVM implements VariableManager {
98
99 private static final boolean IS_WINDOWS = System.getProperty("os.name").indexOf("Windows") >= 0;
100
101 private RuntimeStack runtimeStack = new RuntimeStack();
102
103
104 private Deque<Object> operandStack = new LinkedList<Object>();
105 private List<String> arguments;
106 private boolean sortedArrayKeys;
107 private Map<String, Object> initialVariables;
108 private String initialFsValue;
109 private boolean trapIllegalFormatExceptions;
110 private JRT jrt;
111 private final Locale locale;
112 private Map<String, JawkExtension> extensionInstances;
113
114 private Map<String, ExtensionFunction> extensionFunctions;
115
116
117
118
119 private Object pop() {
120 return operandStack.pop();
121 }
122
123 private void push(Object o) {
124 operandStack.push(o);
125 }
126
127 private final AwkSettings settings;
128
129
130
131
132
133
134
135 public AVM() {
136 this(null, Collections.<String, JawkExtension>emptyMap(), Collections.<String, ExtensionFunction>emptyMap());
137 }
138
139
140
141
142
143
144
145
146
147
148 public AVM(final AwkSettings parameters,
149 final Map<String, JawkExtension> extensionInstances,
150 final Map<String, ExtensionFunction> extensionFunctions) {
151 boolean hasProvidedSettings = parameters != null;
152 this.settings = hasProvidedSettings ? parameters : AwkSettings.DEFAULT_SETTINGS;
153 this.extensionInstances = extensionInstances == null ?
154 Collections.<String, JawkExtension>emptyMap() : extensionInstances;
155 this.extensionFunctions = extensionFunctions == null ?
156 Collections.<String, ExtensionFunction>emptyMap() : extensionFunctions;
157
158 locale = this.settings.getLocale();
159 arguments = this.settings.getNameValueOrFileNames();
160 sortedArrayKeys = this.settings.isUseSortedArrayKeys();
161 initialVariables = this.settings.getVariables();
162 initialFsValue = this.settings.getFieldSeparator();
163 trapIllegalFormatExceptions = hasProvidedSettings
164 && this.settings.isCatchIllegalFormatExceptions();
165
166 jrt = createJrt();
167 jrt.setStreams(settings.getOutputStream(), System.err);
168 initExtensions();
169 }
170
171 protected JRT createJrt() {
172 return new JRT(this);
173 }
174
175 protected AwkTuples createTuples() {
176 return new AwkTuples();
177 }
178
179 protected AVM createSubAvm(
180 AwkSettings parameters,
181 Map<String, JawkExtension> subExtensionInstances,
182 Map<String, ExtensionFunction> subExtensionFunctions) {
183 return new AVM(parameters, subExtensionInstances, subExtensionFunctions);
184 }
185
186 private void initExtensions() {
187 if (extensionInstances.isEmpty()) {
188 return;
189 }
190 Set<JawkExtension> initialized = new LinkedHashSet<JawkExtension>();
191 for (JawkExtension extension : extensionInstances.values()) {
192 if (initialized.add(extension)) {
193 extension.init(this, jrt, settings);
194 }
195 }
196 }
197
198 private long nfOffset = NULL_OFFSET;
199 private long nrOffset = NULL_OFFSET;
200 private long fnrOffset = NULL_OFFSET;
201 private long fsOffset = NULL_OFFSET;
202 private long rsOffset = NULL_OFFSET;
203 private long ofsOffset = NULL_OFFSET;
204 private long orsOffset = NULL_OFFSET;
205 private long rstartOffset = NULL_OFFSET;
206 private long rlengthOffset = NULL_OFFSET;
207 private long filenameOffset = NULL_OFFSET;
208 private long subsepOffset = NULL_OFFSET;
209 private long convfmtOffset = NULL_OFFSET;
210 private long ofmtOffset = NULL_OFFSET;
211 private long environOffset = NULL_OFFSET;
212 private long argcOffset = NULL_OFFSET;
213 private long argvOffset = NULL_OFFSET;
214
215 private static final Integer ZERO = Integer.valueOf(0);
216 private static final Integer ONE = Integer.valueOf(1);
217
218
219 private final BSDRandom randomNumberGenerator = new BSDRandom(1);
220
221
222
223
224
225
226
227
228
229 private int oldseed = 1;
230
231 private Address exitAddress = null;
232
233
234
235
236
237 private boolean withinEndBlocks = false;
238
239
240
241
242 private int exitCode = 0;
243
244
245
246
247 private boolean throwExitException = false;
248
249
250
251
252
253
254 private Map<String, Integer> globalVariableOffsets;
255
256
257
258
259 private Map<String, Boolean> globalVariableArrays;
260 private Set<String> functionNames;
261
262
263
264
265
266
267
268
269
270 public Object eval(AwkTuples tuples, String input) throws IOException {
271 jrt.assignInitialVariables(initialVariables);
272
273
274 try {
275 interpret(tuples);
276 } catch (ExitException e) {
277
278
279 return e.getCode();
280 }
281
282
283 return operandStack.size() == 0 ? null : pop();
284 }
285
286 private static int parseIntField(Object obj, PositionTracker position) {
287 if (obj instanceof Number) {
288 double num = ((Number) obj).doubleValue();
289 if (num < 0) {
290 throw new AwkRuntimeException(position.lineNumber(), "Field $(" + obj.toString() + ") is incorrect.");
291 }
292 return (int) num;
293 }
294
295 String str = obj.toString();
296 if (str.isEmpty()) {
297 return 0;
298 }
299
300 try {
301 double num = new BigDecimal(str).doubleValue();
302 if (num < 0) {
303 throw new AwkRuntimeException(position.lineNumber(), "Field $(" + obj.toString() + ") is incorrect.");
304 }
305 return (int) num;
306 } catch (NumberFormatException nfe) {
307 return 0;
308 }
309 }
310
311 private void setNumOnJRT(int fieldNum, double num) {
312 String numString;
313 if (JRT.isActuallyLong(num)) {
314 numString = Long.toString((long) Math.rint(num));
315 } else {
316 numString = Double.toString(num);
317 }
318
319
320 if (fieldNum == 0) {
321 jrt.setInputLine(numString.toString());
322 jrt.jrtParseFields();
323 } else {
324 jrt.jrtSetInputField(numString, fieldNum);
325 }
326 }
327
328 private String execSubOrGSub(PositionTracker position, int gsubArgPos) {
329 String newString;
330
331
332
333
334
335 boolean isGsub = position.boolArg(gsubArgPos);
336 String convfmt = getCONVFMT().toString();
337 String orig = JRT.toAwkString(pop(), convfmt, locale);
338 String repl = JRT.toAwkString(pop(), convfmt, locale);
339 String ere = JRT.toAwkString(pop(), convfmt, locale);
340 if (isGsub) {
341 newString = replaceAll(orig, ere, repl);
342 } else {
343 newString = replaceFirst(orig, ere, repl);
344 }
345
346 return newString;
347 }
348
349
350
351
352
353
354
355 public void interpret(AwkTuples tuples) throws ExitException, IOException {
356 Map<String, Pattern> regexps = new HashMap<String, Pattern>();
357 Map<Integer, ConditionPair> conditionPairs = new HashMap<Integer, ConditionPair>();
358
359 globalVariableOffsets = tuples.getGlobalVariableOffsetMap();
360 globalVariableArrays = tuples.getGlobalVariableAarrayMap();
361 functionNames = tuples.getFunctionNameSet();
362
363 PositionTracker position = tuples.top();
364
365 try {
366 while (!position.isEOF()) {
367
368 Opcode opcode = position.opcode();
369
370 switch (opcode) {
371 case PRINT: {
372
373
374
375
376 long numArgs = position.intArg(0);
377 printTo(settings.getOutputStream(), numArgs);
378 position.next();
379 break;
380 }
381 case PRINT_TO_FILE: {
382
383
384
385
386
387
388 long numArgs = position.intArg(0);
389 boolean append = position.boolArg(1);
390 String key = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
391 PrintStream ps = jrt.jrtGetPrintStream(key, append);
392 printTo(ps, numArgs);
393 position.next();
394 break;
395 }
396 case PRINT_TO_PIPE: {
397
398
399
400
401
402 long numArgs = position.intArg(0);
403 String cmd = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
404 PrintStream ps = jrt.jrtSpawnForOutput(cmd);
405 printTo(ps, numArgs);
406 position.next();
407 break;
408 }
409 case PRINTF: {
410
411
412
413
414 long numArgs = position.intArg(0);
415 printfTo(settings.getOutputStream(), numArgs);
416 position.next();
417 break;
418 }
419 case PRINTF_TO_FILE: {
420
421
422
423
424
425
426 long numArgs = position.intArg(0);
427 boolean append = position.boolArg(1);
428 String key = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
429 PrintStream ps = jrt.jrtGetPrintStream(key, append);
430 printfTo(ps, numArgs);
431 position.next();
432 break;
433 }
434 case PRINTF_TO_PIPE: {
435
436
437
438
439
440 long numArgs = position.intArg(0);
441 String cmd = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
442 PrintStream ps = jrt.jrtSpawnForOutput(cmd);
443 printfTo(ps, numArgs);
444 position.next();
445 break;
446 }
447 case SPRINTF: {
448
449
450
451
452 long numArgs = position.intArg(0);
453 push(sprintfFunction(numArgs));
454 position.next();
455 break;
456 }
457 case LENGTH: {
458
459
460
461
462
463 long num = position.intArg(0);
464 if (num == 0) {
465
466 push(jrt.jrtGetInputField(0).toString().length());
467 } else {
468 push(pop().toString().length());
469 }
470 position.next();
471 break;
472 }
473 case PUSH: {
474
475 push(position.arg(0));
476 position.next();
477 break;
478 }
479 case POP: {
480
481 pop();
482 position.next();
483 break;
484 }
485 case IFFALSE: {
486
487
488
489
490
491
492 boolean jump = !jrt.toBoolean(pop());
493 if (jump) {
494 position.jump(position.addressArg());
495 } else {
496 position.next();
497 }
498 break;
499 }
500 case TO_NUMBER: {
501
502
503
504
505
506 boolean val = jrt.toBoolean(pop());
507 push(val ? ONE : ZERO);
508 position.next();
509 break;
510 }
511 case IFTRUE: {
512
513
514
515
516
517
518 boolean jump = jrt.toBoolean(pop());
519 if (jump) {
520 position.jump(position.addressArg());
521 } else {
522 position.next();
523 }
524 break;
525 }
526 case NOT: {
527
528
529 Object o = pop();
530
531 boolean result = jrt.toBoolean(o);
532
533 if (result) {
534 push(0);
535 } else {
536 push(1);
537 }
538 position.next();
539 break;
540 }
541 case NEGATE: {
542
543
544 double d = JRT.toDouble(pop());
545 if (JRT.isActuallyLong(d)) {
546 push((long) -Math.rint(d));
547 } else {
548 push(-d);
549 }
550 position.next();
551 break;
552 }
553 case UNARY_PLUS: {
554
555 double d = JRT.toDouble(pop());
556 if (JRT.isActuallyLong(d)) {
557 push((long) Math.rint(d));
558 } else {
559 push(d);
560 }
561 position.next();
562 break;
563 }
564 case GOTO: {
565
566
567 position.jump(position.addressArg());
568 break;
569 }
570 case NOP: {
571
572 position.next();
573 break;
574 }
575 case CONCAT: {
576
577
578 String convfmt = getCONVFMT().toString();
579 String s2 = JRT.toAwkString(pop(), convfmt, locale);
580 String s1 = JRT.toAwkString(pop(), convfmt, locale);
581 String resultString = s1 + s2;
582 push(resultString);
583 position.next();
584 break;
585 }
586 case ASSIGN: {
587
588
589
590 Object value = pop();
591 boolean isGlobal = position.boolArg(1);
592 assign(position.intArg(0), value, isGlobal, position);
593 position.next();
594 break;
595 }
596 case ASSIGN_ARRAY: {
597
598
599
600
601 Object arrIdx = pop();
602 Object rhs = pop();
603 if (rhs == null) {
604 rhs = BLANK;
605 }
606 long offset = position.intArg(0);
607 boolean isGlobal = position.boolArg(1);
608 assignArray(offset, arrIdx, rhs, isGlobal);
609 position.next();
610 break;
611 }
612 case PLUS_EQ_ARRAY:
613 case MINUS_EQ_ARRAY:
614 case MULT_EQ_ARRAY:
615 case DIV_EQ_ARRAY:
616 case MOD_EQ_ARRAY:
617 case POW_EQ_ARRAY: {
618
619
620
621
622 Object arrIdx = pop();
623 Object rhs = pop();
624 if (rhs == null) {
625 rhs = BLANK;
626 }
627 long offset = position.intArg(0);
628 boolean isGlobal = position.boolArg(1);
629
630 double val = JRT.toDouble(rhs);
631
632
633
634
635 Object o1 = runtimeStack.getVariable(offset, isGlobal);
636 if (o1 == null || o1 instanceof UninitializedObject) {
637 o1 = new AssocArray(sortedArrayKeys);
638 runtimeStack.setVariable(offset, o1, isGlobal);
639 } else {
640 assert o1 instanceof AssocArray;
641 }
642
643 AssocArray array = (AssocArray) o1;
644 Object o = array.get(arrIdx);
645 assert o != null;
646 double origVal = JRT.toDouble(o);
647
648 double newVal;
649
650 switch (opcode) {
651 case PLUS_EQ_ARRAY:
652 newVal = origVal + val;
653 break;
654 case MINUS_EQ_ARRAY:
655 newVal = origVal - val;
656 break;
657 case MULT_EQ_ARRAY:
658 newVal = origVal * val;
659 break;
660 case DIV_EQ_ARRAY:
661 newVal = origVal / val;
662 break;
663 case MOD_EQ_ARRAY:
664 newVal = origVal % val;
665 break;
666 case POW_EQ_ARRAY:
667 newVal = Math.pow(origVal, val);
668 break;
669 default:
670 throw new Error("Invalid op code here: " + opcode);
671 }
672
673 if (JRT.isActuallyLong(newVal)) {
674 assignArray(offset, arrIdx, (long) Math.rint(newVal), isGlobal);
675 } else {
676 assignArray(offset, arrIdx, newVal, isGlobal);
677 }
678 position.next();
679 break;
680 }
681
682 case ASSIGN_AS_INPUT: {
683
684 jrt.setInputLine(pop().toString());
685 jrt.jrtParseFields();
686 push(jrt.getInputLine());
687 position.next();
688 break;
689 }
690
691 case ASSIGN_AS_INPUT_FIELD: {
692
693
694 Object fieldNumObj = pop();
695 int fieldNum;
696 if (fieldNumObj instanceof Number) {
697 fieldNum = ((Number) fieldNumObj).intValue();
698 } else {
699 try {
700 fieldNum = Integer.parseInt(fieldNumObj.toString());
701 } catch (NumberFormatException nfe) {
702 fieldNum = 0;
703 }
704 }
705 String value = pop().toString();
706 push(value);
707 if (fieldNum == 0) {
708 jrt.setInputLine(value);
709 jrt.jrtParseFields();
710 } else {
711 jrt.jrtSetInputField(value, fieldNum);
712 }
713 position.next();
714 break;
715 }
716 case PLUS_EQ:
717 case MINUS_EQ:
718 case MULT_EQ:
719 case DIV_EQ:
720 case MOD_EQ:
721 case POW_EQ: {
722
723
724
725 boolean isGlobal = position.boolArg(1);
726 Object o1 = runtimeStack.getVariable(position.intArg(0), isGlobal);
727 if (o1 == null) {
728 o1 = BLANK;
729 }
730 Object o2 = pop();
731 double d1 = JRT.toDouble(o1);
732 double d2 = JRT.toDouble(o2);
733 double ans;
734 switch (opcode) {
735 case PLUS_EQ:
736 ans = d1 + d2;
737 break;
738 case MINUS_EQ:
739 ans = d1 - d2;
740 break;
741 case MULT_EQ:
742 ans = d1 * d2;
743 break;
744 case DIV_EQ:
745 ans = d1 / d2;
746 break;
747 case MOD_EQ:
748 ans = d1 % d2;
749 break;
750 case POW_EQ:
751 ans = Math.pow(d1, d2);
752 break;
753 default:
754 throw new Error("Invalid opcode here: " + opcode);
755 }
756 if (JRT.isActuallyLong(ans)) {
757 long integral = (long) Math.rint(ans);
758 push(integral);
759 runtimeStack.setVariable(position.intArg(0), integral, isGlobal);
760 } else {
761 push(ans);
762 runtimeStack.setVariable(position.intArg(0), ans, isGlobal);
763 }
764 position.next();
765 break;
766 }
767 case PLUS_EQ_INPUT_FIELD:
768 case MINUS_EQ_INPUT_FIELD:
769 case MULT_EQ_INPUT_FIELD:
770 case DIV_EQ_INPUT_FIELD:
771 case MOD_EQ_INPUT_FIELD:
772 case POW_EQ_INPUT_FIELD: {
773
774
775
776
777 int fieldnum = parseIntField(pop(), position);
778 double incval = JRT.toDouble(pop());
779
780
781 Object numObj = jrt.jrtGetInputField(fieldnum);
782 double num;
783 switch (opcode) {
784 case PLUS_EQ_INPUT_FIELD:
785 num = JRT.toDouble(numObj) + incval;
786 break;
787 case MINUS_EQ_INPUT_FIELD:
788 num = JRT.toDouble(numObj) - incval;
789 break;
790 case MULT_EQ_INPUT_FIELD:
791 num = JRT.toDouble(numObj) * incval;
792 break;
793 case DIV_EQ_INPUT_FIELD:
794 num = JRT.toDouble(numObj) / incval;
795 break;
796 case MOD_EQ_INPUT_FIELD:
797 num = JRT.toDouble(numObj) % incval;
798 break;
799 case POW_EQ_INPUT_FIELD:
800 num = Math.pow(JRT.toDouble(numObj), incval);
801 break;
802 default:
803 throw new Error("Invalid opcode here: " + opcode);
804 }
805 setNumOnJRT(fieldnum, num);
806
807
808 push(num);
809 position.next();
810
811 break;
812 }
813 case INC: {
814
815
816 inc(position.intArg(0), position.boolArg(1));
817 position.next();
818 break;
819 }
820 case DEC: {
821
822
823 dec(position.intArg(0), position.boolArg(1));
824 position.next();
825 break;
826 }
827 case POSTINC: {
828
829
830 pop();
831 push(inc(position.intArg(0), position.boolArg(1)));
832 position.next();
833 break;
834 }
835 case POSTDEC: {
836
837
838 pop();
839 push(dec(position.intArg(0), position.boolArg(1)));
840 position.next();
841 break;
842 }
843 case INC_ARRAY_REF: {
844
845
846
847 boolean isGlobal = position.boolArg(1);
848 Object o1 = runtimeStack.getVariable(position.intArg(0), isGlobal);
849 if (o1 == null || o1 instanceof UninitializedObject) {
850 o1 = new AssocArray(sortedArrayKeys);
851 runtimeStack.setVariable(position.intArg(0), o1, isGlobal);
852 }
853 AssocArray aa = (AssocArray) o1;
854 Object key = pop();
855 Object o = aa.get(key);
856 assert o != null;
857 double ans = JRT.toDouble(o) + 1;
858 if (JRT.isActuallyLong(ans)) {
859 aa.put(key, (long) Math.rint(ans));
860 } else {
861 aa.put(key, ans);
862 }
863 position.next();
864 break;
865 }
866 case DEC_ARRAY_REF: {
867
868
869
870 boolean isGlobal = position.boolArg(1);
871 Object o1 = runtimeStack.getVariable(position.intArg(0), isGlobal);
872 if (o1 == null || o1 instanceof UninitializedObject) {
873 o1 = new AssocArray(sortedArrayKeys);
874 runtimeStack.setVariable(position.intArg(0), o1, isGlobal);
875 }
876 AssocArray aa = (AssocArray) o1;
877 Object key = pop();
878 Object o = aa.get(key);
879 assert o != null;
880 double ans = JRT.toDouble(o) - 1;
881 if (JRT.isActuallyLong(ans)) {
882 aa.put(key, (long) Math.rint(ans));
883 } else {
884 aa.put(key, ans);
885 }
886 position.next();
887 break;
888 }
889 case INC_DOLLAR_REF: {
890
891 int fieldnum = parseIntField(pop(), position);
892
893 Object numObj = jrt.jrtGetInputField(fieldnum);
894 double original = JRT.toDouble(numObj);
895 double num = original + 1;
896 setNumOnJRT(fieldnum, num);
897
898 if (JRT.isActuallyLong(original)) {
899 push((long) Math.rint(original));
900 } else {
901 push(Double.valueOf(original));
902 }
903
904 position.next();
905 break;
906 }
907 case DEC_DOLLAR_REF: {
908
909
910 int fieldnum = parseIntField(pop(), position);
911
912 Object numObj = jrt.jrtGetInputField(fieldnum);
913 double original = JRT.toDouble(numObj);
914 double num = original - 1;
915 setNumOnJRT(fieldnum, num);
916
917 if (JRT.isActuallyLong(original)) {
918 push((long) Math.rint(original));
919 } else {
920 push(Double.valueOf(original));
921 }
922
923 position.next();
924 break;
925 }
926 case DEREFERENCE: {
927
928
929 boolean isGlobal = position.boolArg(2);
930 Object o = runtimeStack.getVariable(position.intArg(0), isGlobal);
931 if (o == null) {
932 if (position.boolArg(1)) {
933
934 push(runtimeStack.setVariable(position.intArg(0), new AssocArray(sortedArrayKeys), isGlobal));
935 } else {
936 push(runtimeStack.setVariable(position.intArg(0), BLANK, isGlobal));
937 }
938 } else {
939 push(o);
940 }
941 position.next();
942 break;
943 }
944 case DEREF_ARRAY: {
945
946
947 Object idx = pop();
948 Object array = pop();
949 if (!(array instanceof AssocArray)) {
950 throw new AwkRuntimeException("Attempting to index a non-associative-array.");
951 }
952 Object o = ((AssocArray) array).get(idx);
953 assert o != null;
954 push(o);
955 position.next();
956 break;
957 }
958 case SRAND: {
959
960
961 long numArgs = position.intArg(0);
962 int seed;
963 if (numArgs == 0) {
964
965 seed = JRT.timeSeed();
966 } else {
967 Object o = pop();
968 if (o instanceof Double) {
969 seed = ((Double) o).intValue();
970 } else if (o instanceof Long) {
971 seed = ((Long) o).intValue();
972 } else if (o instanceof Integer) {
973 seed = ((Integer) o).intValue();
974 } else {
975 try {
976 seed = Integer.parseInt(o.toString());
977 } catch (NumberFormatException nfe) {
978 seed = 0;
979 }
980 }
981 }
982 randomNumberGenerator.setSeed(seed);
983 push(oldseed);
984 oldseed = seed;
985 position.next();
986 break;
987 }
988 case RAND: {
989 push(randomNumberGenerator.nextDouble());
990 position.next();
991 break;
992 }
993 case INTFUNC: {
994
995 push((long) JRT.toDouble(pop()));
996 position.next();
997 break;
998 }
999 case SQRT: {
1000
1001 push(Math.sqrt(JRT.toDouble(pop())));
1002 position.next();
1003 break;
1004 }
1005 case LOG: {
1006
1007 push(Math.log(JRT.toDouble(pop())));
1008 position.next();
1009 break;
1010 }
1011 case EXP: {
1012
1013 push(Math.exp(JRT.toDouble(pop())));
1014 position.next();
1015 break;
1016 }
1017 case SIN: {
1018
1019 push(Math.sin(JRT.toDouble(pop())));
1020 position.next();
1021 break;
1022 }
1023 case COS: {
1024
1025 push(Math.cos(JRT.toDouble(pop())));
1026 position.next();
1027 break;
1028 }
1029 case ATAN2: {
1030
1031
1032 double d2 = JRT.toDouble(pop());
1033 double d1 = JRT.toDouble(pop());
1034 push(Math.atan2(d1, d2));
1035 position.next();
1036 break;
1037 }
1038 case MATCH: {
1039
1040
1041 String convfmt = getCONVFMT().toString();
1042 String ere = JRT.toAwkString(pop(), convfmt, locale);
1043 String s = JRT.toAwkString(pop(), convfmt, locale);
1044
1045
1046 int flags = 0;
1047
1048 if (globalVariableOffsets.containsKey("IGNORECASE")) {
1049 Integer offsetObj = globalVariableOffsets.get("IGNORECASE");
1050 Object ignorecase = runtimeStack.getVariable(offsetObj, true);
1051
1052 if (JRT.toDouble(ignorecase) != 0) {
1053 flags |= Pattern.CASE_INSENSITIVE;
1054 }
1055 }
1056
1057 Pattern pattern = Pattern.compile(ere, flags);
1058 Matcher matcher = pattern.matcher(s);
1059 boolean result = matcher.find();
1060 if (result) {
1061 assign(rstartOffset, matcher.start() + 1, true, position);
1062 assign(rlengthOffset, matcher.end() - matcher.start(), true, position);
1063 pop();
1064
1065 } else {
1066 assign(rstartOffset, ZERO, true, position);
1067 assign(rlengthOffset, -1, true, position);
1068 pop();
1069
1070 }
1071 position.next();
1072 break;
1073 }
1074 case INDEX: {
1075
1076
1077 String convfmt = getCONVFMT().toString();
1078 String s2 = JRT.toAwkString(pop(), convfmt, locale);
1079 String s1 = JRT.toAwkString(pop(), convfmt, locale);
1080 push(s1.indexOf(s2) + 1);
1081 position.next();
1082 break;
1083 }
1084 case SUB_FOR_DOLLAR_0: {
1085
1086
1087
1088 boolean isGsub = position.boolArg(0);
1089 String convfmt = getCONVFMT().toString();
1090 String repl = JRT.toAwkString(pop(), convfmt, locale);
1091 String ere = JRT.toAwkString(pop(), convfmt, locale);
1092 String orig = JRT.toAwkString(jrt.jrtGetInputField(0), convfmt, locale);
1093 String newstring;
1094 if (isGsub) {
1095 newstring = replaceAll(orig, ere, repl);
1096 } else {
1097 newstring = replaceFirst(orig, ere, repl);
1098 }
1099
1100 jrt.setInputLine(newstring);
1101 jrt.jrtParseFields();
1102 position.next();
1103 break;
1104 }
1105 case SUB_FOR_DOLLAR_REFERENCE: {
1106
1107
1108
1109
1110
1111 boolean isGsub = position.boolArg(0);
1112 String convfmt = getCONVFMT().toString();
1113 int fieldNum = (int) JRT.toDouble(pop());
1114 String orig = JRT.toAwkString(pop(), convfmt, locale);
1115 String repl = JRT.toAwkString(pop(), convfmt, locale);
1116 String ere = JRT.toAwkString(pop(), convfmt, locale);
1117 String newstring;
1118 if (isGsub) {
1119 newstring = replaceAll(orig, ere, repl);
1120 } else {
1121 newstring = replaceFirst(orig, ere, repl);
1122 }
1123
1124 if (fieldNum == 0) {
1125 jrt.setInputLine(newstring);
1126 jrt.jrtParseFields();
1127 } else {
1128 jrt.jrtSetInputField(newstring, fieldNum);
1129 }
1130 position.next();
1131 break;
1132 }
1133 case SUB_FOR_VARIABLE: {
1134
1135
1136
1137
1138
1139
1140 long offset = position.intArg(0);
1141 boolean isGlobal = position.boolArg(1);
1142 String newString = execSubOrGSub(position, 2);
1143
1144 assign(offset, newString, isGlobal, position);
1145 pop();
1146 position.next();
1147 break;
1148 }
1149 case SUB_FOR_ARRAY_REFERENCE: {
1150
1151
1152
1153
1154
1155
1156
1157
1158 long offset = position.intArg(0);
1159 boolean isGlobal = position.boolArg(1);
1160 Object arrIdx = pop();
1161 String newString = execSubOrGSub(position, 2);
1162
1163 assignArray(offset, arrIdx, newString, isGlobal);
1164 pop();
1165 position.next();
1166 break;
1167 }
1168 case SPLIT: {
1169
1170
1171
1172
1173 String convfmt = getCONVFMT().toString();
1174 long numArgs = position.intArg(0);
1175 String fsString;
1176 if (numArgs == 2) {
1177 fsString = JRT.toAwkString(getFS(), convfmt, locale);
1178 } else if (numArgs == 3) {
1179 fsString = JRT.toAwkString(pop(), convfmt, locale);
1180 } else {
1181 throw new Error("Invalid # of args. split() requires 2 or 3. Got: " + numArgs);
1182 }
1183 Object o = pop();
1184 if (!(o instanceof AssocArray)) {
1185 throw new AwkRuntimeException(position.lineNumber(), o + " is not an array.");
1186 }
1187 String s = JRT.toAwkString(pop(), convfmt, locale);
1188 Enumeration<Object> tokenizer;
1189 if (fsString.equals(" ")) {
1190 tokenizer = new StringTokenizer(s);
1191 } else if (fsString.length() == 1) {
1192 tokenizer = new SingleCharacterTokenizer(s, fsString.charAt(0));
1193 } else if (fsString.isEmpty()) {
1194 tokenizer = new CharacterTokenizer(s);
1195 } else {
1196 tokenizer = new RegexTokenizer(s, fsString);
1197 }
1198
1199 AssocArray assocArray = (AssocArray) o;
1200 assocArray.clear();
1201 int cnt = 0;
1202 while (tokenizer.hasMoreElements()) {
1203 assocArray.put(++cnt, tokenizer.nextElement());
1204 }
1205 push(cnt);
1206 position.next();
1207 break;
1208 }
1209 case SUBSTR: {
1210
1211
1212
1213
1214 long numArgs = position.intArg(0);
1215 int startPos, length;
1216 String s;
1217 if (numArgs == 3) {
1218 length = (int) JRT.toLong(pop());
1219 startPos = (int) JRT.toDouble(pop());
1220 s = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
1221 } else if (numArgs == 2) {
1222 startPos = (int) JRT.toDouble(pop());
1223 s = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
1224 length = s.length() - startPos + 1;
1225 } else {
1226 throw new Error("numArgs for SUBSTR must be 2 or 3. It is " + numArgs);
1227 }
1228 if (startPos <= 0) {
1229 startPos = 1;
1230 }
1231 if (length <= 0 || startPos > s.length()) {
1232 push(BLANK);
1233 } else {
1234 if (startPos + length > s.length()) {
1235 push(s.substring(startPos - 1));
1236 } else {
1237 push(s.substring(startPos - 1, startPos + length - 1));
1238 }
1239 }
1240 position.next();
1241 break;
1242 }
1243 case TOLOWER: {
1244
1245 push(JRT.toAwkString(pop(), getCONVFMT().toString(), locale).toLowerCase());
1246 position.next();
1247 break;
1248 }
1249 case TOUPPER: {
1250
1251 push(JRT.toAwkString(pop(), getCONVFMT().toString(), locale).toUpperCase());
1252 position.next();
1253 break;
1254 }
1255 case SYSTEM: {
1256
1257 String s = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
1258 push(jrt.jrtSystem(s));
1259 position.next();
1260 break;
1261 }
1262 case SWAP: {
1263
1264
1265 swapOnStack();
1266 position.next();
1267 break;
1268 }
1269 case CMP_EQ: {
1270
1271
1272 Object o2 = pop();
1273 Object o1 = pop();
1274 push(JRT.compare2(o1, o2, 0) ? ONE : ZERO);
1275 position.next();
1276 break;
1277 }
1278 case CMP_LT: {
1279
1280
1281 Object o2 = pop();
1282 Object o1 = pop();
1283 push(JRT.compare2(o1, o2, -1) ? ONE : ZERO);
1284 position.next();
1285 break;
1286 }
1287 case CMP_GT: {
1288
1289
1290 Object o2 = pop();
1291 Object o1 = pop();
1292 push(JRT.compare2(o1, o2, 1) ? ONE : ZERO);
1293 position.next();
1294 break;
1295 }
1296 case MATCHES: {
1297
1298
1299 Object o2 = pop();
1300 Object o1 = pop();
1301
1302 String s = o1.toString();
1303
1304 if (o2 instanceof Pattern) {
1305 Pattern p = (Pattern) o2;
1306 Matcher m = p.matcher(s);
1307
1308
1309 boolean result = m.find();
1310 push(result ? 1 : 0);
1311 } else {
1312 String r = JRT.toAwkString(o2, getCONVFMT().toString(), locale);
1313 boolean result = Pattern.compile(r).matcher(s).find();
1314 push(result ? 1 : 0);
1315 }
1316 position.next();
1317 break;
1318 }
1319 case ADD: {
1320
1321
1322 Object o2 = pop();
1323 Object o1 = pop();
1324 double d1 = JRT.toDouble(o1);
1325 double d2 = JRT.toDouble(o2);
1326 double ans = d1 + d2;
1327 if (JRT.isActuallyLong(ans)) {
1328 push((long) Math.rint(ans));
1329 } else {
1330 push(ans);
1331 }
1332 position.next();
1333 break;
1334 }
1335 case SUBTRACT: {
1336
1337
1338 Object o2 = pop();
1339 Object o1 = pop();
1340 double d1 = JRT.toDouble(o1);
1341 double d2 = JRT.toDouble(o2);
1342 double ans = d1 - d2;
1343 if (JRT.isActuallyLong(ans)) {
1344 push((long) Math.rint(ans));
1345 } else {
1346 push(ans);
1347 }
1348 position.next();
1349 break;
1350 }
1351 case MULTIPLY: {
1352
1353
1354 Object o2 = pop();
1355 Object o1 = pop();
1356 double d1 = JRT.toDouble(o1);
1357 double d2 = JRT.toDouble(o2);
1358 double ans = d1 * d2;
1359 if (JRT.isActuallyLong(ans)) {
1360 push((long) Math.rint(ans));
1361 } else {
1362 push(ans);
1363 }
1364 position.next();
1365 break;
1366 }
1367 case DIVIDE: {
1368
1369
1370 Object o2 = pop();
1371 Object o1 = pop();
1372 double d1 = JRT.toDouble(o1);
1373 double d2 = JRT.toDouble(o2);
1374 double ans = d1 / d2;
1375 if (JRT.isActuallyLong(ans)) {
1376 push((long) Math.rint(ans));
1377 } else {
1378 push(ans);
1379 }
1380 position.next();
1381 break;
1382 }
1383 case MOD: {
1384
1385
1386 Object o2 = pop();
1387 Object o1 = pop();
1388 double d1 = JRT.toDouble(o1);
1389 double d2 = JRT.toDouble(o2);
1390 double ans = d1 % d2;
1391 if (JRT.isActuallyLong(ans)) {
1392 push((long) Math.rint(ans));
1393 } else {
1394 push(ans);
1395 }
1396 position.next();
1397 break;
1398 }
1399 case POW: {
1400
1401
1402 Object o2 = pop();
1403 Object o1 = pop();
1404 double d1 = JRT.toDouble(o1);
1405 double d2 = JRT.toDouble(o2);
1406 double ans = Math.pow(d1, d2);
1407 if (JRT.isActuallyLong(ans)) {
1408 push((long) Math.rint(ans));
1409 } else {
1410 push(ans);
1411 }
1412 position.next();
1413 break;
1414 }
1415 case DUP: {
1416
1417 Object o = pop();
1418 push(o);
1419 push(o);
1420 position.next();
1421 break;
1422 }
1423 case KEYLIST: {
1424
1425 Object o = pop();
1426 assert o != null;
1427 if (!(o instanceof AssocArray)) {
1428 throw new AwkRuntimeException(
1429 position.lineNumber(),
1430 "Cannot get a key list (via 'in') of a non associative array. arg = " + o.getClass() + ", " + o);
1431 }
1432 AssocArray aa = (AssocArray) o;
1433 push(new ArrayDeque<>(aa.keySet()));
1434 position.next();
1435 break;
1436 }
1437 case IS_EMPTY_KEYLIST: {
1438
1439
1440 Object o = pop();
1441 if (o == null || !(o instanceof Deque)) {
1442 throw new AwkRuntimeException(
1443 position.lineNumber(),
1444 "Cannot get a key list (via 'in') of a non associative array. arg = " + o.getClass() + ", " + o);
1445 }
1446 Deque<?> keylist = (Deque<?>) o;
1447 if (keylist.isEmpty()) {
1448 position.jump(position.addressArg());
1449 } else {
1450 position.next();
1451 }
1452 break;
1453 }
1454 case GET_FIRST_AND_REMOVE_FROM_KEYLIST: {
1455
1456 Object o = pop();
1457 if (o == null || !(o instanceof Deque)) {
1458 throw new AwkRuntimeException(
1459 position.lineNumber(),
1460 "Cannot get a key list (via 'in') of a non associative array. arg = " + o.getClass() + ", " + o);
1461 }
1462
1463 Deque<?> keylist = (Deque<?>) o;
1464 assert !keylist.isEmpty();
1465 push(keylist.removeFirst());
1466 position.next();
1467 break;
1468 }
1469 case CHECK_CLASS: {
1470
1471
1472 Object o = pop();
1473 if (!position.classArg().isInstance(o)) {
1474 throw new AwkRuntimeException(
1475 position.lineNumber(),
1476 "Verification failed. Top-of-stack = " + o.getClass() + " isn't an instance of " + position.classArg());
1477 }
1478 push(o);
1479 position.next();
1480 break;
1481 }
1482 case CONSUME_INPUT: {
1483
1484
1485
1486 if (avmConsumeInput(false)) {
1487 position.next();
1488 } else {
1489 position.jump(position.addressArg());
1490 }
1491 break;
1492 }
1493
1494 case SET_INPUT_FOR_EVAL: {
1495 jrt.setInputLineforEval(settings.getInput());
1496 position.next();
1497 break;
1498 }
1499
1500 case GETLINE_INPUT: {
1501 avmConsumeInputForGetline();
1502 position.next();
1503 break;
1504 }
1505 case USE_AS_FILE_INPUT: {
1506
1507 String s = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
1508 avmConsumeFileInputForGetline(s);
1509 position.next();
1510 break;
1511 }
1512 case USE_AS_COMMAND_INPUT: {
1513
1514 String s = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
1515 avmConsumeCommandInputForGetline(s);
1516 position.next();
1517 break;
1518 }
1519 case NF_OFFSET: {
1520
1521 nfOffset = position.intArg(0);
1522 assert nfOffset != NULL_OFFSET;
1523 assign(nfOffset, 0, true, position);
1524 pop();
1525 position.next();
1526 break;
1527 }
1528 case NR_OFFSET: {
1529
1530 nrOffset = position.intArg(0);
1531 assert nrOffset != NULL_OFFSET;
1532 assign(nrOffset, 0, true, position);
1533 pop();
1534 position.next();
1535 break;
1536 }
1537 case FNR_OFFSET: {
1538
1539 fnrOffset = position.intArg(0);
1540 assert fnrOffset != NULL_OFFSET;
1541 assign(fnrOffset, 0, true, position);
1542 pop();
1543 position.next();
1544 break;
1545 }
1546 case FS_OFFSET: {
1547
1548 fsOffset = position.intArg(0);
1549 assert fsOffset != NULL_OFFSET;
1550 if (initialFsValue == null) {
1551 assign(fsOffset, " ", true, position);
1552 } else {
1553 assign(fsOffset, initialFsValue, true, position);
1554 }
1555 pop();
1556 position.next();
1557 break;
1558 }
1559 case RS_OFFSET: {
1560
1561 rsOffset = position.intArg(0);
1562 assert rsOffset != NULL_OFFSET;
1563 assign(rsOffset, settings.getDefaultRS(), true, position);
1564 pop();
1565 position.next();
1566 break;
1567 }
1568 case OFS_OFFSET: {
1569
1570 ofsOffset = position.intArg(0);
1571 assert ofsOffset != NULL_OFFSET;
1572 assign(ofsOffset, " ", true, position);
1573 pop();
1574 position.next();
1575 break;
1576 }
1577 case ORS_OFFSET: {
1578
1579 orsOffset = position.intArg(0);
1580 assert orsOffset != NULL_OFFSET;
1581 assign(orsOffset, settings.getDefaultORS(), true, position);
1582 pop();
1583 position.next();
1584 break;
1585 }
1586 case RSTART_OFFSET: {
1587
1588 rstartOffset = position.intArg(0);
1589 assert rstartOffset != NULL_OFFSET;
1590 assign(rstartOffset, "", true, position);
1591 pop();
1592 position.next();
1593 break;
1594 }
1595 case RLENGTH_OFFSET: {
1596
1597 rlengthOffset = position.intArg(0);
1598 assert rlengthOffset != NULL_OFFSET;
1599 assign(rlengthOffset, "", true, position);
1600 pop();
1601 position.next();
1602 break;
1603 }
1604 case FILENAME_OFFSET: {
1605
1606 filenameOffset = position.intArg(0);
1607 assert filenameOffset != NULL_OFFSET;
1608 assign(filenameOffset, "", true, position);
1609 pop();
1610 position.next();
1611 break;
1612 }
1613 case SUBSEP_OFFSET: {
1614
1615 subsepOffset = position.intArg(0);
1616 assert subsepOffset != NULL_OFFSET;
1617 assign(subsepOffset, String.valueOf((char) 28), true, position);
1618 pop();
1619 position.next();
1620 break;
1621 }
1622 case CONVFMT_OFFSET: {
1623
1624 convfmtOffset = position.intArg(0);
1625 assert convfmtOffset != NULL_OFFSET;
1626 assign(convfmtOffset, "%.6g", true, position);
1627 pop();
1628 position.next();
1629 break;
1630 }
1631 case OFMT_OFFSET: {
1632
1633 ofmtOffset = position.intArg(0);
1634 assert ofmtOffset != NULL_OFFSET;
1635 assign(ofmtOffset, "%.6g", true, position);
1636 pop();
1637 position.next();
1638 break;
1639 }
1640 case ENVIRON_OFFSET: {
1641
1642
1643 environOffset = position.intArg(0);
1644 assert environOffset != NULL_OFFSET;
1645
1646 Map<String, String> env = System.getenv();
1647 for (Map.Entry<String, String> var : env.entrySet()) {
1648 assignArray(environOffset, var.getKey(), var.getValue(), true);
1649 pop();
1650 }
1651 position.next();
1652 break;
1653 }
1654 case ARGC_OFFSET: {
1655
1656 argcOffset = position.intArg(0);
1657 assert argcOffset != NULL_OFFSET;
1658
1659
1660 assign(argcOffset, arguments.size() + 1, true, position);
1661 pop();
1662 position.next();
1663 break;
1664 }
1665 case ARGV_OFFSET: {
1666
1667 argvOffset = position.intArg(0);
1668 assert argvOffset != NULL_OFFSET;
1669
1670 int argc = (int) JRT.toDouble(runtimeStack.getVariable(argcOffset, true));
1671 assignArray(argvOffset, 0, "java Awk", true);
1672 pop();
1673 for (int i = 1; i < argc; i++) {
1674
1675 assignArray(argvOffset, i, arguments.get(i - 1), true);
1676 pop();
1677 }
1678 position.next();
1679 break;
1680 }
1681 case GET_INPUT_FIELD: {
1682
1683 int fieldnum = parseIntField(pop(), position);
1684 push(jrt.jrtGetInputField(fieldnum));
1685 position.next();
1686 break;
1687 }
1688 case APPLY_RS: {
1689 assert rsOffset != NULL_OFFSET;
1690 Object rsObj = runtimeStack.getVariable(rsOffset, true);
1691 if (jrt.getPartitioningReader() != null) {
1692 jrt.getPartitioningReader().setRecordSeparator(rsObj.toString());
1693 }
1694 position.next();
1695 break;
1696 }
1697 case CALL_FUNCTION: {
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707 Address funcAddr = position.addressArg();
1708
1709 long numFormalParams = position.intArg(2);
1710 long numActualParams = position.intArg(3);
1711 assert numFormalParams >= numActualParams;
1712 runtimeStack.pushFrame(numFormalParams, position.current());
1713
1714 for (long i = numActualParams - 1; i >= 0; i--) {
1715 runtimeStack.setVariable(i, pop(), false);
1716 }
1717 position.jump(funcAddr);
1718
1719 break;
1720 }
1721 case FUNCTION: {
1722
1723
1724
1725
1726 position.next();
1727 break;
1728 }
1729 case SET_RETURN_RESULT: {
1730
1731 runtimeStack.setReturnValue(pop());
1732 position.next();
1733 break;
1734 }
1735 case RETURN_FROM_FUNCTION: {
1736 position.jump(runtimeStack.popFrame());
1737 push(runtimeStack.getReturnValue());
1738 position.next();
1739 break;
1740 }
1741 case SET_NUM_GLOBALS: {
1742
1743 assert position.intArg(0) == globalVariableOffsets.size();
1744 runtimeStack.setNumGlobals(position.intArg(0));
1745
1746
1747
1748
1749
1750 for (Map.Entry<String, Object> entry : initialVariables.entrySet()) {
1751 String key = entry.getKey();
1752 if (functionNames.contains(key)) {
1753 throw new IllegalArgumentException("Cannot assign a scalar to a function name (" + key + ").");
1754 }
1755 Integer offsetObj = globalVariableOffsets.get(key);
1756 Boolean arrayObj = globalVariableArrays.get(key);
1757 if (offsetObj != null) {
1758 assert arrayObj != null;
1759 if (arrayObj.booleanValue()) {
1760 throw new IllegalArgumentException("Cannot assign a scalar to a non-scalar variable (" + key + ").");
1761 } else {
1762 Object obj = entry.getValue();
1763 runtimeStack.setFilelistVariable(offsetObj.intValue(), obj);
1764 }
1765 }
1766 }
1767
1768 position.next();
1769 break;
1770 }
1771 case CLOSE: {
1772
1773 String s = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
1774 push(jrt.jrtClose(s));
1775 position.next();
1776 break;
1777 }
1778 case APPLY_SUBSEP: {
1779
1780
1781
1782
1783 long count = position.intArg(0);
1784 assert count >= 1;
1785
1786 String convfmt = getCONVFMT().toString();
1787 if (count == 1) {
1788 push(JRT.toAwkString(pop(), convfmt, locale));
1789 } else {
1790 StringBuilder sb = new StringBuilder();
1791 sb.append(JRT.toAwkString(pop(), convfmt, locale));
1792 String subsep = JRT.toAwkString(runtimeStack.getVariable(subsepOffset, true), convfmt, locale);
1793 for (int i = 1; i < count; i++) {
1794 sb.insert(0, subsep);
1795 sb.insert(0, JRT.toAwkString(pop(), convfmt, locale));
1796 }
1797 push(sb.toString());
1798 }
1799 position.next();
1800 break;
1801 }
1802 case DELETE_ARRAY_ELEMENT: {
1803
1804
1805
1806 long offset = position.intArg(0);
1807 boolean isGlobal = position.boolArg(1);
1808 AssocArray aa = (AssocArray) runtimeStack.getVariable(offset, isGlobal);
1809 Object key = pop();
1810 if (aa != null) {
1811 aa.remove(key);
1812 }
1813 position.next();
1814 break;
1815 }
1816 case DELETE_ARRAY: {
1817
1818
1819
1820 long offset = position.intArg(0);
1821 boolean isGlobal = position.boolArg(1);
1822 runtimeStack.removeVariable(offset, isGlobal);
1823 position.next();
1824 break;
1825 }
1826 case SET_EXIT_ADDRESS: {
1827
1828 exitAddress = position.addressArg();
1829 position.next();
1830 break;
1831 }
1832 case SET_WITHIN_END_BLOCKS: {
1833
1834 withinEndBlocks = position.boolArg(0);
1835 position.next();
1836 break;
1837 }
1838 case EXIT_WITHOUT_CODE:
1839 case EXIT_WITH_CODE: {
1840 if (opcode == Opcode.EXIT_WITH_CODE) {
1841
1842 exitCode = (int) JRT.toDouble(pop());
1843 }
1844 throwExitException = true;
1845
1846
1847 if (!withinEndBlocks && exitAddress != null) {
1848
1849 runtimeStack.popAllFrames();
1850
1851 operandStack.clear();
1852 position.jump(exitAddress);
1853 } else {
1854
1855 jrt.jrtCloseAll();
1856
1857 operandStack.clear();
1858 throw new ExitException(exitCode, "The AWK script requested an exit");
1859
1860 }
1861 break;
1862 }
1863 case REGEXP: {
1864
1865 String key = JRT.toAwkString(position.arg(0), getCONVFMT().toString(), locale);
1866 Pattern pattern = regexps.get(key);
1867 if (pattern == null) {
1868 pattern = Pattern.compile(key);
1869 regexps.put(key, pattern);
1870 }
1871 push(pattern);
1872 position.next();
1873 break;
1874 }
1875 case CONDITION_PAIR: {
1876
1877
1878 ConditionPair cp = conditionPairs.get(position.current());
1879 if (cp == null) {
1880 cp = new ConditionPair();
1881 conditionPairs.put(position.current(), cp);
1882 }
1883 boolean end = jrt.toBoolean(pop());
1884 boolean start = jrt.toBoolean(pop());
1885 push(cp.update(start, end) ? ONE : ZERO);
1886 position.next();
1887 break;
1888 }
1889 case IS_IN: {
1890
1891
1892 Object arr = pop();
1893 Object arg = pop();
1894 AssocArray aa = (AssocArray) arr;
1895 boolean result = aa.isIn(arg);
1896 push(result ? ONE : ZERO);
1897 position.next();
1898 break;
1899 }
1900 case THIS: {
1901
1902
1903
1904
1905 position.next();
1906 break;
1907 }
1908 case EXEC: {
1909
1910
1911
1912 String awkCode = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
1913 List<ScriptSource> scriptSources = new ArrayList<ScriptSource>(1);
1914 scriptSources
1915 .add(new ScriptSource(ScriptSource.DESCRIPTION_COMMAND_LINE_SCRIPT, new StringReader(awkCode)));
1916
1917 org.metricshub.jawk.frontend.AwkParser ap = new org.metricshub.jawk.frontend.AwkParser(
1918 extensionFunctions);
1919 try {
1920 AstNode ast = ap.parse(scriptSources);
1921 if (ast != null) {
1922 ast.semanticAnalysis();
1923 ast.semanticAnalysis();
1924 AwkTuples newTuples = createTuples();
1925 int result = ast.populateTuples(newTuples);
1926 assert result == 0;
1927 newTuples.postProcess();
1928 ap.populateGlobalVariableNameToOffsetMappings(newTuples);
1929 AVM newAvm = createSubAvm(
1930 settings,
1931 extensionInstances,
1932 extensionFunctions);
1933 int subScriptExitCode = 0;
1934 try {
1935 newAvm.interpret(newTuples);
1936 } catch (ExitException ex) {
1937 subScriptExitCode = ex.getCode();
1938 }
1939 push(subScriptExitCode);
1940 } else {
1941 push(-1);
1942 }
1943 } catch (IOException ioe) {
1944 throw new AwkRuntimeException(position.lineNumber(), "IO Exception caught : " + ioe);
1945 }
1946
1947 position.next();
1948 break;
1949 }
1950 case EXTENSION: {
1951
1952
1953
1954
1955
1956
1957
1958 ExtensionFunction function = position.extensionFunctionArg();
1959 long numArgs = position.intArg(1);
1960 boolean isInitial = position.boolArg(2);
1961
1962 Object[] args = new Object[(int) numArgs];
1963 for (int i = (int) numArgs - 1; i >= 0; i--) {
1964 args[i] = pop();
1965 }
1966
1967 String extensionClassName = function.getExtensionClassName();
1968 JawkExtension extension = extensionInstances.get(extensionClassName);
1969 if (extension == null) {
1970 throw new AwkRuntimeException(
1971 position.lineNumber(),
1972 "Extension instance for class '" + extensionClassName
1973 + "' is not registered");
1974 }
1975 if (!(extension instanceof AbstractExtension)) {
1976 throw new AwkRuntimeException(
1977 position.lineNumber(),
1978 "Extension instance for class '" + extensionClassName
1979 + "' does not extend "
1980 + AbstractExtension.class.getName());
1981 }
1982
1983 Object retval = function.invoke((AbstractExtension) extension, args);
1984
1985
1986
1987
1988 if (isInitial && retval != null && retval instanceof BlockObject) {
1989 retval = new BlockManager().block((BlockObject) retval);
1990 }
1991
1992
1993 if (retval == null) {
1994 retval = "";
1995 } else
1996 if (!(retval instanceof Integer
1997 ||
1998 retval instanceof Long
1999 ||
2000 retval instanceof Double
2001 ||
2002 retval instanceof String
2003 ||
2004 retval instanceof AssocArray
2005 ||
2006 retval instanceof BlockObject)) {
2007
2008
2009 retval = retval.toString();
2010 }
2011 push(retval);
2012
2013 position.next();
2014 break;
2015 }
2016 default:
2017 throw new Error("invalid opcode: " + position.opcode());
2018 }
2019 }
2020
2021
2022 jrt.jrtCloseAll();
2023 } catch (RuntimeException re) {
2024
2025 runtimeStack.popAllFrames();
2026
2027 operandStack.clear();
2028 if (re instanceof AwkSandboxException) {
2029 throw re;
2030 }
2031 throw new AwkRuntimeException(position.lineNumber(), re.getMessage(), re);
2032 } catch (AssertionError ae) {
2033
2034 runtimeStack.popAllFrames();
2035
2036 operandStack.clear();
2037 throw ae;
2038 }
2039
2040
2041 if (throwExitException) {
2042 throw new ExitException(exitCode, "The AWK script requested an exit");
2043 }
2044 }
2045
2046
2047
2048
2049 public void waitForIO() {
2050 jrt.jrtCloseAll();
2051 }
2052
2053 private void printTo(PrintStream ps, long numArgs) {
2054
2055
2056 if (numArgs == 0) {
2057
2058 ps.print(jrt.jrtGetInputField(0));
2059 ps.print(getORS().toString());
2060 } else {
2061
2062
2063 String ofsString = getOFS().toString();
2064
2065
2066 Object[] args = new Object[(int) numArgs];
2067 for (int i = (int) numArgs - 1; i >= 0; i--) {
2068 args[i] = pop();
2069 }
2070
2071
2072 for (int i = 0; i < numArgs; i++) {
2073 ps.print(JRT.toAwkStringForOutput(args[i], getOFMT().toString(), locale));
2074
2075 if (i < numArgs - 1) {
2076
2077 ps.print(ofsString);
2078 }
2079 }
2080 ps.print(getORS().toString());
2081 }
2082
2083
2084 ps.flush();
2085 }
2086
2087 private void printfTo(PrintStream ps, long numArgs) {
2088
2089 ps.print(sprintfFunction(numArgs));
2090
2091 if (IS_WINDOWS) {
2092 ps.flush();
2093 }
2094 }
2095
2096
2097
2098
2099 private String sprintfFunction(long numArgs) {
2100
2101 if (numArgs == 0) {
2102 return "";
2103 }
2104
2105
2106 Object[] argArray = new Object[(int) (numArgs - 1)];
2107
2108
2109
2110
2111 for (int i = (int) numArgs - 2; i >= 0; i--) {
2112 argArray[i] = pop();
2113 }
2114
2115
2116 String fmt = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
2117
2118 if (trapIllegalFormatExceptions) {
2119 return Printf4J.sprintf(locale, fmt, argArray);
2120 } else {
2121 return JRT.sprintfNoCatch(locale, fmt, argArray);
2122 }
2123 }
2124
2125 private StringBuffer replaceFirstSb = new StringBuffer();
2126
2127
2128
2129
2130 private String replaceFirst(String orig, String ere, String repl) {
2131 push(JRT.replaceFirst(orig, repl, ere, replaceFirstSb));
2132 return replaceFirstSb.toString();
2133 }
2134
2135 private StringBuffer replaceAllSb = new StringBuffer();
2136
2137
2138
2139
2140 private String replaceAll(String orig, String ere, String repl) {
2141 push(JRT.replaceAll(orig, repl, ere, replaceAllSb));
2142 return replaceAllSb.toString();
2143 }
2144
2145
2146
2147
2148 private void assign(long l, Object value, boolean isGlobal, PositionTracker position) {
2149
2150 if (runtimeStack.getVariable(l, isGlobal) instanceof AssocArray) {
2151 throw new AwkRuntimeException(position.lineNumber(), "cannot assign anything to an unindexed associative array");
2152 }
2153 push(value);
2154 runtimeStack.setVariable(l, value, isGlobal);
2155 if (l == nfOffset && jrt != null && jrt.hasInputFields()) {
2156 jrt.jrtSetNF(value);
2157 }
2158 }
2159
2160
2161
2162
2163 private void assignArray(long offset, Object arrIdx, Object rhs, boolean isGlobal) {
2164 Object o1 = runtimeStack.getVariable(offset, isGlobal);
2165 if (o1 == null || o1.equals(BLANK)) {
2166 o1 = new AssocArray(sortedArrayKeys);
2167 runtimeStack.setVariable(offset, o1, isGlobal);
2168 }
2169 assert o1 != null;
2170
2171
2172
2173
2174
2175
2176
2177
2178 assert o1 instanceof AssocArray;
2179 AssocArray array = (AssocArray) o1;
2180
2181
2182
2183 array.put(arrIdx, rhs);
2184 push(rhs);
2185 }
2186
2187
2188
2189
2190
2191 private Object inc(long l, boolean isGlobal) {
2192 Object o = runtimeStack.getVariable(l, isGlobal);
2193 if (o == null || o instanceof UninitializedObject) {
2194 o = ZERO;
2195 runtimeStack.setVariable(l, o, isGlobal);
2196 }
2197 runtimeStack.setVariable(l, JRT.inc(o), isGlobal);
2198 return o;
2199 }
2200
2201
2202
2203
2204
2205 private Object dec(long l, boolean isGlobal) {
2206 Object o = runtimeStack.getVariable(l, isGlobal);
2207 if (o == null || o instanceof UninitializedObject) {
2208 o = ZERO;
2209 runtimeStack.setVariable(l, o, isGlobal);
2210 }
2211 runtimeStack.setVariable(l, JRT.dec(o), isGlobal);
2212 return o;
2213 }
2214
2215
2216 @Override
2217 public final Object getRS() {
2218 assert rsOffset != NULL_OFFSET;
2219 Object rsObj = runtimeStack.getVariable(rsOffset, true);
2220 return rsObj;
2221 }
2222
2223
2224 @Override
2225 public final Object getOFS() {
2226 assert ofsOffset != NULL_OFFSET;
2227 Object ofsObj = runtimeStack.getVariable(ofsOffset, true);
2228 return ofsObj;
2229 }
2230
2231 public final Object getORS() {
2232 return runtimeStack.getVariable(orsOffset, true);
2233 }
2234
2235
2236 @Override
2237 public final Object getSUBSEP() {
2238 assert subsepOffset != NULL_OFFSET;
2239 Object subsepObj = runtimeStack.getVariable(subsepOffset, true);
2240 return subsepObj;
2241 }
2242
2243
2244
2245
2246
2247
2248
2249
2250 @SuppressWarnings("unused")
2251 private void setFilelistVariable(String nameValue) {
2252 int eqIdx = nameValue.indexOf('=');
2253
2254 assert eqIdx >= 0;
2255 if (eqIdx == 0) {
2256 throw new IllegalArgumentException(
2257 "Must have a non-blank variable name in a name=value variable assignment argument.");
2258 }
2259 String name = nameValue.substring(0, eqIdx);
2260 String value = nameValue.substring(eqIdx + 1);
2261 Object obj;
2262 try {
2263 obj = Integer.parseInt(value);
2264 } catch (NumberFormatException nfe) {
2265 try {
2266 obj = Double.parseDouble(value);
2267 } catch (NumberFormatException nfe2) {
2268 obj = value;
2269 }
2270 }
2271
2272
2273 if (functionNames.contains(name)) {
2274 throw new IllegalArgumentException("Cannot assign a scalar to a function name (" + name + ").");
2275 }
2276
2277 Integer offsetObj = globalVariableOffsets.get(name);
2278 Boolean arrayObj = globalVariableArrays.get(name);
2279
2280 if (offsetObj != null) {
2281 assert arrayObj != null;
2282 if (arrayObj.booleanValue()) {
2283 throw new IllegalArgumentException("Cannot assign a scalar to a non-scalar variable (" + name + ").");
2284 } else {
2285 runtimeStack.setFilelistVariable(offsetObj.intValue(), obj);
2286 }
2287 }
2288
2289 }
2290
2291
2292 @Override
2293 public final void assignVariable(String name, Object obj) {
2294
2295 if (functionNames.contains(name)) {
2296 throw new IllegalArgumentException("Cannot assign a scalar to a function name (" + name + ").");
2297 }
2298
2299 Integer offsetObj = globalVariableOffsets.get(name);
2300 Boolean arrayObj = globalVariableArrays.get(name);
2301
2302 if (offsetObj != null) {
2303 assert arrayObj != null;
2304 if (arrayObj.booleanValue()) {
2305 throw new IllegalArgumentException("Cannot assign a scalar to a non-scalar variable (" + name + ").");
2306 } else {
2307 runtimeStack.setFilelistVariable(offsetObj.intValue(), obj);
2308 }
2309 }
2310 }
2311
2312 private void swapOnStack() {
2313 Object o1 = pop();
2314 Object o2 = pop();
2315 push(o1);
2316 push(o2);
2317 }
2318
2319 private void avmConsumeInputForGetline() throws IOException {
2320 if (avmConsumeInput(true)) {
2321 push(1);
2322 } else {
2323 push("");
2324 push(0);
2325 }
2326 swapOnStack();
2327 }
2328
2329 private void avmConsumeFileInputForGetline(String filename) throws IOException {
2330 if (avmConsumeFileInput(filename)) {
2331 push(1);
2332 } else {
2333 push(0);
2334 }
2335 swapOnStack();
2336 }
2337
2338 private void avmConsumeCommandInputForGetline(String cmd) throws IOException {
2339 if (avmConsumeCommandInput(cmd)) {
2340 push(1);
2341 } else {
2342 push(0);
2343 }
2344 swapOnStack();
2345 }
2346
2347 private boolean avmConsumeFileInput(String filename) throws IOException {
2348 boolean retval = jrt.jrtConsumeFileInput(filename);
2349 if (retval) {
2350 push(jrt.getInputLine());
2351 } else {
2352 push("");
2353 }
2354 return retval;
2355 }
2356
2357 private boolean avmConsumeCommandInput(String cmd) throws IOException {
2358 boolean retval = jrt.jrtConsumeCommandInput(cmd);
2359 if (retval) {
2360 push(jrt.getInputLine());
2361 } else {
2362 push("");
2363 }
2364 return retval;
2365 }
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376 private boolean avmConsumeInput(boolean forGetline) throws IOException {
2377 boolean retval = jrt.consumeInput(settings.getInput(), forGetline, locale);
2378 if (retval && forGetline) {
2379 push(jrt.getInputLine());
2380 }
2381 return retval;
2382 }
2383
2384
2385 @Override
2386 public Object getFS() {
2387 assert fsOffset != NULL_OFFSET;
2388 Object fsString = runtimeStack.getVariable(fsOffset, true);
2389 return fsString;
2390 }
2391
2392
2393 @Override
2394 public Object getCONVFMT() {
2395 assert convfmtOffset != NULL_OFFSET : "convfmtOffset not defined";
2396 Object convfmtString = runtimeStack.getVariable(convfmtOffset, true);
2397 return convfmtString;
2398 }
2399
2400
2401 @Override
2402 public void resetFNR() {
2403 runtimeStack.setVariable(fnrOffset, ZERO, true);
2404 }
2405
2406
2407 @Override
2408 public void incFNR() {
2409 inc(fnrOffset, true);
2410 }
2411
2412
2413 @Override
2414 public void incNR() {
2415 inc(nrOffset, true);
2416 }
2417
2418
2419 @Override
2420 public void setNF(Integer newNf) {
2421 runtimeStack.setVariable(nfOffset, newNf, true);
2422 }
2423
2424
2425 @Override
2426 public void setFILENAME(String filename) {
2427 runtimeStack.setVariable(filenameOffset, filename, true);
2428 }
2429
2430
2431 @Override
2432 public Object getARGV() {
2433 return runtimeStack.getVariable(argvOffset, true);
2434 }
2435
2436
2437 @Override
2438 public Object getARGC() {
2439 return runtimeStack.getVariable(argcOffset, true);
2440 }
2441
2442 private String getOFMT() {
2443 assert ofmtOffset != NULL_OFFSET;
2444 String ofmtString = runtimeStack.getVariable(ofmtOffset, true).toString();
2445 return ofmtString;
2446 }
2447
2448 private static final UninitializedObject BLANK = new UninitializedObject();
2449
2450
2451
2452
2453 public static final int NULL_OFFSET = -1;
2454
2455 }