From ee8ca9f028829cfa7e717442c7807fe4b29bc0f2 Mon Sep 17 00:00:00 2001 From: Bertrand Martin Date: Mon, 11 May 2026 18:46:25 +0200 Subject: [PATCH 1/3] Reduce executeTuples bytecode for tuple refactor --- src/main/java/io/jawk/backend/AVM.java | 728 ++++++++------- .../java/io/jawk/intermediate/AwkTuples.java | 374 ++++---- .../io/jawk/intermediate/PositionTracker.java | 131 +-- src/main/java/io/jawk/intermediate/Tuple.java | 837 ++++++++++++++---- .../io/jawk/AwkTupleOptimizationTest.java | 28 +- 5 files changed, 1250 insertions(+), 848 deletions(-) diff --git a/src/main/java/io/jawk/backend/AVM.java b/src/main/java/io/jawk/backend/AVM.java index d84a815c..9897f387 100644 --- a/src/main/java/io/jawk/backend/AVM.java +++ b/src/main/java/io/jawk/backend/AVM.java @@ -54,6 +54,22 @@ import io.jawk.intermediate.Address; import io.jawk.intermediate.Opcode; import io.jawk.intermediate.PositionTracker; +import io.jawk.intermediate.Tuple; +import io.jawk.intermediate.Tuple.BooleanTuple; +import io.jawk.intermediate.Tuple.CallFunctionTuple; +import io.jawk.intermediate.Tuple.ClassTuple; +import io.jawk.intermediate.Tuple.CountAndAppendTuple; +import io.jawk.intermediate.Tuple.CountTuple; +import io.jawk.intermediate.Tuple.DereferenceTuple; +import io.jawk.intermediate.Tuple.ExtensionTuple; +import io.jawk.intermediate.Tuple.InputFieldTuple; +import io.jawk.intermediate.Tuple.LongTuple; +import io.jawk.intermediate.Tuple.PushDoubleTuple; +import io.jawk.intermediate.Tuple.PushLongTuple; +import io.jawk.intermediate.Tuple.PushStringTuple; +import io.jawk.intermediate.Tuple.RegexTuple; +import io.jawk.intermediate.Tuple.SubstitutionVariableTuple; +import io.jawk.intermediate.Tuple.VariableTuple; import io.jawk.intermediate.UninitializedObject; import io.jawk.jrt.AssocArray; import io.jawk.jrt.AwkRuntimeException; @@ -1008,96 +1024,40 @@ private void executeTuples(PositionTracker position) try { while (!position.isEOF()) { // System_out.println("--> "+position); - opcode = position.opcode(); + Tuple tuple = position.current(); + opcode = tuple.getOpcode(); if (profiling) { - tupleStartNanos = beforeProfiledTuple(position, opcode); + tupleStartNanos = beforeProfiledTuple(tuple, opcode); } // switch on OPCODE switch (opcode) { case PRINT: { - // arg[0] = # of items to print on the stack - // stack[0] = item 1 - // stack[1] = item 2 - // etc. - long numArgs = position.intArg(0); - jrt.printDefault(numArgs == 0 ? new Object[] { jrt.jrtGetInputField(0) } : popArguments(numArgs)); + execPrint((CountTuple) tuple); position.next(); break; } case PRINT_TO_FILE: { - // arg[0] = # of items to print on the stack - // arg[1] = true=append, false=overwrite - // stack[0] = output filename - // stack[1] = item 1 - // stack[2] = item 2 - // etc. - boolean append = position.boolArg(1); - String key = jrt.toAwkString(pop()); - long numArgs = position.intArg(0); - jrt - .printToFile( - key, - append, - numArgs == 0 ? new Object[] - { jrt.jrtGetInputField(0) } : popArguments(numArgs)); + execPrintToFile((CountAndAppendTuple) tuple); position.next(); break; } case PRINT_TO_PIPE: { - // arg[0] = # of items to print on the stack - // stack[0] = command to execute - // stack[1] = item 1 - // stack[2] = item 2 - // etc. - String cmd = jrt.toAwkString(pop()); - long numArgs = position.intArg(0); - jrt - .printToProcess( - cmd, - numArgs == 0 ? new Object[] - { jrt.jrtGetInputField(0) } : popArguments(numArgs)); + execPrintToPipe((CountTuple) tuple); position.next(); break; } case PRINTF: { - // arg[0] = # of items to print on the stack (includes format string) - // stack[0] = format string - // stack[1] = item 1 - // etc. - long numArgs = position.intArg(0); - Object[] values = popArguments(numArgs - 1); - String format = jrt.toAwkString(pop()); - jrt.printfDefault(format, values); + execPrintf((CountTuple) tuple); position.next(); break; } case PRINTF_TO_FILE: { - // arg[0] = # of items to print on the stack (includes format string) - // arg[1] = true=append, false=overwrite - // stack[0] = output filename - // stack[1] = format string - // stack[2] = item 1 - // etc. - boolean append = position.boolArg(1); - String key = jrt.toAwkString(pop()); - long numArgs = position.intArg(0); - Object[] values = popArguments(numArgs - 1); - String format = jrt.toAwkString(pop()); - jrt.printfToFile(key, append, format, values); + execPrintfToFile((CountAndAppendTuple) tuple); position.next(); break; } case PRINTF_TO_PIPE: { - // arg[0] = # of items to print on the stack (includes format string) - // stack[0] = command to execute - // stack[1] = format string - // stack[2] = item 1 - // etc. - String cmd = jrt.toAwkString(pop()); - long numArgs = position.intArg(0); - Object[] values = popArguments(numArgs - 1); - String format = jrt.toAwkString(pop()); - jrt.printfToProcess(cmd, format, values); + execPrintfToPipe((CountTuple) tuple); position.next(); break; } @@ -1106,47 +1066,35 @@ private void executeTuples(PositionTracker position) // stack[0] = arg1 (format string) // stack[1] = arg2 // etc. - long numArgs = position.intArg(0); + CountTuple countTuple = (CountTuple) tuple; + long numArgs = countTuple.getCount(); push(sprintfFunction(numArgs)); position.next(); break; } case LENGTH: { - // arg[0] = 0==use $0, otherwise, use the stack element - // stack[0] = element to measure (only if arg[0] != 0) - - // print items from the top of the stack - // # of items - long num = position.intArg(0); - if (num == 0) { - // display $0 - push(jrt.jrtGetInputField(0).toString().length()); - } else { - Object value = pop(); - if (value instanceof Map) { - push((long) ((Map) value).size()); - } else { - push(jrt.toAwkString(value).length()); - } - } + execLength((CountTuple) tuple); position.next(); break; } case PUSH_LONG: { // arg[0] = long constant to push onto the stack - push(position.intArg(0)); + PushLongTuple pushTuple = (PushLongTuple) tuple; + push(pushTuple.getValue()); position.next(); break; } case PUSH_DOUBLE: { // arg[0] = double constant to push onto the stack - push(position.doubleArg(0)); + PushDoubleTuple pushTuple = (PushDoubleTuple) tuple; + push(pushTuple.getValue()); position.next(); break; } case PUSH_STRING: { // arg[0] = string constant to push onto the stack - push(position.stringArg(0)); + PushStringTuple pushTuple = (PushStringTuple) tuple; + push(pushTuple.getValue()); position.next(); break; } @@ -1165,7 +1113,7 @@ private void executeTuples(PositionTracker position) // if String, then check for "" or double value of "0" boolean jump = !jrt.toBoolean(pop()); if (jump) { - position.jump(position.addressArg()); + position.jump(tuple.getAddress()); } else { position.next(); } @@ -1191,7 +1139,7 @@ private void executeTuples(PositionTracker position) // if String, then check for "" or double value of "0" boolean jump = jrt.toBoolean(pop()); if (jump) { - position.jump(position.addressArg()); + position.jump(tuple.getAddress()); } else { position.next(); } @@ -1238,7 +1186,7 @@ private void executeTuples(PositionTracker position) case GOTO: { // arg[0] = address - position.jump(position.addressArg()); + position.jump(tuple.getAddress()); break; } case NOP: { @@ -1260,9 +1208,9 @@ private void executeTuples(PositionTracker position) // arg[0] = offset // arg[1] = isGlobal // stack[0] = value + VariableTuple variableTuple = (VariableTuple) tuple; Object value = pop(); - boolean isGlobal = position.boolArg(1); - assign(position.intArg(0), value, isGlobal, position); + assign(variableTuple.getVariableOffset(), value, variableTuple.isGlobal(), position); position.next(); break; } @@ -1276,8 +1224,9 @@ private void executeTuples(PositionTracker position) if (rhs == null) { rhs = BLANK; } - long offset = position.intArg(0); - boolean isGlobal = position.boolArg(1); + VariableTuple variableTuple = (VariableTuple) tuple; + long offset = variableTuple.getVariableOffset(); + boolean isGlobal = variableTuple.isGlobal(); assignArray(offset, arrIdx, rhs, isGlobal); position.next(); break; @@ -1311,8 +1260,9 @@ private void executeTuples(PositionTracker position) if (rhs == null) { rhs = BLANK; } - long offset = position.intArg(0); - boolean isGlobal = position.boolArg(1); + VariableTuple variableTuple = (VariableTuple) tuple; + long offset = variableTuple.getVariableOffset(); + boolean isGlobal = variableTuple.isGlobal(); double val = JRT.toDouble(rhs); @@ -1441,8 +1391,10 @@ private void executeTuples(PositionTracker position) // arg[0] = offset // arg[1] = isGlobal // stack[0] = value - boolean isGlobal = position.boolArg(1); - Object o1 = runtimeStack.getVariable(position.intArg(0), isGlobal); + VariableTuple variableTuple = (VariableTuple) tuple; + long offset = variableTuple.getVariableOffset(); + boolean isGlobal = variableTuple.isGlobal(); + Object o1 = runtimeStack.getVariable(offset, isGlobal); if (o1 == null) { o1 = BLANK; } @@ -1475,10 +1427,10 @@ private void executeTuples(PositionTracker position) if (JRT.isActuallyLong(ans)) { long integral = (long) Math.rint(ans); push(integral); - runtimeStack.setVariable(position.intArg(0), integral, isGlobal); + runtimeStack.setVariable(offset, integral, isGlobal); } else { push(ans); - runtimeStack.setVariable(position.intArg(0), ans, isGlobal); + runtimeStack.setVariable(offset, ans, isGlobal); } position.next(); break; @@ -1532,14 +1484,16 @@ private void executeTuples(PositionTracker position) case INC: { // arg[0] = offset // arg[1] = isGlobal - inc(position.intArg(0), position.boolArg(1)); + VariableTuple variableTuple = (VariableTuple) tuple; + inc(variableTuple.getVariableOffset(), variableTuple.isGlobal()); position.next(); break; } case DEC: { // arg[0] = offset // arg[1] = isGlobal - dec(position.intArg(0), position.boolArg(1)); + VariableTuple variableTuple = (VariableTuple) tuple; + dec(variableTuple.getVariableOffset(), variableTuple.isGlobal()); position.next(); break; } @@ -1547,7 +1501,8 @@ private void executeTuples(PositionTracker position) // arg[0] = offset // arg[1] = isGlobal pop(); - push(inc(position.intArg(0), position.boolArg(1))); + VariableTuple variableTuple = (VariableTuple) tuple; + push(inc(variableTuple.getVariableOffset(), variableTuple.isGlobal())); position.next(); break; } @@ -1555,7 +1510,8 @@ private void executeTuples(PositionTracker position) // arg[0] = offset // arg[1] = isGlobal pop(); - push(dec(position.intArg(0), position.boolArg(1))); + VariableTuple variableTuple = (VariableTuple) tuple; + push(dec(variableTuple.getVariableOffset(), variableTuple.isGlobal())); position.next(); break; } @@ -1563,8 +1519,9 @@ private void executeTuples(PositionTracker position) // arg[0] = offset // arg[1] = isGlobal // stack[0] = array index - boolean isGlobal = position.boolArg(1); - Map aa = ensureMapVariable(position.intArg(0), isGlobal); + VariableTuple variableTuple = (VariableTuple) tuple; + boolean isGlobal = variableTuple.isGlobal(); + Map aa = ensureMapVariable(variableTuple.getVariableOffset(), isGlobal); Object key = pop(); checkScalar(key); Object o = aa.get(key); @@ -1581,8 +1538,9 @@ private void executeTuples(PositionTracker position) // arg[0] = offset // arg[1] = isGlobal // stack[0] = array index - boolean isGlobal = position.boolArg(1); - Map aa = ensureMapVariable(position.intArg(0), isGlobal); + VariableTuple variableTuple = (VariableTuple) tuple; + boolean isGlobal = variableTuple.isGlobal(); + Map aa = ensureMapVariable(variableTuple.getVariableOffset(), isGlobal); Object key = pop(); checkScalar(key); Object o = aa.get(key); @@ -1667,14 +1625,16 @@ private void executeTuples(PositionTracker position) case DEREFERENCE: { // arg[0] = offset // arg[1] = isGlobal - boolean isGlobal = position.boolArg(2); - Object o = runtimeStack.getVariable(position.intArg(0), isGlobal); + DereferenceTuple dereferenceTuple = (DereferenceTuple) tuple; + boolean isGlobal = dereferenceTuple.isGlobal(); + long offset = dereferenceTuple.getVariableOffset(); + Object o = runtimeStack.getVariable(offset, isGlobal); if (o == null) { - if (position.boolArg(1)) { + if (dereferenceTuple.isArray()) { // is_array - push(runtimeStack.setVariable(position.intArg(0), newAwkArray(), isGlobal)); + push(runtimeStack.setVariable(offset, newAwkArray(), isGlobal)); } else { - push(runtimeStack.setVariable(position.intArg(0), BLANK, isGlobal)); + push(runtimeStack.setVariable(offset, BLANK, isGlobal)); } } else { push(o); @@ -1718,7 +1678,8 @@ private void executeTuples(PositionTracker position) case SRAND: { // arg[0] = numArgs (where 0 = no args, anything else = one argument) // stack[0] = seed (only if numArgs != 0) - long numArgs = position.intArg(0); + CountTuple countTuple = (CountTuple) tuple; + long numArgs = countTuple.getCount(); int seed; if (numArgs == 0) { // use the time of day for the seed @@ -1796,37 +1757,7 @@ private void executeTuples(PositionTracker position) break; } case MATCH: { - // stack[0] = 2nd arg to match() function - // stack[1] = 1st arg to match() function - String ere = jrt.toAwkString(pop()); - String s = jrt.toAwkString(pop()); - - // check if IGNORECASE set - int flags = 0; - - if (globalVariableOffsets.containsKey("IGNORECASE")) { - Integer offsetObj = globalVariableOffsets.get("IGNORECASE"); - Object ignorecase = runtimeStack.getVariable(offsetObj, true); - - if (JRT.toDouble(ignorecase) != 0) { - flags |= Pattern.CASE_INSENSITIVE; - } - } - - Pattern pattern = Pattern.compile(ere, flags); - Matcher matcher = pattern.matcher(s); - boolean result = matcher.find(); - if (result) { - int start = matcher.start() + 1; - int len = matcher.end() - matcher.start(); - jrt.setRSTART(start); - jrt.setRLENGTH(len); - push(start); - } else { - jrt.setRSTART(0); - jrt.setRLENGTH(-1); - push(0); - } + execMatch(); position.next(); break; } @@ -1840,174 +1771,37 @@ private void executeTuples(PositionTracker position) break; } case SUB_FOR_DOLLAR_0: { - // arg[0] = isGlobal - // stack[0] = replacement string - // stack[1] = ere - boolean isGsub = position.boolArg(0); - String repl = jrt.toAwkString(pop()); - String ere = jrt.toAwkString(pop()); - String orig = jrt.toAwkString(jrt.jrtGetInputField(0)); - String newstring; - if (isGsub) { - newstring = replaceAll(orig, ere, repl); - } else { - newstring = replaceFirst(orig, ere, repl); - } - // assign it to "$0" - jrt.setInputLine(newstring); - jrt.jrtParseFields(); + execSubForDollar0((BooleanTuple) tuple); position.next(); break; } case SUB_FOR_DOLLAR_REFERENCE: { - // arg[0] = isGlobal - // stack[0] = field num - // stack[1] = original field value - // stack[2] = replacement string - // stack[3] = ere - boolean isGsub = position.boolArg(0); - long fieldNum = JRT.parseFieldNumber(pop()); - String orig = jrt.toAwkString(pop()); - String repl = jrt.toAwkString(pop()); - String ere = jrt.toAwkString(pop()); - String newstring; - if (isGsub) { - newstring = replaceAll(orig, ere, repl); - } else { - newstring = replaceFirst(orig, ere, repl); - } - // assign it to "$0" - if (fieldNum == 0) { - jrt.setInputLine(newstring); - jrt.jrtParseFields(); - } else { - jrt.jrtSetInputField(newstring, fieldNum); - } + execSubForDollarReference((BooleanTuple) tuple); position.next(); break; } case SUB_FOR_VARIABLE: { - // arg[0] = offset - // arg[1] = isGlobal - // arg[2] = isGsub - // stack[0] = original variable value - // stack[1] = replacement string - // stack[2] = ere - long offset = position.intArg(0); - boolean isGlobal = position.boolArg(1); - String newString = execSubOrGSub(position, 2); - // assign it to "offset/global" - assign(offset, newString, isGlobal, position); - pop(); + execSubForVariable((SubstitutionVariableTuple) tuple, position); position.next(); break; } case SUB_FOR_ARRAY_REFERENCE: { - // arg[0] = offset - // arg[1] = isGlobal - // arg[2] = isGsub - // stack[0] = original variable value - // stack[1] = replacement string - // stack[2] = ere - // stack[3] = array index - // ARRAY reference offset/isGlobal - long offset = position.intArg(0); - boolean isGlobal = position.boolArg(1); - Object arrIdx = pop(); - String newString = execSubOrGSub(position, 2); - // assign it to "offset/arrIdx/global" - assignArray(offset, arrIdx, newString, isGlobal); - pop(); + execSubForArrayReference((SubstitutionVariableTuple) tuple); position.next(); break; } case SUB_FOR_MAP_REFERENCE: { - // arg[0] = isGsub - // stack[0] = array index - // stack[1] = associative array - // stack[2] = original variable value - // stack[3] = replacement string - // stack[4] = ere - Object arrIdx = pop(); - Map array = toMap(pop()); - String newString = execSubOrGSub(position, 0); - assignMapElement(array, arrIdx, newString); - pop(); + execSubForMapReference((BooleanTuple) tuple); position.next(); break; } case SPLIT: { - // arg[0] = num args - // stack[0] = field_sep (only if num args == 3) - // stack[1] = array - // stack[2] = string - long numArgs = position.intArg(0); - String fsString; - if (numArgs == 2) { - fsString = jrt.toAwkString(jrt.getFSVar()); - } else if (numArgs == 3) { - fsString = jrt.toAwkString(pop()); - } else { - throw new Error("Invalid # of args. split() requires 2 or 3. Got: " + numArgs); - } - Object o = pop(); - if (!(o instanceof Map)) { - throw new AwkRuntimeException(position.lineNumber(), o + " is not an array."); - } - String s = jrt.toAwkString(pop()); - Enumeration tokenizer; - if (fsString.equals(" ")) { - tokenizer = new StringTokenizer(s); - } else if (fsString.length() == 1) { - tokenizer = new SingleCharacterTokenizer(s, fsString.charAt(0)); - } else if (fsString.isEmpty()) { - tokenizer = new CharacterTokenizer(s); - } else { - tokenizer = new RegexTokenizer(s, fsString); - } - - @SuppressWarnings("unchecked") - Map assocArray = (Map) o; - assocArray.clear(); - long cnt = 0; - while (tokenizer.hasMoreElements()) { - assocArray.put(++cnt, tokenizer.nextElement()); - } - push(cnt); + execSplit((CountTuple) tuple, position); position.next(); break; } case SUBSTR: { - // arg[0] = num args - // stack[0] = length (only if num args == 3) - // stack[1] = start pos - // stack[2] = string - long numArgs = position.intArg(0); - int startPos, length; - String s; - if (numArgs == 3) { - length = (int) JRT.toLong(pop()); - startPos = (int) JRT.toDouble(pop()); - s = jrt.toAwkString(pop()); - } else if (numArgs == 2) { - startPos = (int) JRT.toDouble(pop()); - s = jrt.toAwkString(pop()); - length = s.length() - startPos + 1; - } else { - throw new Error("numArgs for SUBSTR must be 2 or 3. It is " + numArgs); - } - if (startPos <= 0) { - startPos = 1; - } - if (length <= 0 || startPos > s.length()) { - push(BLANK); - } else { - if (startPos + length > s.length()) { - push(s.substring(startPos - 1)); - } else { - push(s.substring(startPos - 1, startPos + length - 1)); - } - } + execSubstr((CountTuple) tuple); position.next(); break; } @@ -2223,7 +2017,7 @@ private void executeTuples(PositionTracker position) } Deque keylist = (Deque) o; if (keylist.isEmpty()) { - position.jump(position.addressArg()); + position.jump(tuple.getAddress()); } else { position.next(); } @@ -2246,12 +2040,13 @@ private void executeTuples(PositionTracker position) case CHECK_CLASS: { // arg[0] = class object // stack[0] = item to check + ClassTuple checkTuple = (ClassTuple) tuple; Object o = pop(); - if (!position.classArg().isInstance(o)) { + if (!checkTuple.getType().isInstance(o)) { throw new AwkRuntimeException( position.lineNumber(), "Verification failed. Top-of-stack = " + o.getClass() + " isn't an instance of " - + position.classArg()); + + checkTuple.getType()); } push(o); position.next(); @@ -2264,7 +2059,7 @@ private void executeTuples(PositionTracker position) if (jrt.consumeInput(resolvedInputSource)) { position.next(); } else { - position.jump(position.addressArg()); + position.jump(tuple.getAddress()); } break; } @@ -2317,7 +2112,8 @@ private void executeTuples(PositionTracker position) case ENVIRON_OFFSET: { // stack[0] = offset //// assignArray(offset, arrIdx, newstring, isGlobal); - environOffset = position.intArg(0); + LongTuple offsetTuple = (LongTuple) tuple; + environOffset = offsetTuple.getValue(); // set the initial variables Map env = System.getenv(); for (Map.Entry var : env.entrySet()) { @@ -2329,7 +2125,8 @@ private void executeTuples(PositionTracker position) } case ARGC_OFFSET: { // stack[0] = offset - argcOffset = position.intArg(0); + LongTuple offsetTuple = (LongTuple) tuple; + argcOffset = offsetTuple.getValue(); // assign(argcOffset, arguments.size(), true, position); // true = global // +1 to include the "jawk" program name (ARGV[0]) assign(argcOffset, arguments.size() + 1, true, position); // true = global @@ -2339,7 +2136,8 @@ private void executeTuples(PositionTracker position) } case ARGV_OFFSET: { // stack[0] = offset - argvOffset = position.intArg(0); + LongTuple offsetTuple = (LongTuple) tuple; + argvOffset = offsetTuple.getValue(); // consume argv (looping from 1 to argc) int argc = (int) JRT.toDouble(runtimeStack.getVariable(argcOffset, true)); // true = global assignArray(argvOffset, 0, "jawk", true); @@ -2360,7 +2158,8 @@ private void executeTuples(PositionTracker position) break; } case GET_INPUT_FIELD_CONST: { - long fieldnum = position.intArg(0); + InputFieldTuple inputFieldTuple = (InputFieldTuple) tuple; + long fieldnum = inputFieldTuple.getFieldIndex(); push(jrt.jrtGetInputField(fieldnum)); position.next(); break; @@ -2380,11 +2179,11 @@ private void executeTuples(PositionTracker position) // ... // stack[n-1] = first actual parameter // etc. - Address funcAddr = position.addressArg(); - // String func_name = position.arg(1).toString(); - long numFormalParams = position.intArg(2); - long numActualParams = position.intArg(3); - runtimeStack.pushFrame(numFormalParams, position.current()); + CallFunctionTuple callTuple = (CallFunctionTuple) tuple; + Address funcAddr = callTuple.getAddress(); + long numFormalParams = callTuple.getNumFormalParams(); + long numActualParams = callTuple.getNumActualParams(); + runtimeStack.pushFrame(numFormalParams, position.currentIndex()); // Arguments are stacked, so first in the stack is the last for the function for (long i = numActualParams - 1; i >= 0; i--) { runtimeStack.setVariable(i, pop(), false); // false = local @@ -2414,29 +2213,7 @@ private void executeTuples(PositionTracker position) break; } case SET_NUM_GLOBALS: { - // arg[0] = # of globals - Object[] globals = runtimeStack.getNumGlobals(); - if (mergedGlobalLayoutActive) { - if (!hasCompatiblePersistentGlobalLayout(position.intArg(0))) { - throw new IllegalStateException( - "AVM globals are already initialized for an incompatible persistent layout."); - } - applyExecutionInitialVariablesToGlobalSlots(true); - } else if (globals == null) { - runtimeStack.setNumGlobals(position.intArg(0), globalVariableOffsets); - initializedEvalGlobalVariableOffsets = globalVariableOffsets; - initializedEvalGlobalVariableArrays = globalVariableArrays; - - // now that we have the global variable size, - // we can allocate the initial variables - - // assign -v variables and per-call overrides prepared for this execution - applyExecutionInitialVariablesToGlobalSlots(false); - } else if (!hasCompatibleEvalGlobalLayout(position.intArg(0))) { - throw new IllegalStateException( - "AVM globals are already initialized for a different eval layout. Call prepareForEval(...) first."); - } - + execSetNumGlobals((CountTuple) tuple); position.next(); break; } @@ -2448,30 +2225,7 @@ private void executeTuples(PositionTracker position) break; } case APPLY_SUBSEP: { - // arg[0] = # of elements for SUBSEP application - // stack[0] = first element - // stack[1] = second element - // etc. - long count = position.intArg(0); - // String s; - if (count == 1) { - Object value = pop(); - checkScalar(value); - push(jrt.toAwkString(value)); - } else { - StringBuilder sb = new StringBuilder(); - Object value = pop(); - checkScalar(value); - sb.append(jrt.toAwkString(value)); - String subsep = jrt.toAwkString(jrt.getSUBSEPVar()); - for (int i = 1; i < count; i++) { - sb.insert(0, subsep); - value = pop(); - checkScalar(value); - sb.insert(0, jrt.toAwkString(value)); - } - push(sb.toString()); - } + execApplySubsep((CountTuple) tuple); position.next(); break; } @@ -2479,8 +2233,9 @@ private void executeTuples(PositionTracker position) // arg[0] = offset // arg[1] = isGlobal // stack[0] = array index - long offset = position.intArg(0); - boolean isGlobal = position.boolArg(1); + VariableTuple variableTuple = (VariableTuple) tuple; + long offset = variableTuple.getVariableOffset(); + boolean isGlobal = variableTuple.isGlobal(); Map aa = getMapVariable(offset, isGlobal); Object key = pop(); checkScalar(key); @@ -2504,8 +2259,9 @@ private void executeTuples(PositionTracker position) // arg[0] = offset // arg[1] = isGlobal // (nothing on the stack) - long offset = position.intArg(0); - boolean isGlobal = position.boolArg(1); + VariableTuple variableTuple = (VariableTuple) tuple; + long offset = variableTuple.getVariableOffset(); + boolean isGlobal = variableTuple.isGlobal(); Map array = getMapVariable(offset, isGlobal); if (array != null) { array.clear(); @@ -2515,13 +2271,14 @@ private void executeTuples(PositionTracker position) } case SET_EXIT_ADDRESS: { // arg[0] = exit address - exitAddress = position.addressArg(); + exitAddress = tuple.getAddress(); position.next(); break; } case SET_WITHIN_END_BLOCKS: { // arg[0] = whether within the END blocks section - withinEndBlocks = position.boolArg(0); + BooleanTuple endBlocksTuple = (BooleanTuple) tuple; + withinEndBlocks = endBlocksTuple.getValue(); position.next(); break; } @@ -2551,7 +2308,8 @@ private void executeTuples(PositionTracker position) } case REGEXP: { // Literal regex tuples must provide a precompiled Pattern as arg[1] - Pattern pattern = position.patternArg(1); + RegexTuple regexTuple = (RegexTuple) tuple; + Pattern pattern = regexTuple.getPattern(); push(pattern); position.next(); break; @@ -2562,10 +2320,11 @@ private void executeTuples(PositionTracker position) if (conditionPairs == null) { conditionPairs = new HashMap(); } - ConditionPair cp = conditionPairs.get(position.current()); + int currentIndex = position.currentIndex(); + ConditionPair cp = conditionPairs.get(currentIndex); if (cp == null) { cp = new ConditionPair(); - conditionPairs.put(position.current(), cp); + conditionPairs.put(currentIndex, cp); } boolean end = jrt.toBoolean(pop()); boolean start = jrt.toBoolean(pop()); @@ -2609,9 +2368,10 @@ private void executeTuples(PositionTracker position) // stack[0] = first actual parameter // stack[1] = second actual parameter // etc. - ExtensionFunction function = position.extensionFunctionArg(); - long numArgs = position.intArg(1); - boolean isInitial = position.boolArg(2); + ExtensionTuple extensionTuple = (ExtensionTuple) tuple; + ExtensionFunction function = extensionTuple.getFunction(); + long numArgs = extensionTuple.getArgCount(); + boolean isInitial = extensionTuple.isInitial(); Object[] args = new Object[(int) numArgs]; for (int i = (int) numArgs - 1; i >= 0; i--) { @@ -2908,12 +2668,250 @@ public ProfilingReport getProfilingReport() { return new ProfilingReport(tupleProfilingStats, functionProfilingStats); } - private long beforeProfiledTuple(PositionTracker position, Opcode opcode) { + private void execPrint(CountTuple tuple) throws IOException { + long numArgs = tuple.getCount(); + jrt.printDefault(numArgs == 0 ? new Object[] { jrt.jrtGetInputField(0) } : popArguments(numArgs)); + } + + private void execPrintToFile(CountAndAppendTuple tuple) throws IOException { + String key = jrt.toAwkString(pop()); + long numArgs = tuple.getCount(); + jrt + .printToFile( + key, + tuple.isAppend(), + numArgs == 0 ? new Object[] + { jrt.jrtGetInputField(0) } : popArguments(numArgs)); + } + + private void execPrintToPipe(CountTuple tuple) throws IOException { + String cmd = jrt.toAwkString(pop()); + long numArgs = tuple.getCount(); + jrt.printToProcess(cmd, numArgs == 0 ? new Object[] { jrt.jrtGetInputField(0) } : popArguments(numArgs)); + } + + private void execPrintf(CountTuple tuple) throws IOException { + long numArgs = tuple.getCount(); + Object[] values = popArguments(numArgs - 1); + String format = jrt.toAwkString(pop()); + jrt.printfDefault(format, values); + } + + private void execPrintfToFile(CountAndAppendTuple tuple) throws IOException { + String key = jrt.toAwkString(pop()); + long numArgs = tuple.getCount(); + Object[] values = popArguments(numArgs - 1); + String format = jrt.toAwkString(pop()); + jrt.printfToFile(key, tuple.isAppend(), format, values); + } + + private void execPrintfToPipe(CountTuple tuple) throws IOException { + String cmd = jrt.toAwkString(pop()); + long numArgs = tuple.getCount(); + Object[] values = popArguments(numArgs - 1); + String format = jrt.toAwkString(pop()); + jrt.printfToProcess(cmd, format, values); + } + + private void execLength(CountTuple tuple) { + long num = tuple.getCount(); + if (num == 0) { + push(jrt.jrtGetInputField(0).toString().length()); + return; + } + Object value = pop(); + if (value instanceof Map) { + push((long) ((Map) value).size()); + } else { + push(jrt.toAwkString(value).length()); + } + } + + private void execMatch() { + String ere = jrt.toAwkString(pop()); + String s = jrt.toAwkString(pop()); + int flags = 0; + if (globalVariableOffsets.containsKey("IGNORECASE")) { + Integer offsetObj = globalVariableOffsets.get("IGNORECASE"); + Object ignorecase = runtimeStack.getVariable(offsetObj, true); + if (JRT.toDouble(ignorecase) != 0) { + flags |= Pattern.CASE_INSENSITIVE; + } + } + Pattern pattern = Pattern.compile(ere, flags); + Matcher matcher = pattern.matcher(s); + if (matcher.find()) { + int start = matcher.start() + 1; + int len = matcher.end() - matcher.start(); + jrt.setRSTART(start); + jrt.setRLENGTH(len); + push(start); + } else { + jrt.setRSTART(0); + jrt.setRLENGTH(-1); + push(0); + } + } + + private void execSubForDollar0(BooleanTuple tuple) { + boolean isGsub = tuple.getValue(); + String repl = jrt.toAwkString(pop()); + String ere = jrt.toAwkString(pop()); + String orig = jrt.toAwkString(jrt.jrtGetInputField(0)); + String newstring = isGsub ? replaceAll(orig, ere, repl) : replaceFirst(orig, ere, repl); + jrt.setInputLine(newstring); + jrt.jrtParseFields(); + } + + private void execSubForDollarReference(BooleanTuple tuple) { + boolean isGsub = tuple.getValue(); + long fieldNum = JRT.parseFieldNumber(pop()); + String orig = jrt.toAwkString(pop()); + String repl = jrt.toAwkString(pop()); + String ere = jrt.toAwkString(pop()); + String newstring = isGsub ? replaceAll(orig, ere, repl) : replaceFirst(orig, ere, repl); + if (fieldNum == 0) { + jrt.setInputLine(newstring); + jrt.jrtParseFields(); + } else { + jrt.jrtSetInputField(newstring, fieldNum); + } + } + + private void execSubForVariable(SubstitutionVariableTuple tuple, PositionTracker position) { + String newString = execSubOrGSub(tuple.isGlobalSubstitution()); + assign(tuple.getVariableOffset(), newString, tuple.isGlobal(), position); + pop(); + } + + private void execSubForArrayReference(SubstitutionVariableTuple tuple) { + Object arrIdx = pop(); + String newString = execSubOrGSub(tuple.isGlobalSubstitution()); + assignArray(tuple.getVariableOffset(), arrIdx, newString, tuple.isGlobal()); + pop(); + } + + private void execSubForMapReference(BooleanTuple tuple) { + Object arrIdx = pop(); + Map array = toMap(pop()); + String newString = execSubOrGSub(tuple.getValue()); + assignMapElement(array, arrIdx, newString); + pop(); + } + + private void execSplit(CountTuple tuple, PositionTracker position) { + long numArgs = tuple.getCount(); + String fsString; + if (numArgs == 2) { + fsString = jrt.toAwkString(jrt.getFSVar()); + } else if (numArgs == 3) { + fsString = jrt.toAwkString(pop()); + } else { + throw new Error("Invalid # of args. split() requires 2 or 3. Got: " + numArgs); + } + Object o = pop(); + if (!(o instanceof Map)) { + throw new AwkRuntimeException(position.lineNumber(), o + " is not an array."); + } + String s = jrt.toAwkString(pop()); + Enumeration tokenizer; + if (fsString.equals(" ")) { + tokenizer = new StringTokenizer(s); + } else if (fsString.length() == 1) { + tokenizer = new SingleCharacterTokenizer(s, fsString.charAt(0)); + } else if (fsString.isEmpty()) { + tokenizer = new CharacterTokenizer(s); + } else { + tokenizer = new RegexTokenizer(s, fsString); + } + + @SuppressWarnings("unchecked") + Map assocArray = (Map) o; + assocArray.clear(); + long cnt = 0; + while (tokenizer.hasMoreElements()) { + assocArray.put(++cnt, tokenizer.nextElement()); + } + push(cnt); + } + + private void execSubstr(CountTuple tuple) { + long numArgs = tuple.getCount(); + int startPos, length; + String s; + if (numArgs == 3) { + length = (int) JRT.toLong(pop()); + startPos = (int) JRT.toDouble(pop()); + s = jrt.toAwkString(pop()); + } else if (numArgs == 2) { + startPos = (int) JRT.toDouble(pop()); + s = jrt.toAwkString(pop()); + length = s.length() - startPos + 1; + } else { + throw new Error("numArgs for SUBSTR must be 2 or 3. It is " + numArgs); + } + if (startPos <= 0) { + startPos = 1; + } + if (length <= 0 || startPos > s.length()) { + push(BLANK); + } else if (startPos + length > s.length()) { + push(s.substring(startPos - 1)); + } else { + push(s.substring(startPos - 1, startPos + length - 1)); + } + } + + private void execSetNumGlobals(CountTuple tuple) { + long numGlobals = tuple.getCount(); + Object[] globals = runtimeStack.getNumGlobals(); + if (mergedGlobalLayoutActive) { + if (!hasCompatiblePersistentGlobalLayout(numGlobals)) { + throw new IllegalStateException( + "AVM globals are already initialized for an incompatible persistent layout."); + } + applyExecutionInitialVariablesToGlobalSlots(true); + } else if (globals == null) { + runtimeStack.setNumGlobals(numGlobals, globalVariableOffsets); + initializedEvalGlobalVariableOffsets = globalVariableOffsets; + initializedEvalGlobalVariableArrays = globalVariableArrays; + applyExecutionInitialVariablesToGlobalSlots(false); + } else if (!hasCompatibleEvalGlobalLayout(numGlobals)) { + throw new IllegalStateException( + "AVM globals are already initialized for a different eval layout. Call prepareForEval(...) first."); + } + } + + private void execApplySubsep(CountTuple tuple) { + long count = tuple.getCount(); + if (count == 1) { + Object value = pop(); + checkScalar(value); + push(jrt.toAwkString(value)); + return; + } + StringBuilder sb = new StringBuilder(); + Object value = pop(); + checkScalar(value); + sb.append(jrt.toAwkString(value)); + String subsep = jrt.toAwkString(jrt.getSUBSEPVar()); + for (int i = 1; i < count; i++) { + sb.insert(0, subsep); + value = pop(); + checkScalar(value); + sb.insert(0, jrt.toAwkString(value)); + } + push(sb.toString()); + } + + private long beforeProfiledTuple(Tuple tuple, Opcode opcode) { long now = System.nanoTime(); if (opcode == Opcode.CALL_FUNCTION) { - activeProfilingFunctions.push(new ActiveFunction(position.stringArg(1), now)); + CallFunctionTuple callTuple = (CallFunctionTuple) tuple; + activeProfilingFunctions.push(new ActiveFunction(callTuple.getFunctionName(), now)); } else if (opcode == Opcode.EXTENSION) { - ExtensionFunction function = position.extensionFunctionArg(); + ExtensionTuple extensionTuple = (ExtensionTuple) tuple; + ExtensionFunction function = extensionTuple.getFunction(); activeProfilingFunctions.push(new ActiveFunction(function.getKeyword(), now)); } return now; @@ -3038,14 +3036,12 @@ private void setNumOnJRT(long fieldNum, double num) { } } - private String execSubOrGSub(PositionTracker position, int gsubArgPos) { + private String execSubOrGSub(boolean isGsub) { String newString; - // arg[gsubArgPos] = isGsub // stack[0] = original field value // stack[1] = replacement string // stack[2] = ere - boolean isGsub = position.boolArg(gsubArgPos); String orig = jrt.toAwkString(pop()); String repl = jrt.toAwkString(pop()); String ere = jrt.toAwkString(pop()); diff --git a/src/main/java/io/jawk/intermediate/AwkTuples.java b/src/main/java/io/jawk/intermediate/AwkTuples.java index 4fbddd63..65e641ea 100644 --- a/src/main/java/io/jawk/intermediate/AwkTuples.java +++ b/src/main/java/io/jawk/intermediate/AwkTuples.java @@ -106,7 +106,7 @@ public static String toOpcodeString(int opcode) { *

*/ public void pop() { - queue.add(new Tuple(Opcode.POP)); + queue.add(new Tuple.NoOperandTuple(Opcode.POP)); } /** @@ -118,13 +118,13 @@ public void pop() { */ public void push(Object o) { if (o instanceof String) { - queue.add(new Tuple(Opcode.PUSH_STRING, o.toString())); + queue.add(new Tuple.PushStringTuple(o.toString())); } else if (o instanceof Integer) { - queue.add(new Tuple(Opcode.PUSH_LONG, (long) (Integer) o)); + queue.add(new Tuple.PushLongTuple((long) (Integer) o)); } else if (o instanceof Long) { - queue.add(new Tuple(Opcode.PUSH_LONG, (long) (Long) o)); + queue.add(new Tuple.PushLongTuple((long) (Long) o)); } else if (o instanceof Double) { - queue.add(new Tuple(Opcode.PUSH_DOUBLE, (Double) o)); + queue.add(new Tuple.PushDoubleTuple((Double) o)); } } @@ -136,7 +136,7 @@ public void push(Object o) { * @param address a {@link io.jawk.intermediate.Address} object */ public void ifFalse(Address address) { - queue.add(new Tuple(Opcode.IFFALSE, address)); + queue.add(new Tuple.AddressTuple(Opcode.IFFALSE, address)); } /** @@ -145,7 +145,7 @@ public void ifFalse(Address address) { *

*/ public void toNumber() { - queue.add(new Tuple(Opcode.TO_NUMBER)); + queue.add(new Tuple.NoOperandTuple(Opcode.TO_NUMBER)); } /** @@ -156,7 +156,7 @@ public void toNumber() { * @param address a {@link io.jawk.intermediate.Address} object */ public void ifTrue(Address address) { - queue.add(new Tuple(Opcode.IFTRUE, address)); + queue.add(new Tuple.AddressTuple(Opcode.IFTRUE, address)); } /** @@ -167,7 +167,7 @@ public void ifTrue(Address address) { * @param address a {@link io.jawk.intermediate.Address} object */ public void gotoAddress(Address address) { - queue.add(new Tuple(Opcode.GOTO, address)); + queue.add(new Tuple.AddressTuple(Opcode.GOTO, address)); } /** @@ -201,7 +201,7 @@ public AwkTuples address(Address address) { *

*/ public void nop() { - queue.add(new Tuple(Opcode.NOP)); + queue.add(new Tuple.NoOperandTuple(Opcode.NOP)); } /** @@ -212,7 +212,7 @@ public void nop() { * @param numExprs a int */ public void print(int numExprs) { - queue.add(new Tuple(Opcode.PRINT, numExprs)); + queue.add(new Tuple.CountTuple(Opcode.PRINT, numExprs)); } /** @@ -224,7 +224,7 @@ public void print(int numExprs) { * @param append a boolean */ public void printToFile(int numExprs, boolean append) { - queue.add(new Tuple(Opcode.PRINT_TO_FILE, numExprs, append)); + queue.add(new Tuple.CountAndAppendTuple(Opcode.PRINT_TO_FILE, numExprs, append)); } /** @@ -235,7 +235,7 @@ public void printToFile(int numExprs, boolean append) { * @param numExprs a int */ public void printToPipe(int numExprs) { - queue.add(new Tuple(Opcode.PRINT_TO_PIPE, numExprs)); + queue.add(new Tuple.CountTuple(Opcode.PRINT_TO_PIPE, numExprs)); } /** @@ -246,7 +246,7 @@ public void printToPipe(int numExprs) { * @param numExprs a int */ public void printf(int numExprs) { - queue.add(new Tuple(Opcode.PRINTF, numExprs)); + queue.add(new Tuple.CountTuple(Opcode.PRINTF, numExprs)); } /** @@ -258,7 +258,7 @@ public void printf(int numExprs) { * @param append a boolean */ public void printfToFile(int numExprs, boolean append) { - queue.add(new Tuple(Opcode.PRINTF_TO_FILE, numExprs, append)); + queue.add(new Tuple.CountAndAppendTuple(Opcode.PRINTF_TO_FILE, numExprs, append)); } /** @@ -269,7 +269,7 @@ public void printfToFile(int numExprs, boolean append) { * @param numExprs a int */ public void printfToPipe(int numExprs) { - queue.add(new Tuple(Opcode.PRINTF_TO_PIPE, numExprs)); + queue.add(new Tuple.CountTuple(Opcode.PRINTF_TO_PIPE, numExprs)); } /** @@ -280,7 +280,7 @@ public void printfToPipe(int numExprs) { * @param numExprs a int */ public void sprintf(int numExprs) { - queue.add(new Tuple(Opcode.SPRINTF, numExprs)); + queue.add(new Tuple.CountTuple(Opcode.SPRINTF, numExprs)); } /** @@ -291,7 +291,7 @@ public void sprintf(int numExprs) { * @param numExprs a int */ public void length(int numExprs) { - queue.add(new Tuple(Opcode.LENGTH, numExprs)); + queue.add(new Tuple.CountTuple(Opcode.LENGTH, numExprs)); } /** @@ -300,7 +300,7 @@ public void length(int numExprs) { *

*/ public void concat() { - queue.add(new Tuple(Opcode.CONCAT)); + queue.add(new Tuple.NoOperandTuple(Opcode.CONCAT)); } /** @@ -312,7 +312,7 @@ public void concat() { * @param isGlobal a boolean */ public void assign(int offset, boolean isGlobal) { - queue.add(new Tuple(Opcode.ASSIGN, offset, isGlobal)); + queue.add(new Tuple.VariableTuple(Opcode.ASSIGN, offset, isGlobal)); } /** @@ -324,14 +324,14 @@ public void assign(int offset, boolean isGlobal) { * @param isGlobal a boolean */ public void assignArray(int offset, boolean isGlobal) { - queue.add(new Tuple(Opcode.ASSIGN_ARRAY, offset, isGlobal)); + queue.add(new Tuple.VariableTuple(Opcode.ASSIGN_ARRAY, offset, isGlobal)); } /** * Assigns a value to a stack-provided associative-array element. */ public void assignMapElement() { - queue.add(new Tuple(Opcode.ASSIGN_MAP_ELEMENT)); + queue.add(new Tuple.NoOperandTuple(Opcode.ASSIGN_MAP_ELEMENT)); } /** @@ -340,7 +340,7 @@ public void assignMapElement() { *

*/ public void assignAsInput() { - queue.add(new Tuple(Opcode.ASSIGN_AS_INPUT)); + queue.add(new Tuple.NoOperandTuple(Opcode.ASSIGN_AS_INPUT)); } /** @@ -358,7 +358,7 @@ public void markEvalTupleStream() { *

*/ public void assignAsInputField() { - queue.add(new Tuple(Opcode.ASSIGN_AS_INPUT_FIELD)); + queue.add(new Tuple.NoOperandTuple(Opcode.ASSIGN_AS_INPUT_FIELD)); } /** @@ -371,7 +371,7 @@ public void assignAsInputField() { * @param isGlobal a boolean */ public void dereference(int offset, boolean isArray, boolean isGlobal) { - queue.add(new Tuple(Opcode.DEREFERENCE, offset, isArray, isGlobal)); + queue.add(new Tuple.DereferenceTuple(offset, isArray, isGlobal)); } /** @@ -383,7 +383,7 @@ public void dereference(int offset, boolean isArray, boolean isGlobal) { * @param isGlobal a boolean */ public void plusEq(int offset, boolean isGlobal) { - queue.add(new Tuple(Opcode.PLUS_EQ, offset, isGlobal)); + queue.add(new Tuple.CompoundAssignTuple(Opcode.PLUS_EQ, offset, isGlobal)); } /** @@ -395,7 +395,7 @@ public void plusEq(int offset, boolean isGlobal) { * @param isGlobal a boolean */ public void minusEq(int offset, boolean isGlobal) { - queue.add(new Tuple(Opcode.MINUS_EQ, offset, isGlobal)); + queue.add(new Tuple.CompoundAssignTuple(Opcode.MINUS_EQ, offset, isGlobal)); } /** @@ -407,7 +407,7 @@ public void minusEq(int offset, boolean isGlobal) { * @param isGlobal a boolean */ public void multEq(int offset, boolean isGlobal) { - queue.add(new Tuple(Opcode.MULT_EQ, offset, isGlobal)); + queue.add(new Tuple.CompoundAssignTuple(Opcode.MULT_EQ, offset, isGlobal)); } /** @@ -419,7 +419,7 @@ public void multEq(int offset, boolean isGlobal) { * @param isGlobal a boolean */ public void divEq(int offset, boolean isGlobal) { - queue.add(new Tuple(Opcode.DIV_EQ, offset, isGlobal)); + queue.add(new Tuple.CompoundAssignTuple(Opcode.DIV_EQ, offset, isGlobal)); } /** @@ -431,7 +431,7 @@ public void divEq(int offset, boolean isGlobal) { * @param isGlobal a boolean */ public void modEq(int offset, boolean isGlobal) { - queue.add(new Tuple(Opcode.MOD_EQ, offset, isGlobal)); + queue.add(new Tuple.CompoundAssignTuple(Opcode.MOD_EQ, offset, isGlobal)); } /** @@ -443,7 +443,7 @@ public void modEq(int offset, boolean isGlobal) { * @param isGlobal a boolean */ public void powEq(int offset, boolean isGlobal) { - queue.add(new Tuple(Opcode.POW_EQ, offset, isGlobal)); + queue.add(new Tuple.CompoundAssignTuple(Opcode.POW_EQ, offset, isGlobal)); } /** @@ -455,14 +455,14 @@ public void powEq(int offset, boolean isGlobal) { * @param isGlobal a boolean */ public void plusEqArray(int offset, boolean isGlobal) { - queue.add(new Tuple(Opcode.PLUS_EQ_ARRAY, offset, isGlobal)); + queue.add(new Tuple.CompoundAssignArrayTuple(Opcode.PLUS_EQ_ARRAY, offset, isGlobal)); } /** * Applies {@code +=} to a stack-provided associative-array element. */ public void plusEqMapElement() { - queue.add(new Tuple(Opcode.PLUS_EQ_MAP_ELEMENT)); + queue.add(new Tuple.CompoundAssignMapElementTuple(Opcode.PLUS_EQ_MAP_ELEMENT)); } /** @@ -474,14 +474,14 @@ public void plusEqMapElement() { * @param isGlobal a boolean */ public void minusEqArray(int offset, boolean isGlobal) { - queue.add(new Tuple(Opcode.MINUS_EQ_ARRAY, offset, isGlobal)); + queue.add(new Tuple.CompoundAssignArrayTuple(Opcode.MINUS_EQ_ARRAY, offset, isGlobal)); } /** * Applies {@code -=} to a stack-provided associative-array element. */ public void minusEqMapElement() { - queue.add(new Tuple(Opcode.MINUS_EQ_MAP_ELEMENT)); + queue.add(new Tuple.CompoundAssignMapElementTuple(Opcode.MINUS_EQ_MAP_ELEMENT)); } /** @@ -493,14 +493,14 @@ public void minusEqMapElement() { * @param isGlobal a boolean */ public void multEqArray(int offset, boolean isGlobal) { - queue.add(new Tuple(Opcode.MULT_EQ_ARRAY, offset, isGlobal)); + queue.add(new Tuple.CompoundAssignArrayTuple(Opcode.MULT_EQ_ARRAY, offset, isGlobal)); } /** * Applies {@code *=} to a stack-provided associative-array element. */ public void multEqMapElement() { - queue.add(new Tuple(Opcode.MULT_EQ_MAP_ELEMENT)); + queue.add(new Tuple.CompoundAssignMapElementTuple(Opcode.MULT_EQ_MAP_ELEMENT)); } /** @@ -512,14 +512,14 @@ public void multEqMapElement() { * @param isGlobal a boolean */ public void divEqArray(int offset, boolean isGlobal) { - queue.add(new Tuple(Opcode.DIV_EQ_ARRAY, offset, isGlobal)); + queue.add(new Tuple.CompoundAssignArrayTuple(Opcode.DIV_EQ_ARRAY, offset, isGlobal)); } /** * Applies {@code /=} to a stack-provided associative-array element. */ public void divEqMapElement() { - queue.add(new Tuple(Opcode.DIV_EQ_MAP_ELEMENT)); + queue.add(new Tuple.CompoundAssignMapElementTuple(Opcode.DIV_EQ_MAP_ELEMENT)); } /** @@ -531,14 +531,14 @@ public void divEqMapElement() { * @param isGlobal a boolean */ public void modEqArray(int offset, boolean isGlobal) { - queue.add(new Tuple(Opcode.MOD_EQ_ARRAY, offset, isGlobal)); + queue.add(new Tuple.CompoundAssignArrayTuple(Opcode.MOD_EQ_ARRAY, offset, isGlobal)); } /** * Applies {@code %=} to a stack-provided associative-array element. */ public void modEqMapElement() { - queue.add(new Tuple(Opcode.MOD_EQ_MAP_ELEMENT)); + queue.add(new Tuple.CompoundAssignMapElementTuple(Opcode.MOD_EQ_MAP_ELEMENT)); } /** @@ -550,7 +550,7 @@ public void modEqMapElement() { * @param isGlobal a boolean */ public void powEqArray(int offset, boolean isGlobal) { - queue.add(new Tuple(Opcode.POW_EQ_ARRAY, offset, isGlobal)); + queue.add(new Tuple.CompoundAssignArrayTuple(Opcode.POW_EQ_ARRAY, offset, isGlobal)); } /** @@ -558,7 +558,7 @@ public void powEqArray(int offset, boolean isGlobal) { * element. */ public void powEqMapElement() { - queue.add(new Tuple(Opcode.POW_EQ_MAP_ELEMENT)); + queue.add(new Tuple.CompoundAssignMapElementTuple(Opcode.POW_EQ_MAP_ELEMENT)); } /** @@ -567,7 +567,7 @@ public void powEqMapElement() { *

*/ public void plusEqInputField() { - queue.add(new Tuple(Opcode.PLUS_EQ_INPUT_FIELD)); + queue.add(new Tuple.CompoundAssignInputFieldTuple(Opcode.PLUS_EQ_INPUT_FIELD)); } /** @@ -576,7 +576,7 @@ public void plusEqInputField() { *

*/ public void minusEqInputField() { - queue.add(new Tuple(Opcode.MINUS_EQ_INPUT_FIELD)); + queue.add(new Tuple.CompoundAssignInputFieldTuple(Opcode.MINUS_EQ_INPUT_FIELD)); } /** @@ -585,7 +585,7 @@ public void minusEqInputField() { *

*/ public void multEqInputField() { - queue.add(new Tuple(Opcode.MULT_EQ_INPUT_FIELD)); + queue.add(new Tuple.CompoundAssignInputFieldTuple(Opcode.MULT_EQ_INPUT_FIELD)); } /** @@ -594,7 +594,7 @@ public void multEqInputField() { *

*/ public void divEqInputField() { - queue.add(new Tuple(Opcode.DIV_EQ_INPUT_FIELD)); + queue.add(new Tuple.CompoundAssignInputFieldTuple(Opcode.DIV_EQ_INPUT_FIELD)); } /** @@ -603,7 +603,7 @@ public void divEqInputField() { *

*/ public void modEqInputField() { - queue.add(new Tuple(Opcode.MOD_EQ_INPUT_FIELD)); + queue.add(new Tuple.CompoundAssignInputFieldTuple(Opcode.MOD_EQ_INPUT_FIELD)); } /** @@ -612,7 +612,7 @@ public void modEqInputField() { *

*/ public void powEqInputField() { - queue.add(new Tuple(Opcode.POW_EQ_INPUT_FIELD)); + queue.add(new Tuple.CompoundAssignInputFieldTuple(Opcode.POW_EQ_INPUT_FIELD)); } /** @@ -623,7 +623,7 @@ public void powEqInputField() { * @param num a int */ public void srand(int num) { - queue.add(new Tuple(Opcode.SRAND, num)); + queue.add(new Tuple.CountTuple(Opcode.SRAND, num)); } /** @@ -632,7 +632,7 @@ public void srand(int num) { *

*/ public void rand() { - queue.add(new Tuple(Opcode.RAND)); + queue.add(new Tuple.NoOperandTuple(Opcode.RAND)); } /** @@ -641,7 +641,7 @@ public void rand() { *

*/ public void intFunc() { - queue.add(new Tuple(Opcode.INTFUNC)); + queue.add(new Tuple.NoOperandTuple(Opcode.INTFUNC)); } /** @@ -650,7 +650,7 @@ public void intFunc() { *

*/ public void sqrt() { - queue.add(new Tuple(Opcode.SQRT)); + queue.add(new Tuple.NoOperandTuple(Opcode.SQRT)); } /** @@ -659,7 +659,7 @@ public void sqrt() { *

*/ public void log() { - queue.add(new Tuple(Opcode.LOG)); + queue.add(new Tuple.NoOperandTuple(Opcode.LOG)); } /** @@ -668,7 +668,7 @@ public void log() { *

*/ public void exp() { - queue.add(new Tuple(Opcode.EXP)); + queue.add(new Tuple.NoOperandTuple(Opcode.EXP)); } /** @@ -677,7 +677,7 @@ public void exp() { *

*/ public void sin() { - queue.add(new Tuple(Opcode.SIN)); + queue.add(new Tuple.NoOperandTuple(Opcode.SIN)); } /** @@ -686,7 +686,7 @@ public void sin() { *

*/ public void cos() { - queue.add(new Tuple(Opcode.COS)); + queue.add(new Tuple.NoOperandTuple(Opcode.COS)); } /** @@ -695,7 +695,7 @@ public void cos() { *

*/ public void atan2() { - queue.add(new Tuple(Opcode.ATAN2)); + queue.add(new Tuple.NoOperandTuple(Opcode.ATAN2)); } /** @@ -704,7 +704,7 @@ public void atan2() { *

*/ public void match() { - queue.add(new Tuple(Opcode.MATCH)); + queue.add(new Tuple.NoOperandTuple(Opcode.MATCH)); } /** @@ -713,7 +713,7 @@ public void match() { *

*/ public void index() { - queue.add(new Tuple(Opcode.INDEX)); + queue.add(new Tuple.NoOperandTuple(Opcode.INDEX)); } /** @@ -724,7 +724,7 @@ public void index() { * @param isGsub a boolean */ public void subForDollar0(boolean isGsub) { - queue.add(new Tuple(Opcode.SUB_FOR_DOLLAR_0, isGsub)); + queue.add(new Tuple.BooleanTuple(Opcode.SUB_FOR_DOLLAR_0, isGsub)); } /** @@ -735,7 +735,7 @@ public void subForDollar0(boolean isGsub) { * @param isGsub a boolean */ public void subForDollarReference(boolean isGsub) { - queue.add(new Tuple(Opcode.SUB_FOR_DOLLAR_REFERENCE, isGsub)); + queue.add(new Tuple.BooleanTuple(Opcode.SUB_FOR_DOLLAR_REFERENCE, isGsub)); } /** @@ -748,7 +748,7 @@ public void subForDollarReference(boolean isGsub) { * @param isGsub a boolean */ public void subForVariable(int offset, boolean isGlobal, boolean isGsub) { - queue.add(new Tuple(Opcode.SUB_FOR_VARIABLE, offset, isGlobal, isGsub)); + queue.add(new Tuple.SubstitutionVariableTuple(Opcode.SUB_FOR_VARIABLE, offset, isGlobal, isGsub)); } /** @@ -761,7 +761,7 @@ public void subForVariable(int offset, boolean isGlobal, boolean isGsub) { * @param isGsub a boolean */ public void subForArrayReference(int offset, boolean isGlobal, boolean isGsub) { - queue.add(new Tuple(Opcode.SUB_FOR_ARRAY_REFERENCE, offset, isGlobal, isGsub)); + queue.add(new Tuple.SubstitutionVariableTuple(Opcode.SUB_FOR_ARRAY_REFERENCE, offset, isGlobal, isGsub)); } /** @@ -771,7 +771,7 @@ public void subForArrayReference(int offset, boolean isGlobal, boolean isGsub) { * @param isGsub {@code true} for {@code gsub}, {@code false} for {@code sub} */ public void subForMapReference(boolean isGsub) { - queue.add(new Tuple(Opcode.SUB_FOR_MAP_REFERENCE, isGsub)); + queue.add(new Tuple.BooleanTuple(Opcode.SUB_FOR_MAP_REFERENCE, isGsub)); } /** @@ -782,7 +782,7 @@ public void subForMapReference(boolean isGsub) { * @param numargs a int */ public void split(int numargs) { - queue.add(new Tuple(Opcode.SPLIT, numargs)); + queue.add(new Tuple.CountTuple(Opcode.SPLIT, numargs)); } /** @@ -793,7 +793,7 @@ public void split(int numargs) { * @param numargs a int */ public void substr(int numargs) { - queue.add(new Tuple(Opcode.SUBSTR, numargs)); + queue.add(new Tuple.CountTuple(Opcode.SUBSTR, numargs)); } /** @@ -802,7 +802,7 @@ public void substr(int numargs) { *

*/ public void tolower() { - queue.add(new Tuple(Opcode.TOLOWER)); + queue.add(new Tuple.NoOperandTuple(Opcode.TOLOWER)); } /** @@ -811,7 +811,7 @@ public void tolower() { *

*/ public void toupper() { - queue.add(new Tuple(Opcode.TOUPPER)); + queue.add(new Tuple.NoOperandTuple(Opcode.TOUPPER)); } /** @@ -820,7 +820,7 @@ public void toupper() { *

*/ public void system() { - queue.add(new Tuple(Opcode.SYSTEM)); + queue.add(new Tuple.NoOperandTuple(Opcode.SYSTEM)); } /** @@ -829,7 +829,7 @@ public void system() { *

*/ public void swap() { - queue.add(new Tuple(Opcode.SWAP)); + queue.add(new Tuple.NoOperandTuple(Opcode.SWAP)); } /** @@ -838,7 +838,7 @@ public void swap() { *

*/ public void add() { - queue.add(new Tuple(Opcode.ADD)); + queue.add(new Tuple.NoOperandTuple(Opcode.ADD)); } /** @@ -847,7 +847,7 @@ public void add() { *

*/ public void subtract() { - queue.add(new Tuple(Opcode.SUBTRACT)); + queue.add(new Tuple.NoOperandTuple(Opcode.SUBTRACT)); } /** @@ -856,7 +856,7 @@ public void subtract() { *

*/ public void multiply() { - queue.add(new Tuple(Opcode.MULTIPLY)); + queue.add(new Tuple.NoOperandTuple(Opcode.MULTIPLY)); } /** @@ -865,7 +865,7 @@ public void multiply() { *

*/ public void divide() { - queue.add(new Tuple(Opcode.DIVIDE)); + queue.add(new Tuple.NoOperandTuple(Opcode.DIVIDE)); } /** @@ -874,7 +874,7 @@ public void divide() { *

*/ public void mod() { - queue.add(new Tuple(Opcode.MOD)); + queue.add(new Tuple.NoOperandTuple(Opcode.MOD)); } /** @@ -883,7 +883,7 @@ public void mod() { *

*/ public void pow() { - queue.add(new Tuple(Opcode.POW)); + queue.add(new Tuple.NoOperandTuple(Opcode.POW)); } /** @@ -895,7 +895,7 @@ public void pow() { * @param isGlobal a boolean */ public void inc(int offset, boolean isGlobal) { - queue.add(new Tuple(Opcode.INC, offset, isGlobal)); + queue.add(new Tuple.VariableTuple(Opcode.INC, offset, isGlobal)); } /** @@ -907,7 +907,7 @@ public void inc(int offset, boolean isGlobal) { * @param isGlobal a boolean */ public void dec(int offset, boolean isGlobal) { - queue.add(new Tuple(Opcode.DEC, offset, isGlobal)); + queue.add(new Tuple.VariableTuple(Opcode.DEC, offset, isGlobal)); } /** @@ -919,7 +919,7 @@ public void dec(int offset, boolean isGlobal) { * @param isGlobal a boolean */ public void postInc(int offset, boolean isGlobal) { - queue.add(new Tuple(Opcode.POSTINC, offset, isGlobal)); + queue.add(new Tuple.VariableTuple(Opcode.POSTINC, offset, isGlobal)); } /** @@ -931,7 +931,7 @@ public void postInc(int offset, boolean isGlobal) { * @param isGlobal a boolean */ public void postDec(int offset, boolean isGlobal) { - queue.add(new Tuple(Opcode.POSTDEC, offset, isGlobal)); + queue.add(new Tuple.VariableTuple(Opcode.POSTDEC, offset, isGlobal)); } /** @@ -943,14 +943,14 @@ public void postDec(int offset, boolean isGlobal) { * @param isGlobal a boolean */ public void incArrayRef(int offset, boolean isGlobal) { - queue.add(new Tuple(Opcode.INC_ARRAY_REF, offset, isGlobal)); + queue.add(new Tuple.VariableTuple(Opcode.INC_ARRAY_REF, offset, isGlobal)); } /** * Increments a stack-provided associative-array element reference. */ public void incMapRef() { - queue.add(new Tuple(Opcode.INC_MAP_REF)); + queue.add(new Tuple.NoOperandTuple(Opcode.INC_MAP_REF)); } /** @@ -962,14 +962,14 @@ public void incMapRef() { * @param isGlobal a boolean */ public void decArrayRef(int offset, boolean isGlobal) { - queue.add(new Tuple(Opcode.DEC_ARRAY_REF, offset, isGlobal)); + queue.add(new Tuple.VariableTuple(Opcode.DEC_ARRAY_REF, offset, isGlobal)); } /** * Decrements a stack-provided associative-array element reference. */ public void decMapRef() { - queue.add(new Tuple(Opcode.DEC_MAP_REF)); + queue.add(new Tuple.NoOperandTuple(Opcode.DEC_MAP_REF)); } /** @@ -978,7 +978,7 @@ public void decMapRef() { *

*/ public void incDollarRef() { - queue.add(new Tuple(Opcode.INC_DOLLAR_REF)); + queue.add(new Tuple.NoOperandTuple(Opcode.INC_DOLLAR_REF)); } /** @@ -987,7 +987,7 @@ public void incDollarRef() { *

*/ public void decDollarRef() { - queue.add(new Tuple(Opcode.DEC_DOLLAR_REF)); + queue.add(new Tuple.NoOperandTuple(Opcode.DEC_DOLLAR_REF)); } /** @@ -996,7 +996,7 @@ public void decDollarRef() { *

*/ public void dup() { - queue.add(new Tuple(Opcode.DUP)); + queue.add(new Tuple.NoOperandTuple(Opcode.DUP)); } /** @@ -1005,7 +1005,7 @@ public void dup() { *

*/ public void not() { - queue.add(new Tuple(Opcode.NOT)); + queue.add(new Tuple.NoOperandTuple(Opcode.NOT)); } /** @@ -1014,7 +1014,7 @@ public void not() { *

*/ public void negate() { - queue.add(new Tuple(Opcode.NEGATE)); + queue.add(new Tuple.NoOperandTuple(Opcode.NEGATE)); } /** @@ -1023,7 +1023,7 @@ public void negate() { *

*/ public void unaryPlus() { - queue.add(new Tuple(Opcode.UNARY_PLUS)); + queue.add(new Tuple.NoOperandTuple(Opcode.UNARY_PLUS)); } /** @@ -1032,7 +1032,7 @@ public void unaryPlus() { *

*/ public void cmpEq() { - queue.add(new Tuple(Opcode.CMP_EQ)); + queue.add(new Tuple.NoOperandTuple(Opcode.CMP_EQ)); } /** @@ -1041,7 +1041,7 @@ public void cmpEq() { *

*/ public void cmpLt() { - queue.add(new Tuple(Opcode.CMP_LT)); + queue.add(new Tuple.NoOperandTuple(Opcode.CMP_LT)); } /** @@ -1050,7 +1050,7 @@ public void cmpLt() { *

*/ public void cmpGt() { - queue.add(new Tuple(Opcode.CMP_GT)); + queue.add(new Tuple.NoOperandTuple(Opcode.CMP_GT)); } /** @@ -1059,7 +1059,7 @@ public void cmpGt() { *

*/ public void matches() { - queue.add(new Tuple(Opcode.MATCHES)); + queue.add(new Tuple.NoOperandTuple(Opcode.MATCHES)); } /** @@ -1068,7 +1068,7 @@ public void matches() { *

*/ public void dereferenceArray() { - queue.add(new Tuple(Opcode.DEREF_ARRAY)); + queue.add(new Tuple.NoOperandTuple(Opcode.DEREF_ARRAY)); } /** @@ -1076,7 +1076,7 @@ public void dereferenceArray() { * the key is missing. */ public void peekArrayElement() { - queue.add(new Tuple(Opcode.PEEK_ARRAY_ELEMENT)); + queue.add(new Tuple.NoOperandTuple(Opcode.PEEK_ARRAY_ELEMENT)); } /** @@ -1084,7 +1084,7 @@ public void peekArrayElement() { * needed. */ public void ensureArrayElement() { - queue.add(new Tuple(Opcode.ENSURE_ARRAY_ELEMENT)); + queue.add(new Tuple.NoOperandTuple(Opcode.ENSURE_ARRAY_ELEMENT)); } /** @@ -1093,7 +1093,7 @@ public void ensureArrayElement() { *

*/ public void keylist() { - queue.add(new Tuple(Opcode.KEYLIST)); + queue.add(new Tuple.NoOperandTuple(Opcode.KEYLIST)); } /** @@ -1104,7 +1104,7 @@ public void keylist() { * @param address a {@link io.jawk.intermediate.Address} object */ public void isEmptyList(Address address) { - queue.add(new Tuple(Opcode.IS_EMPTY_KEYLIST, address)); + queue.add(new Tuple.AddressTuple(Opcode.IS_EMPTY_KEYLIST, address)); } /** @@ -1113,7 +1113,7 @@ public void isEmptyList(Address address) { *

*/ public void getFirstAndRemoveFromList() { - queue.add(new Tuple(Opcode.GET_FIRST_AND_REMOVE_FROM_KEYLIST)); + queue.add(new Tuple.NoOperandTuple(Opcode.GET_FIRST_AND_REMOVE_FROM_KEYLIST)); } /** @@ -1125,7 +1125,7 @@ public void getFirstAndRemoveFromList() { * @return a boolean */ public boolean checkClass(Class cls) { - queue.add(new Tuple(Opcode.CHECK_CLASS, cls)); + queue.add(new Tuple.ClassTuple(cls)); return true; } @@ -1135,7 +1135,7 @@ public boolean checkClass(Class cls) { *

*/ public void getInputField() { - queue.add(new Tuple(Opcode.GET_INPUT_FIELD)); + queue.add(new Tuple.NoOperandTuple(Opcode.GET_INPUT_FIELD)); } /** @@ -1146,7 +1146,7 @@ public void getInputField() { * @param fieldIndex a long */ public void getInputField(long fieldIndex) { - queue.add(new Tuple(Opcode.GET_INPUT_FIELD_CONST, fieldIndex)); + queue.add(new Tuple.InputFieldTuple(fieldIndex)); } /** @@ -1157,7 +1157,7 @@ public void getInputField(long fieldIndex) { * @param address a {@link io.jawk.intermediate.Address} object */ public void consumeInput(Address address) { - queue.add(new Tuple(Opcode.CONSUME_INPUT, address)); + queue.add(new Tuple.AddressTuple(Opcode.CONSUME_INPUT, address)); } /** @@ -1166,7 +1166,7 @@ public void consumeInput(Address address) { *

*/ public void getlineInput() { - queue.add(new Tuple(Opcode.GETLINE_INPUT)); + queue.add(new Tuple.NoOperandTuple(Opcode.GETLINE_INPUT)); } /** @@ -1175,7 +1175,7 @@ public void getlineInput() { *

*/ public void getlineInputToTarget() { - queue.add(new Tuple(Opcode.GETLINE_INPUT_TO_TARGET)); + queue.add(new Tuple.NoOperandTuple(Opcode.GETLINE_INPUT_TO_TARGET)); } /** @@ -1184,7 +1184,7 @@ public void getlineInputToTarget() { *

*/ public void useAsFileInput() { - queue.add(new Tuple(Opcode.USE_AS_FILE_INPUT)); + queue.add(new Tuple.NoOperandTuple(Opcode.USE_AS_FILE_INPUT)); } /** @@ -1193,7 +1193,7 @@ public void useAsFileInput() { *

*/ public void useAsCommandInput() { - queue.add(new Tuple(Opcode.USE_AS_COMMAND_INPUT)); + queue.add(new Tuple.NoOperandTuple(Opcode.USE_AS_COMMAND_INPUT)); } /** @@ -1204,7 +1204,7 @@ public void useAsCommandInput() { * @param offset a int */ public void nfOffset(int offset) { - queue.add(new Tuple(Opcode.NF_OFFSET, offset)); + queue.add(new Tuple.LongTuple(Opcode.NF_OFFSET, offset)); } /** @@ -1215,7 +1215,7 @@ public void nfOffset(int offset) { * @param offset a int */ public void nrOffset(int offset) { - queue.add(new Tuple(Opcode.NR_OFFSET, offset)); + queue.add(new Tuple.LongTuple(Opcode.NR_OFFSET, offset)); } /** @@ -1226,7 +1226,7 @@ public void nrOffset(int offset) { * @param offset a int */ public void fnrOffset(int offset) { - queue.add(new Tuple(Opcode.FNR_OFFSET, offset)); + queue.add(new Tuple.LongTuple(Opcode.FNR_OFFSET, offset)); } /** @@ -1237,7 +1237,7 @@ public void fnrOffset(int offset) { * @param offset a int */ public void fsOffset(int offset) { - queue.add(new Tuple(Opcode.FS_OFFSET, offset)); + queue.add(new Tuple.LongTuple(Opcode.FS_OFFSET, offset)); } /** @@ -1248,7 +1248,7 @@ public void fsOffset(int offset) { * @param offset a int */ public void rsOffset(int offset) { - queue.add(new Tuple(Opcode.RS_OFFSET, offset)); + queue.add(new Tuple.LongTuple(Opcode.RS_OFFSET, offset)); } /** @@ -1259,7 +1259,7 @@ public void rsOffset(int offset) { * @param offset a int */ public void ofsOffset(int offset) { - queue.add(new Tuple(Opcode.OFS_OFFSET, offset)); + queue.add(new Tuple.LongTuple(Opcode.OFS_OFFSET, offset)); } /** @@ -1270,7 +1270,7 @@ public void ofsOffset(int offset) { * @param offset a int */ public void orsOffset(int offset) { - queue.add(new Tuple(Opcode.ORS_OFFSET, offset)); + queue.add(new Tuple.LongTuple(Opcode.ORS_OFFSET, offset)); } /** @@ -1281,7 +1281,7 @@ public void orsOffset(int offset) { * @param offset a int */ public void rstartOffset(int offset) { - queue.add(new Tuple(Opcode.RSTART_OFFSET, offset)); + queue.add(new Tuple.LongTuple(Opcode.RSTART_OFFSET, offset)); } /** @@ -1292,7 +1292,7 @@ public void rstartOffset(int offset) { * @param offset a int */ public void rlengthOffset(int offset) { - queue.add(new Tuple(Opcode.RLENGTH_OFFSET, offset)); + queue.add(new Tuple.LongTuple(Opcode.RLENGTH_OFFSET, offset)); } /** @@ -1303,7 +1303,7 @@ public void rlengthOffset(int offset) { * @param offset a int */ public void filenameOffset(int offset) { - queue.add(new Tuple(Opcode.FILENAME_OFFSET, offset)); + queue.add(new Tuple.LongTuple(Opcode.FILENAME_OFFSET, offset)); } /** @@ -1314,7 +1314,7 @@ public void filenameOffset(int offset) { * @param offset a int */ public void subsepOffset(int offset) { - queue.add(new Tuple(Opcode.SUBSEP_OFFSET, offset)); + queue.add(new Tuple.LongTuple(Opcode.SUBSEP_OFFSET, offset)); } /** @@ -1325,7 +1325,7 @@ public void subsepOffset(int offset) { * @param offset a int */ public void convfmtOffset(int offset) { - queue.add(new Tuple(Opcode.CONVFMT_OFFSET, offset)); + queue.add(new Tuple.LongTuple(Opcode.CONVFMT_OFFSET, offset)); } /** @@ -1336,7 +1336,7 @@ public void convfmtOffset(int offset) { * @param offset a int */ public void ofmtOffset(int offset) { - queue.add(new Tuple(Opcode.OFMT_OFFSET, offset)); + queue.add(new Tuple.LongTuple(Opcode.OFMT_OFFSET, offset)); } /** @@ -1347,7 +1347,7 @@ public void ofmtOffset(int offset) { * @param offset a int */ public void environOffset(int offset) { - queue.add(new Tuple(Opcode.ENVIRON_OFFSET, offset)); + queue.add(new Tuple.LongTuple(Opcode.ENVIRON_OFFSET, offset)); } /** @@ -1358,7 +1358,7 @@ public void environOffset(int offset) { * @param offset a int */ public void argcOffset(int offset) { - queue.add(new Tuple(Opcode.ARGC_OFFSET, offset)); + queue.add(new Tuple.LongTuple(Opcode.ARGC_OFFSET, offset)); } /** @@ -1369,148 +1369,148 @@ public void argcOffset(int offset) { * @param offset a int */ public void argvOffset(int offset) { - queue.add(new Tuple(Opcode.ARGV_OFFSET, offset)); + queue.add(new Tuple.LongTuple(Opcode.ARGV_OFFSET, offset)); } // JRT-managed special variable helpers /** Pushes the current value of {@code NF} onto the operand stack. */ public void pushNF() { - queue.add(new Tuple(Opcode.PUSH_NF)); + queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_NF)); } /** Assigns the top-of-stack value to {@code NF}. */ public void assignNF() { - queue.add(new Tuple(Opcode.ASSIGN_NF)); + queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_NF)); } /** Pushes the current value of {@code NR} onto the operand stack. */ public void pushNR() { - queue.add(new Tuple(Opcode.PUSH_NR)); + queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_NR)); } /** Assigns the top-of-stack value to {@code NR}. */ public void assignNR() { - queue.add(new Tuple(Opcode.ASSIGN_NR)); + queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_NR)); } /** Pushes the current value of {@code FNR} onto the operand stack. */ public void pushFNR() { - queue.add(new Tuple(Opcode.PUSH_FNR)); + queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_FNR)); } /** Assigns the top-of-stack value to {@code FNR}. */ public void assignFNR() { - queue.add(new Tuple(Opcode.ASSIGN_FNR)); + queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_FNR)); } /** Pushes the current value of {@code FS} onto the operand stack. */ public void pushFS() { - queue.add(new Tuple(Opcode.PUSH_FS)); + queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_FS)); } /** Assigns the top-of-stack value to {@code FS}. */ public void assignFS() { - queue.add(new Tuple(Opcode.ASSIGN_FS)); + queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_FS)); } /** Pushes the current value of {@code RS} onto the operand stack. */ public void pushRS() { - queue.add(new Tuple(Opcode.PUSH_RS)); + queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_RS)); } /** Assigns the top-of-stack value to {@code RS}. */ public void assignRS() { - queue.add(new Tuple(Opcode.ASSIGN_RS)); + queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_RS)); } /** Pushes the current value of {@code OFS} onto the operand stack. */ public void pushOFS() { - queue.add(new Tuple(Opcode.PUSH_OFS)); + queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_OFS)); } /** Assigns the top-of-stack value to {@code OFS}. */ public void assignOFS() { - queue.add(new Tuple(Opcode.ASSIGN_OFS)); + queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_OFS)); } /** Pushes the current value of {@code ORS} onto the operand stack. */ public void pushORS() { - queue.add(new Tuple(Opcode.PUSH_ORS)); + queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_ORS)); } /** Assigns the top-of-stack value to {@code ORS}. */ public void assignORS() { - queue.add(new Tuple(Opcode.ASSIGN_ORS)); + queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_ORS)); } /** Pushes the current value of {@code RSTART} onto the operand stack. */ public void pushRSTART() { - queue.add(new Tuple(Opcode.PUSH_RSTART)); + queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_RSTART)); } /** Assigns the top-of-stack value to {@code RSTART}. */ public void assignRSTART() { - queue.add(new Tuple(Opcode.ASSIGN_RSTART)); + queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_RSTART)); } /** Pushes the current value of {@code RLENGTH} onto the operand stack. */ public void pushRLENGTH() { - queue.add(new Tuple(Opcode.PUSH_RLENGTH)); + queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_RLENGTH)); } /** Assigns the top-of-stack value to {@code RLENGTH}. */ public void assignRLENGTH() { - queue.add(new Tuple(Opcode.ASSIGN_RLENGTH)); + queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_RLENGTH)); } /** Pushes the current value of {@code FILENAME} onto the operand stack. */ public void pushFILENAME() { - queue.add(new Tuple(Opcode.PUSH_FILENAME)); + queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_FILENAME)); } /** Assigns the top-of-stack value to {@code FILENAME}. */ public void assignFILENAME() { - queue.add(new Tuple(Opcode.ASSIGN_FILENAME)); + queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_FILENAME)); } /** Pushes the current value of {@code SUBSEP} onto the operand stack. */ public void pushSUBSEP() { - queue.add(new Tuple(Opcode.PUSH_SUBSEP)); + queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_SUBSEP)); } /** Assigns the top-of-stack value to {@code SUBSEP}. */ public void assignSUBSEP() { - queue.add(new Tuple(Opcode.ASSIGN_SUBSEP)); + queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_SUBSEP)); } /** Pushes the current value of {@code CONVFMT} onto the operand stack. */ public void pushCONVFMT() { - queue.add(new Tuple(Opcode.PUSH_CONVFMT)); + queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_CONVFMT)); } /** Assigns the top-of-stack value to {@code CONVFMT}. */ public void assignCONVFMT() { - queue.add(new Tuple(Opcode.ASSIGN_CONVFMT)); + queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_CONVFMT)); } /** Pushes the current value of {@code OFMT} onto the operand stack. */ public void pushOFMT() { - queue.add(new Tuple(Opcode.PUSH_OFMT)); + queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_OFMT)); } /** Assigns the top-of-stack value to {@code OFMT}. */ public void assignOFMT() { - queue.add(new Tuple(Opcode.ASSIGN_OFMT)); + queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_OFMT)); } /** Pushes the current value of {@code ARGC} onto the operand stack. */ public void pushARGC() { - queue.add(new Tuple(Opcode.PUSH_ARGC)); + queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_ARGC)); } /** Assigns the top-of-stack value to {@code ARGC}. */ public void assignARGC() { - queue.add(new Tuple(Opcode.ASSIGN_ARGC)); + queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_ARGC)); } /** @@ -1519,7 +1519,7 @@ public void assignARGC() { *

*/ public void applyRS() { - queue.add(new Tuple(Opcode.APPLY_RS)); + queue.add(new Tuple.NoOperandTuple(Opcode.APPLY_RS)); } /** @@ -1531,11 +1531,11 @@ public void applyRS() { * @param numFormalParams a int */ public void function(String funcName, int numFormalParams) { - queue.add(new Tuple(Opcode.FUNCTION, funcName, numFormalParams)); + queue.add(new Tuple.FunctionTuple(funcName, numFormalParams)); } // public void callFunction(Address addr, String funcName, int numFormalParams, int numActualParams) { - // queue.add(new Tuple(Opcode.CALL_FUNCTION, addr, funcName, numFormalParams, numActualParams)); } + // queue.add(new Tuple.CallFunctionTuple(addr, funcName, numFormalParams, numActualParams)); } /** *

@@ -1552,7 +1552,7 @@ public void callFunction( String funcName, int numFormalParams, int numActualParams) { - queue.add(new Tuple(Opcode.CALL_FUNCTION, addressSupplier, funcName, numFormalParams, numActualParams)); + queue.add(new Tuple.CallFunctionTuple(addressSupplier, funcName, numFormalParams, numActualParams)); } /** @@ -1561,7 +1561,7 @@ public void callFunction( *

*/ public void setReturnResult() { - queue.add(new Tuple(Opcode.SET_RETURN_RESULT)); + queue.add(new Tuple.NoOperandTuple(Opcode.SET_RETURN_RESULT)); } /** @@ -1570,7 +1570,7 @@ public void setReturnResult() { *

*/ public void returnFromFunction() { - queue.add(new Tuple(Opcode.RETURN_FROM_FUNCTION)); + queue.add(new Tuple.NoOperandTuple(Opcode.RETURN_FROM_FUNCTION)); } /** @@ -1581,7 +1581,7 @@ public void returnFromFunction() { * @param numGlobals a int */ public void setNumGlobals(int numGlobals) { - queue.add(new Tuple(Opcode.SET_NUM_GLOBALS, numGlobals)); + queue.add(new Tuple.CountTuple(Opcode.SET_NUM_GLOBALS, numGlobals)); } /** @@ -1590,7 +1590,7 @@ public void setNumGlobals(int numGlobals) { *

*/ public void close() { - queue.add(new Tuple(Opcode.CLOSE)); + queue.add(new Tuple.NoOperandTuple(Opcode.CLOSE)); } /** @@ -1601,7 +1601,7 @@ public void close() { * @param count a int */ public void applySubsep(int count) { - queue.add(new Tuple(Opcode.APPLY_SUBSEP, count)); + queue.add(new Tuple.CountTuple(Opcode.APPLY_SUBSEP, count)); } /** @@ -1613,14 +1613,14 @@ public void applySubsep(int count) { * @param isGlobal a boolean */ public void deleteArrayElement(int offset, boolean isGlobal) { - queue.add(new Tuple(Opcode.DELETE_ARRAY_ELEMENT, offset, isGlobal)); + queue.add(new Tuple.VariableTuple(Opcode.DELETE_ARRAY_ELEMENT, offset, isGlobal)); } /** * Deletes a stack-provided associative-array element. */ public void deleteMapElement() { - queue.add(new Tuple(Opcode.DELETE_MAP_ELEMENT)); + queue.add(new Tuple.NoOperandTuple(Opcode.DELETE_MAP_ELEMENT)); } /** @@ -1632,7 +1632,7 @@ public void deleteMapElement() { * @param isGlobal a boolean */ public void deleteArray(int offset, boolean isGlobal) { - queue.add(new Tuple(Opcode.DELETE_ARRAY, offset, isGlobal)); + queue.add(new Tuple.VariableTuple(Opcode.DELETE_ARRAY, offset, isGlobal)); } /** @@ -1643,7 +1643,7 @@ public void deleteArray(int offset, boolean isGlobal) { * @param addr a {@link io.jawk.intermediate.Address} object */ public void setExitAddress(Address addr) { - queue.add(new Tuple(Opcode.SET_EXIT_ADDRESS, addr)); + queue.add(new Tuple.AddressTuple(Opcode.SET_EXIT_ADDRESS, addr)); } /** @@ -1654,7 +1654,7 @@ public void setExitAddress(Address addr) { * @param b a boolean */ public void setWithinEndBlocks(boolean b) { - queue.add(new Tuple(Opcode.SET_WITHIN_END_BLOCKS, b)); + queue.add(new Tuple.BooleanTuple(Opcode.SET_WITHIN_END_BLOCKS, b)); } /** @@ -1663,7 +1663,7 @@ public void setWithinEndBlocks(boolean b) { *

*/ public void exitWithCode() { - queue.add(new Tuple(Opcode.EXIT_WITH_CODE)); + queue.add(new Tuple.NoOperandTuple(Opcode.EXIT_WITH_CODE)); } /** @@ -1672,7 +1672,7 @@ public void exitWithCode() { *

*/ public void exitWithoutCode() { - queue.add(new Tuple(Opcode.EXIT_WITHOUT_CODE)); + queue.add(new Tuple.NoOperandTuple(Opcode.EXIT_WITHOUT_CODE)); } /** @@ -1686,7 +1686,7 @@ public void regexp(String regexpStr) { // For literal regexes (created by RegexpAst), precompile the Pattern // and store it alongside the original string to skip runtime compilation. Pattern precompiled = Pattern.compile(regexpStr); - queue.add(new Tuple(Opcode.REGEXP, regexpStr, precompiled)); + queue.add(new Tuple.RegexTuple(regexpStr, precompiled)); } /** @@ -1695,7 +1695,7 @@ public void regexp(String regexpStr) { *

*/ public void conditionPair() { - queue.add(new Tuple(Opcode.CONDITION_PAIR)); + queue.add(new Tuple.NoOperandTuple(Opcode.CONDITION_PAIR)); } /** @@ -1704,14 +1704,14 @@ public void conditionPair() { *

*/ public void isIn() { - queue.add(new Tuple(Opcode.IS_IN)); + queue.add(new Tuple.NoOperandTuple(Opcode.IS_IN)); } /** * Emits a tuple that pushes the current script context onto the stack. */ public void scriptThis() { - queue.add(new Tuple(Opcode.THIS)); + queue.add(new Tuple.NoOperandTuple(Opcode.THIS)); } /** @@ -1722,7 +1722,7 @@ public void scriptThis() { * @param isInitial {@code true} when this tuple opens an extension call sequence */ public void extension(ExtensionFunction function, int paramCount, boolean isInitial) { - queue.add(new Tuple(Opcode.EXTENSION, function, paramCount, isInitial)); + queue.add(new Tuple.ExtensionTuple(function, paramCount, isInitial)); } /** @@ -1896,7 +1896,7 @@ private boolean peepholeOptimizePass() { long fieldIndex = JRT.toLong(literal); Tuple replacement = createGetInputFieldConst( fieldIndex, - tuple.getLineno()); + tuple.getLineNumber()); optimizedQueue.add(replacement); mapFoldedRange(indexMapping, oldIndex, 2, newIndex); oldIndex += 2; @@ -1912,7 +1912,7 @@ private boolean peepholeOptimizePass() { if (secondLiteral != null) { Object folded = foldBinary(literal, secondLiteral, opTuple); if (folded != null) { - Tuple replacement = createLiteralPush(folded, tuple.getLineno()); + Tuple replacement = createLiteralPush(folded, tuple.getLineNumber()); optimizedQueue.add(replacement); mapFoldedRange(indexMapping, oldIndex, 3, newIndex); oldIndex += 3; @@ -1926,7 +1926,7 @@ private boolean peepholeOptimizePass() { Tuple opTuple = original.get(oldIndex + 1); Object folded = foldUnary(literal, opTuple); if (folded != null) { - Tuple replacement = createLiteralPush(folded, tuple.getLineno()); + Tuple replacement = createLiteralPush(folded, tuple.getLineNumber()); optimizedQueue.add(replacement); mapFoldedRange(indexMapping, oldIndex, 2, newIndex); oldIndex += 2; @@ -1967,11 +1967,11 @@ private void mapFoldedRange(int[] indexMapping, int startIndex, int length, int private Object literalValue(Tuple tuple) { switch (tuple.getOpcode()) { case PUSH_LONG: - return Long.valueOf(tuple.getInts()[0]); + return Long.valueOf(((Tuple.PushLongTuple) tuple).getValue()); case PUSH_DOUBLE: - return Double.valueOf(tuple.getDoubles()[0]); + return Double.valueOf(((Tuple.PushDoubleTuple) tuple).getValue()); case PUSH_STRING: - return tuple.getStrings()[0]; + return ((Tuple.PushStringTuple) tuple).getValue(); default: return null; } @@ -2082,20 +2082,20 @@ private Object foldUnary(Object literal, Tuple operation) { private Tuple createLiteralPush(Object value, int lineNumber) { Tuple tuple; if (value instanceof Long) { - tuple = new Tuple(Opcode.PUSH_LONG, ((Long) value).longValue()); + tuple = new Tuple.PushLongTuple(((Long) value).longValue()); } else if (value instanceof Integer) { - tuple = new Tuple(Opcode.PUSH_LONG, ((Integer) value).longValue()); + tuple = new Tuple.PushLongTuple(((Integer) value).longValue()); } else if (value instanceof Double) { - tuple = new Tuple(Opcode.PUSH_DOUBLE, ((Double) value).doubleValue()); + tuple = new Tuple.PushDoubleTuple(((Double) value).doubleValue()); } else if (value instanceof Number) { double d = ((Number) value).doubleValue(); if (JRT.isActuallyLong(d)) { - tuple = new Tuple(Opcode.PUSH_LONG, (long) Math.rint(d)); + tuple = new Tuple.PushLongTuple((long) Math.rint(d)); } else { - tuple = new Tuple(Opcode.PUSH_DOUBLE, d); + tuple = new Tuple.PushDoubleTuple(d); } } else if (value instanceof String) { - tuple = new Tuple(Opcode.PUSH_STRING, (String) value); + tuple = new Tuple.PushStringTuple((String) value); } else { throw new IllegalArgumentException("Unsupported literal value: " + value); } @@ -2104,7 +2104,7 @@ private Tuple createLiteralPush(Object value, int lineNumber) { } private Tuple createGetInputFieldConst(long fieldIndex, int lineNumber) { - Tuple tuple = new Tuple(Opcode.GET_INPUT_FIELD_CONST, fieldIndex); + Tuple tuple = new Tuple.InputFieldTuple(fieldIndex); tuple.setLineNumber(lineNumber); return tuple; } diff --git a/src/main/java/io/jawk/intermediate/PositionTracker.java b/src/main/java/io/jawk/intermediate/PositionTracker.java index c588de0e..5227ab16 100644 --- a/src/main/java/io/jawk/intermediate/PositionTracker.java +++ b/src/main/java/io/jawk/intermediate/PositionTracker.java @@ -1,8 +1,6 @@ package io.jawk.intermediate; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import io.jawk.ext.ExtensionFunction; -import java.util.regex.Pattern; /*- * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ @@ -89,128 +87,13 @@ public Opcode opcode() { return tuple.getOpcode(); } - /** - * Returns the long argument at the specified index without type dispatch. - * - * @param argIdx argument index - * @return the long value - */ - public long intArg(int argIdx) { - return tuple.getInts()[argIdx]; - } - - /** - * Returns the boolean argument at the specified index without type dispatch. - * - * @param argIdx argument index - * @return the boolean value - */ - public boolean boolArg(int argIdx) { - return tuple.getBools()[argIdx]; - } - - /** - * Returns the string argument at the specified index without type dispatch. - * - * @param argIdx argument index - * @return the string value - */ - public String stringArg(int argIdx) { - return tuple.getStrings()[argIdx]; - } - - /** - * Returns the double argument at the specified index without type dispatch. - * - * @param argIdx argument index - * @return the double value - */ - public double doubleArg(int argIdx) { - return tuple.getDoubles()[argIdx]; - } - - /** - * Returns the current tuple argument with runtime type dispatch. - * - * @param argIdx Argument index - * @return Argument value as its boxed Java type - */ - public Object arg(int argIdx) { - Class c = tuple.getTypes()[argIdx]; - if (c == Long.class) { - return tuple.getInts()[argIdx]; - } - if (c == Double.class) { - return tuple.getDoubles()[argIdx]; - } - if (c == String.class) { - return tuple.getStrings()[argIdx]; - } - if (c == Pattern.class) { - return tuple.getPatterns()[argIdx]; - } - if (c == Address.class) { - return tuple.getAddress(); - } - if (c == ExtensionFunction.class) { - return tuple.getExtensionFunction(); - } - throw new Error("Invalid arg type: " + c + ", arg_idx = " + argIdx + ", tuple = " + tuple); - } - - /** - * Returns the regular-expression argument at the supplied index. - * - * @param argIdx Argument index - * @return Pattern argument - */ - public Pattern patternArg(int argIdx) { - if (tuple.getTypes()[argIdx] != Pattern.class) { - throw new Error("Tuple does not contain a Pattern at index " + argIdx + ": " + tuple); - } - return tuple.getPatterns()[argIdx]; - } - - /** - * Returns the extension-function argument stored on the current tuple. - * - * @return Extension function metadata - */ - public ExtensionFunction extensionFunctionArg() { - if (tuple.getTypes()[0] != ExtensionFunction.class) { - throw new Error("Tuple does not contain an extension function: " + tuple); - } - return tuple.getExtensionFunction(); - } - - /** - * Returns the tuple address argument, resolving lazy suppliers when needed. - * - * @return Current tuple address argument - */ - public Address addressArg() { - if (tuple.getAddress() == null) { - tuple.setAddress(tuple.getAddressSupplier().get()); - } - return tuple.getAddress(); - } - - /** - * Returns the class argument stored on the current tuple. - * - * @return Class argument - */ - public Class classArg() { - return tuple.getCls(); - } - /** * Returns the source line number associated with the current tuple. * * @return Tuple source line number */ public int lineNumber() { - return tuple.getLineno(); + return tuple.getLineNumber(); } /** @@ -218,10 +101,20 @@ public int lineNumber() { * * @return Current queue index */ - public int current() { + public int currentIndex() { return idx; } + /** + * Returns the current typed tuple. + * + * @return current tuple + */ + @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "PositionTracker exposes the current immutable instruction node for typed dispatch") + public Tuple current() { + return tuple; + } + /** * Jumps directly to the tuple at the supplied queue index. * diff --git a/src/main/java/io/jawk/intermediate/Tuple.java b/src/main/java/io/jawk/intermediate/Tuple.java index e6e08e14..94cd4640 100644 --- a/src/main/java/io/jawk/intermediate/Tuple.java +++ b/src/main/java/io/jawk/intermediate/Tuple.java @@ -23,259 +23,752 @@ */ import java.io.Serializable; -import java.util.regex.Pattern; +import java.util.List; import java.util.function.Supplier; +import java.util.regex.Pattern; import io.jawk.ext.ExtensionFunction; /** - * Represents a single opcode and its arguments within the tuple stream produced - * by {@link AwkTuples}. While {@code AwkTuples} manages the list of tuples, this - * class models one instruction and up to four typed operands. - *

- * Some tuples defer resolution of a function address until the tuple list is - * finalized; such tuples hold a {@link Supplier} that provides the - * {@link Address} when needed. + * Represents one instruction in the tuple stream produced by {@link AwkTuples}. + * Concrete subclasses carry only the operands required by their opcode or opcode + * group. * * @author Danny Daglas * @see AwkTuples */ -class Tuple implements Serializable { +public abstract class Tuple implements Serializable { private static final long serialVersionUID = 8105941219003992817L; - private Opcode opcode; - private long[] ints = new long[4]; - private boolean[] bools = new boolean[4]; - private double[] doubles = new double[4]; - private String[] strings = new String[4]; - private Pattern[] patterns = new Pattern[4]; - private Class[] types = new Class[4]; - private Address address = null; - private Class cls = null; - private transient Supplier

addressSupplier = null; - private int lineno = -1; + private final Opcode opcode; + private int lineNumber = -1; private Tuple next = null; - private ExtensionFunction extensionFunction; Tuple(Opcode opcode) { this.opcode = opcode; } - Tuple(Opcode opcode, long i1) { - this(opcode); - ints[0] = i1; - types[0] = Long.class; + /** + * Returns this tuple's opcode. + * + * @return opcode executed by the AVM + */ + public final Opcode getOpcode() { + return opcode; } - Tuple(Opcode opcode, long i1, long i2) { - this(opcode, i1); - ints[1] = i2; - types[1] = Long.class; + /** + * Returns this tuple's jump/call address, if it has one. + * + * @return tuple address, or {@code null} + */ + public Address getAddress() { + return null; + } + + /** + * Resolves deferred operands and validates resolved addresses. + * + * @param queue tuple queue used to validate address targets + */ + public void touch(List queue) { + Address address = getAddress(); + if (address == null) { + return; + } + if (address.index() == -1) { + throw new Error("address " + address + " is unresolved"); + } + if (address.index() >= queue.size()) { + throw new Error("address " + address + " doesn't resolve to an actual list element"); + } } - Tuple(Opcode opcode, long i1, boolean b2) { - this(opcode, i1); - bools[1] = b2; - types[1] = Boolean.class; + boolean hasNext() { + return next != null; } - Tuple(Opcode opcode, long i1, boolean b2, boolean b3) { - this(opcode, i1, b2); - bools[2] = b3; - types[2] = Boolean.class; + /** + * Returns the next tuple in execution order. + * + * @return next tuple, or {@code null} at the end of the stream + */ + Tuple getNext() { + return next; } - Tuple(Opcode opcode, double d1) { - this(opcode); - doubles[0] = d1; - types[0] = Double.class; + void setNext(Tuple next) { + this.next = next; } - Tuple(Opcode opcode, String s1) { - this(opcode); - strings[0] = s1; - types[0] = String.class; + void setLineNumber(int lineNumber) { + this.lineNumber = lineNumber; } - Tuple(Opcode opcode, String s1, Pattern p2) { - this(opcode, s1); - patterns[1] = p2; - types[1] = Pattern.class; + /** + * Returns the source line number associated with this tuple. + * + * @return source line number, or {@code -1} when unknown + */ + public int getLineNumber() { + return lineNumber; } - Tuple(Opcode opcode, boolean b1) { - this(opcode); - bools[0] = b1; - types[0] = Boolean.class; + private static String stringArgument(String value) { + return ", \"" + value + '"'; } - Tuple(Opcode opcode, String s1, long i2) { - this(opcode, s1); - ints[1] = i2; - types[1] = Long.class; + private static String patternArgument(Pattern pattern) { + return ", /" + (pattern == null ? "" : pattern.pattern()) + '/'; } - Tuple(Opcode opcode, Address address) { - this(opcode); - this.address = address; - types[0] = Address.class; - } + /** + * Tuple for opcodes without operands. + */ + public static class NoOperandTuple extends Tuple { + private static final long serialVersionUID = 1L; + + NoOperandTuple(Opcode opcode) { + super(opcode); + } - Tuple(Opcode opcode, String strarg, long intarg, boolean boolarg) { - this(opcode, strarg, intarg); - bools[2] = boolarg; - types[2] = Boolean.class; + @Override + public String toString() { + return getOpcode().name(); + } } - Tuple(Opcode opcode, ExtensionFunction function, long intarg, boolean boolarg) { - this(opcode); - this.extensionFunction = function; - types[0] = ExtensionFunction.class; - ints[1] = intarg; - types[1] = Long.class; - bools[2] = boolarg; - types[2] = Boolean.class; + /** + * Tuple for JRT-managed built-in variable operations. + */ + public static final class BuiltinVarTuple extends NoOperandTuple { + private static final long serialVersionUID = 1L; + + BuiltinVarTuple(Opcode opcode) { + super(opcode); + } } - Tuple(Opcode opcode, Supplier
addressSupplier, String s2, long i3, long i4) { - this(opcode); - this.addressSupplier = addressSupplier; - strings[1] = s2; - types[1] = String.class; - ints[2] = i3; - types[2] = Long.class; - ints[3] = i4; - types[3] = Long.class; + /** + * Tuple for a long literal. + */ + public static final class PushLongTuple extends Tuple { + private static final long serialVersionUID = 1L; + private final long value; + + PushLongTuple(long value) { + super(Opcode.PUSH_LONG); + this.value = value; + } + + /** + * Returns the literal value. + * + * @return literal long value + */ + public long getValue() { + return value; + } + + @Override + public String toString() { + return getOpcode().name() + ", " + value; + } } - Tuple(Opcode opcode, Class cls) { - this(opcode); - this.cls = cls; - types[0] = Class.class; + /** + * Tuple for a double literal. + */ + public static final class PushDoubleTuple extends Tuple { + private static final long serialVersionUID = 1L; + private final double value; + + PushDoubleTuple(double value) { + super(Opcode.PUSH_DOUBLE); + this.value = value; + } + + /** + * Returns the literal value. + * + * @return literal double value + */ + public double getValue() { + return value; + } + + @Override + public String toString() { + return getOpcode().name() + ", " + value; + } } - Tuple(Opcode opcode, String s1, String s2) { - this(opcode, s1); - strings[1] = s2; - types[1] = String.class; + /** + * Tuple for a string literal. + */ + public static final class PushStringTuple extends Tuple { + private static final long serialVersionUID = 1L; + private final String value; + + PushStringTuple(String value) { + super(Opcode.PUSH_STRING); + this.value = value; + } + + /** + * Returns the literal value. + * + * @return literal string value + */ + public String getValue() { + return value; + } + + @Override + public String toString() { + return getOpcode().name() + stringArgument(value); + } } - boolean hasNext() { - return next != null; + /** + * Tuple for opcodes whose single operand is a count. + */ + public static class CountTuple extends Tuple { + private static final long serialVersionUID = 1L; + private final long count; + + CountTuple(Opcode opcode, long count) { + super(opcode); + this.count = count; + } + + /** + * Returns the tuple count operand. + * + * @return count operand + */ + public final long getCount() { + return count; + } + + @Override + public String toString() { + return getOpcode().name() + ", " + count; + } } - Tuple getNext() { - return next; + /** + * Tuple for print/printf redirection with an append flag. + */ + public static final class CountAndAppendTuple extends CountTuple { + private static final long serialVersionUID = 1L; + private final boolean append; + + CountAndAppendTuple(Opcode opcode, long count, boolean append) { + super(opcode, count); + this.append = append; + } + + /** + * Indicates whether redirected output should append. + * + * @return {@code true} for append mode + */ + public boolean isAppend() { + return append; + } + + @Override + public String toString() { + return getOpcode().name() + ", " + getCount() + ", " + append; + } } - void setNext(Tuple next) { - this.next = next; + /** + * Tuple for a long operand that is not interpreted by the tuple itself. + */ + public static class LongTuple extends Tuple { + private static final long serialVersionUID = 1L; + private final long value; + + LongTuple(Opcode opcode, long value) { + super(opcode); + this.value = value; + } + + /** + * Returns the long operand. + * + * @return long operand + */ + public final long getValue() { + return value; + } + + @Override + public String toString() { + return getOpcode().name() + ", " + value; + } } - void setLineNumber(int lineNumber) { - this.lineno = lineNumber; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(opcode.name()); - int idx = 0; - while ((idx < types.length) && (types[idx] != null)) { - sb.append(", "); - Class type = types[idx]; - if (type == Long.class) { - sb.append(ints[idx]); - } else if (type == Boolean.class) { - sb.append(bools[idx]); - } else if (type == Double.class) { - sb.append(doubles[idx]); - } else if (type == String.class) { - sb.append('"').append(strings[idx]).append('"'); - } else if (type == Pattern.class) { - // Display regex patterns in /.../ form for readability - Pattern p = patterns[idx]; - sb - .append('/') - .append(p == null ? "" : p.pattern()) - .append('/'); - } else if (type == Address.class) { - sb.append(address); - } else if (type == ExtensionFunction.class) { - sb.append(extensionFunction.getKeyword()); - } else if (type == Class.class) { - sb.append(cls); - } else { - throw new Error("Unknown param type (" + idx + "): " + type); - } - ++idx; + /** + * Tuple for a constant input-field index. + */ + public static final class InputFieldTuple extends LongTuple { + private static final long serialVersionUID = 1L; + + InputFieldTuple(long fieldIndex) { + super(Opcode.GET_INPUT_FIELD_CONST, fieldIndex); + } + + /** + * Returns the constant input-field index. + * + * @return input-field index + */ + public long getFieldIndex() { + return getValue(); } - return sb.toString(); } - public void touch(java.util.List queue) { - if (addressSupplier != null) { - address = addressSupplier.get(); - types[0] = Address.class; + /** + * Tuple for an address operand. + */ + public static class AddressTuple extends Tuple { + private static final long serialVersionUID = 1L; + private Address address; + + AddressTuple(Opcode opcode, Address address) { + super(opcode); + this.address = address; } - if (address != null) { - if (address.index() == -1) { - throw new Error("address " + address + " is unresolved"); - } - if (address.index() >= queue.size()) { - throw new Error("address " + address + " doesn't resolve to an actual list element"); - } + + @Override + public Address getAddress() { + return address; + } + + void setAddress(Address address) { + this.address = address; + } + + @Override + public String toString() { + return getOpcode().name() + ", " + address; } } - Opcode getOpcode() { - return opcode; + /** + * Tuple for variable offset/global operands. + */ + public static class VariableTuple extends Tuple { + private static final long serialVersionUID = 1L; + private final long variableOffset; + private final boolean global; + + VariableTuple(Opcode opcode, long variableOffset, boolean global) { + super(opcode); + this.variableOffset = variableOffset; + this.global = global; + } + + /** + * Returns the variable offset. + * + * @return variable offset + */ + public final long getVariableOffset() { + return variableOffset; + } + + /** + * Indicates whether the variable offset belongs to the global frame. + * + * @return {@code true} for a global variable + */ + public final boolean isGlobal() { + return global; + } + + @Override + public String toString() { + return getOpcode().name() + ", " + variableOffset + ", " + global; + } } - long[] getInts() { - return ints; + /** + * Tuple for scalar compound assignments. + */ + public static final class CompoundAssignTuple extends VariableTuple { + private static final long serialVersionUID = 1L; + + CompoundAssignTuple(Opcode opcode, long variableOffset, boolean global) { + super(opcode, variableOffset, global); + } } - boolean[] getBools() { - return bools; + /** + * Tuple for array compound assignments. + */ + public static final class CompoundAssignArrayTuple extends VariableTuple { + private static final long serialVersionUID = 1L; + + CompoundAssignArrayTuple(Opcode opcode, long variableOffset, boolean global) { + super(opcode, variableOffset, global); + } } - double[] getDoubles() { - return doubles; + /** + * Tuple for stack-provided map element compound assignments. + */ + public static final class CompoundAssignMapElementTuple extends NoOperandTuple { + private static final long serialVersionUID = 1L; + + CompoundAssignMapElementTuple(Opcode opcode) { + super(opcode); + } } - String[] getStrings() { - return strings; + /** + * Tuple for input-field compound assignments. + */ + public static final class CompoundAssignInputFieldTuple extends NoOperandTuple { + private static final long serialVersionUID = 1L; + + CompoundAssignInputFieldTuple(Opcode opcode) { + super(opcode); + } } - Pattern[] getPatterns() { - return patterns; + /** + * Tuple for variable dereference. + */ + public static final class DereferenceTuple extends Tuple { + private static final long serialVersionUID = 1L; + private final long variableOffset; + private final boolean array; + private final boolean global; + + DereferenceTuple(long variableOffset, boolean array, boolean global) { + super(Opcode.DEREFERENCE); + this.variableOffset = variableOffset; + this.array = array; + this.global = global; + } + + /** + * Returns the variable offset. + * + * @return variable offset + */ + public long getVariableOffset() { + return variableOffset; + } + + /** + * Indicates whether this dereference should initialize an array. + * + * @return {@code true} when the variable is an array + */ + public boolean isArray() { + return array; + } + + /** + * Indicates whether the variable offset belongs to the global frame. + * + * @return {@code true} for a global variable + */ + public boolean isGlobal() { + return global; + } + + @Override + public String toString() { + return getOpcode().name() + ", " + variableOffset + ", " + array + ", " + global; + } } - Class[] getTypes() { - return types; + /** + * Tuple for boolean operands. + */ + public static final class BooleanTuple extends Tuple { + private static final long serialVersionUID = 1L; + private final boolean value; + + BooleanTuple(Opcode opcode, boolean value) { + super(opcode); + this.value = value; + } + + /** + * Returns the boolean operand. + * + * @return boolean operand + */ + public boolean getValue() { + return value; + } + + @Override + public String toString() { + return getOpcode().name() + ", " + value; + } } - Address getAddress() { - return address; + /** + * Tuple for sub/gsub against variable-backed values. + */ + public static final class SubstitutionVariableTuple extends VariableTuple { + private static final long serialVersionUID = 1L; + private final boolean globalSubstitution; + + SubstitutionVariableTuple(Opcode opcode, long variableOffset, boolean global, boolean globalSubstitution) { + super(opcode, variableOffset, global); + this.globalSubstitution = globalSubstitution; + } + + /** + * Indicates whether this substitution is global. + * + * @return {@code true} for {@code gsub}, {@code false} for {@code sub} + */ + public boolean isGlobalSubstitution() { + return globalSubstitution; + } + + @Override + public String toString() { + return getOpcode().name() + ", " + getVariableOffset() + ", " + isGlobal() + ", " + globalSubstitution; + } } - Class getCls() { - return cls; + /** + * Tuple for a precompiled literal regular expression. + */ + public static final class RegexTuple extends Tuple { + private static final long serialVersionUID = 1L; + private final String regex; + private final Pattern pattern; + + RegexTuple(String regex, Pattern pattern) { + super(Opcode.REGEXP); + this.regex = regex; + this.pattern = pattern; + } + + /** + * Returns the original regular expression text. + * + * @return regular expression text + */ + public String getRegex() { + return regex; + } + + /** + * Returns the precompiled regular expression. + * + * @return compiled pattern + */ + public Pattern getPattern() { + return pattern; + } + + @Override + public String toString() { + return getOpcode().name() + stringArgument(regex) + patternArgument(pattern); + } } - Supplier
getAddressSupplier() { - return addressSupplier; + /** + * Tuple for a class check. + */ + public static final class ClassTuple extends Tuple { + private static final long serialVersionUID = 1L; + private final Class type; + + ClassTuple(Class type) { + super(Opcode.CHECK_CLASS); + this.type = type; + } + + /** + * Returns the required runtime type. + * + * @return required class + */ + public Class getType() { + return type; + } + + @Override + public String toString() { + return getOpcode().name() + ", " + type; + } } - ExtensionFunction getExtensionFunction() { - return extensionFunction; + /** + * Tuple for function definitions. + */ + public static final class FunctionTuple extends Tuple { + private static final long serialVersionUID = 1L; + private final String functionName; + private final long numFormalParams; + + FunctionTuple(String functionName, long numFormalParams) { + super(Opcode.FUNCTION); + this.functionName = functionName; + this.numFormalParams = numFormalParams; + } + + /** + * Returns the function name. + * + * @return function name + */ + public String getFunctionName() { + return functionName; + } + + /** + * Returns the number of formal parameters. + * + * @return formal parameter count + */ + public long getNumFormalParams() { + return numFormalParams; + } + + @Override + public String toString() { + return getOpcode().name() + stringArgument(functionName) + ", " + numFormalParams; + } } - void setAddress(Address address) { - this.address = address; + /** + * Tuple for function calls. + */ + public static final class CallFunctionTuple extends AddressTuple { + private static final long serialVersionUID = 1L; + private transient Supplier
addressSupplier; + private final String functionName; + private final long numFormalParams; + private final long numActualParams; + + CallFunctionTuple( + Supplier
addressSupplier, + String functionName, + long numFormalParams, + long numActualParams) { + super(Opcode.CALL_FUNCTION, null); + this.addressSupplier = addressSupplier; + this.functionName = functionName; + this.numFormalParams = numFormalParams; + this.numActualParams = numActualParams; + } + + @Override + public Address getAddress() { + Address address = super.getAddress(); + if (address == null && addressSupplier != null) { + address = addressSupplier.get(); + setAddress(address); + } + return address; + } + + @Override + public void touch(List queue) { + getAddress(); + super.touch(queue); + } + + /** + * Returns the function name. + * + * @return function name + */ + public String getFunctionName() { + return functionName; + } + + /** + * Returns the number of formal parameters. + * + * @return formal parameter count + */ + public long getNumFormalParams() { + return numFormalParams; + } + + /** + * Returns the number of actual parameters at this call site. + * + * @return actual parameter count + */ + public long getNumActualParams() { + return numActualParams; + } + + @Override + public String toString() { + return getOpcode().name() + + ", " + + getAddress() + + stringArgument(functionName) + + ", " + + numFormalParams + + ", " + + numActualParams; + } } - int getLineno() { - return lineno; + /** + * Tuple for extension function invocations. + */ + public static final class ExtensionTuple extends Tuple { + private static final long serialVersionUID = 1L; + private final ExtensionFunction function; + private final long argCount; + private final boolean initial; + + ExtensionTuple(ExtensionFunction function, long argCount, boolean initial) { + super(Opcode.EXTENSION); + this.function = function; + this.argCount = argCount; + this.initial = initial; + } + + /** + * Returns the extension function metadata. + * + * @return extension function + */ + public ExtensionFunction getFunction() { + return function; + } + + /** + * Returns the number of extension arguments. + * + * @return argument count + */ + public long getArgCount() { + return argCount; + } + + /** + * Indicates whether this tuple starts an extension call sequence. + * + * @return {@code true} for the initial extension call tuple + */ + public boolean isInitial() { + return initial; + } + + @Override + public String toString() { + return getOpcode().name() + ", " + function.getKeyword() + ", " + argCount + ", " + initial; + } } } diff --git a/src/test/java/io/jawk/AwkTupleOptimizationTest.java b/src/test/java/io/jawk/AwkTupleOptimizationTest.java index 5107a346..a10883ca 100644 --- a/src/test/java/io/jawk/AwkTupleOptimizationTest.java +++ b/src/test/java/io/jawk/AwkTupleOptimizationTest.java @@ -40,6 +40,11 @@ import io.jawk.intermediate.AwkTuples; import io.jawk.intermediate.Opcode; import io.jawk.intermediate.PositionTracker; +import io.jawk.intermediate.Tuple; +import io.jawk.intermediate.Tuple.AddressTuple; +import io.jawk.intermediate.Tuple.PushDoubleTuple; +import io.jawk.intermediate.Tuple.PushLongTuple; +import io.jawk.intermediate.Tuple.PushStringTuple; public class AwkTupleOptimizationTest { @@ -208,7 +213,8 @@ public void retainsRecursiveFunctionBodies() throws Exception { PositionTracker tracker = rawTuples(tuples).top(); while (!tracker.isEOF()) { if (tracker.opcode() == Opcode.CALL_FUNCTION) { - Address address = tracker.addressArg(); + AddressTuple tuple = (AddressTuple) tracker.current(); + Address address = tuple.getAddress(); assertTrue("Call target should be assigned", address.index() >= 0); callTargets.add(Integer.valueOf(address.index())); } @@ -327,7 +333,8 @@ public void retargetsBranchesAwayFromPlaceholderTuples() throws Exception { PositionTracker tracker = rawTuples(tuples).top(); while (!tracker.isEOF()) { if (usesAddress(tracker.opcode())) { - branchTargets.add(Integer.valueOf(tracker.addressArg().index())); + AddressTuple tuple = (AddressTuple) tracker.current(); + branchTargets.add(Integer.valueOf(tuple.getAddress().index())); } tracker.next(); } @@ -505,7 +512,7 @@ private static boolean hasLiteralPush(AwkProgram tuples, Object expected) { if (opcode == Opcode.PUSH_LONG || opcode == Opcode.PUSH_DOUBLE || opcode == Opcode.PUSH_STRING) { - Object value = tracker.arg(0); + Object value = literalValue(tracker.current()); if (expected instanceof Number && value instanceof Number) { double actual = ((Number) value).doubleValue(); if (Double.compare(actual, ((Number) expected).doubleValue()) == 0) { @@ -540,7 +547,7 @@ private static boolean usesAddress(Opcode opcode) { private static Opcode opcodeAt(AwkProgram tuples, int index) { PositionTracker tracker = rawTuples(tuples).top(); while (!tracker.isEOF()) { - if (tracker.current() == index) { + if (tracker.currentIndex() == index) { return tracker.opcode(); } tracker.next(); @@ -548,6 +555,19 @@ private static Opcode opcodeAt(AwkProgram tuples, int index) { throw new AssertionError("No tuple at index " + index); } + private static Object literalValue(Tuple tuple) { + switch (tuple.getOpcode()) { + case PUSH_LONG: + return Long.valueOf(((PushLongTuple) tuple).getValue()); + case PUSH_DOUBLE: + return Double.valueOf(((PushDoubleTuple) tuple).getValue()); + case PUSH_STRING: + return ((PushStringTuple) tuple).getValue(); + default: + throw new AssertionError("Tuple is not a literal push: " + tuple); + } + } + private static AwkTuples rawTuples(AwkProgram program) { return (AwkTuples) program; } From fa457a51dc77f7b5acdbb14a618d5ace7acdf71a Mon Sep 17 00:00:00 2001 From: Bertrand Martin Date: Mon, 11 May 2026 20:41:42 +0200 Subject: [PATCH 2/3] Drop resolved call-function address supplier --- src/main/java/io/jawk/intermediate/Tuple.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/io/jawk/intermediate/Tuple.java b/src/main/java/io/jawk/intermediate/Tuple.java index 94cd4640..d7de0443 100644 --- a/src/main/java/io/jawk/intermediate/Tuple.java +++ b/src/main/java/io/jawk/intermediate/Tuple.java @@ -673,6 +673,7 @@ public Address getAddress() { if (address == null && addressSupplier != null) { address = addressSupplier.get(); setAddress(address); + addressSupplier = null; } return address; } From 090df46e32a39d2cadde661977081986fdff1bc4 Mon Sep 17 00:00:00 2001 From: Bertrand Martin Date: Mon, 11 May 2026 20:51:14 +0200 Subject: [PATCH 3/3] Refine tuple dispatch and clean up PR review comments --- src/main/java/io/jawk/intermediate/AwkTuples.java | 3 --- src/main/java/io/jawk/intermediate/PositionTracker.java | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/io/jawk/intermediate/AwkTuples.java b/src/main/java/io/jawk/intermediate/AwkTuples.java index 65e641ea..a4f8ce0e 100644 --- a/src/main/java/io/jawk/intermediate/AwkTuples.java +++ b/src/main/java/io/jawk/intermediate/AwkTuples.java @@ -1534,9 +1534,6 @@ public void function(String funcName, int numFormalParams) { queue.add(new Tuple.FunctionTuple(funcName, numFormalParams)); } - // public void callFunction(Address addr, String funcName, int numFormalParams, int numActualParams) { - // queue.add(new Tuple.CallFunctionTuple(addr, funcName, numFormalParams, numActualParams)); } - /** *

* callFunction. diff --git a/src/main/java/io/jawk/intermediate/PositionTracker.java b/src/main/java/io/jawk/intermediate/PositionTracker.java index 5227ab16..5ab1e2f1 100644 --- a/src/main/java/io/jawk/intermediate/PositionTracker.java +++ b/src/main/java/io/jawk/intermediate/PositionTracker.java @@ -110,7 +110,7 @@ public int currentIndex() { * * @return current tuple */ - @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "PositionTracker exposes the current immutable instruction node for typed dispatch") + @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "Tuple mutation is limited to package-internal stream construction and deferred address resolution") public Tuple current() { return tuple; }