Fix CFXJSE_FormCalcContext::Substr() behavior. - Update tests according to the XFA spec. - Make it behave as described in the XFA spec so tests pass. - This makes an integer underflow in the implementation go away. BUG=chromium:956985 Change-Id: I5c488de0b04cca5c93fc75e99d4aa5ad09b4c0fe Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/53618 Commit-Queue: Lei Zhang <thestig@chromium.org> Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/fxjs/xfa/cfxjse_formcalc_context.cpp b/fxjs/xfa/cfxjse_formcalc_context.cpp index 387879c..b9971c9 100644 --- a/fxjs/xfa/cfxjse_formcalc_context.cpp +++ b/fxjs/xfa/cfxjse_formcalc_context.cpp
@@ -4272,29 +4272,34 @@ return; } - std::unique_ptr<CFXJSE_Value> stringValue = GetSimpleValue(pThis, args, 0); - std::unique_ptr<CFXJSE_Value> startValue = GetSimpleValue(pThis, args, 1); - std::unique_ptr<CFXJSE_Value> endValue = GetSimpleValue(pThis, args, 2); - if (ValueIsNull(pThis, stringValue.get()) || - (ValueIsNull(pThis, startValue.get())) || - (ValueIsNull(pThis, endValue.get()))) { + std::unique_ptr<CFXJSE_Value> string_value = GetSimpleValue(pThis, args, 0); + std::unique_ptr<CFXJSE_Value> start_value = GetSimpleValue(pThis, args, 1); + std::unique_ptr<CFXJSE_Value> end_value = GetSimpleValue(pThis, args, 2); + if (ValueIsNull(pThis, string_value.get()) || + ValueIsNull(pThis, start_value.get()) || + ValueIsNull(pThis, end_value.get())) { args.GetReturnValue()->SetNull(); return; } - ByteString bsSource = ValueToUTF8String(stringValue.get()); - int32_t iLength = bsSource.GetLength(); + ByteString bsSource = ValueToUTF8String(string_value.get()); + size_t iLength = bsSource.GetLength(); if (iLength == 0) { args.GetReturnValue()->SetString(""); return; } - int32_t iStart = pdfium::clamp( - iLength, 1, static_cast<int32_t>(ValueToFloat(pThis, startValue.get()))); - int32_t iCount = - std::max(0, static_cast<int32_t>(ValueToFloat(pThis, endValue.get()))); + // |start_value| is 1-based. Assume first character if |start_value| is less + // than 1, per spec. Subtract 1 since |iStart| is 0-based. + size_t iStart = std::max(ValueToInteger(pThis, start_value.get()), 1) - 1; + if (iStart >= iLength) { + args.GetReturnValue()->SetString(""); + return; + } - --iStart; + // Negative values are treated as 0. Can't clamp() due to sign mismatches. + size_t iCount = std::max(ValueToInteger(pThis, end_value.get()), 0); + iCount = std::min(iCount, iLength - iStart); args.GetReturnValue()->SetString(bsSource.Mid(iStart, iCount).AsStringView()); }
diff --git a/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp b/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp index ff2c47d..77e3880 100644 --- a/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp +++ b/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp
@@ -1402,13 +1402,13 @@ struct { const char* program; const char* result; - } static const kTests[] = {{"Substr(\"ABCDEFG\", -1, 4)", ""}, - {"Substr(\"ABCDEFG\", 0, 4)", ""}, + } static const kTests[] = {{"Substr(\"ABCDEFG\", -1, 4)", "ABCD"}, + {"Substr(\"ABCDEFG\", 0, 4)", "ABCD"}, {"Substr(\"ABCDEFG\", 3, 4)", "CDEF"}, {"Substr(\"ABCDEFG\", 4, 4)", "DEFG"}, - {"Substr(\"ABCDEFG\", 5, 4)", ""}, - {"Substr(\"ABCDEFG\", 6, 4)", ""}, - {"Substr(\"ABCDEFG\", 7, 4)", ""}, + {"Substr(\"ABCDEFG\", 5, 4)", "EFG"}, + {"Substr(\"ABCDEFG\", 6, 4)", "FG"}, + {"Substr(\"ABCDEFG\", 7, 4)", "G"}, {"Substr(\"ABCDEFG\", 8, 4)", ""}, {"Substr(\"ABCDEFG\", 5, -1)", ""}, {"Substr(\"ABCDEFG\", 5, 0)", ""},