Bug 1935611 - Fix libyuv/libpng link failed for loongarch64. r=glandium,tnikkel,ng
[gecko.git] / dom / xslt / xpath / txCoreFunctionCall.cpp
blob38f0a8d2907aa2554b9a13861d10d3072415452b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/ArrayUtils.h"
7 #include "mozilla/FloatingPoint.h"
9 #include "txExpr.h"
10 #include "txNodeSet.h"
11 #include "nsGkAtoms.h"
12 #include "txIXPathContext.h"
13 #include "nsWhitespaceTokenizer.h"
14 #include "txXPathTreeWalker.h"
15 #include <math.h>
16 #include "txStringUtils.h"
17 #include "txXMLUtils.h"
19 using namespace mozilla;
21 struct txCoreFunctionDescriptor {
22 const int8_t mMinParams;
23 const int8_t mMaxParams;
24 const Expr::ResultType mReturnType;
25 const nsStaticAtom* const mName;
28 // This must be ordered in the same order as txCoreFunctionCall::eType.
29 // If you change one, change the other.
30 static const txCoreFunctionDescriptor descriptTable[] = {
31 {1, 1, Expr::NUMBER_RESULT, nsGkAtoms::count}, // COUNT
32 {1, 1, Expr::NODESET_RESULT, nsGkAtoms::id}, // ID
33 {0, 0, Expr::NUMBER_RESULT, nsGkAtoms::last}, // LAST
34 {0, 1, Expr::STRING_RESULT, nsGkAtoms::localName}, // LOCAL_NAME
35 {0, 1, Expr::STRING_RESULT, nsGkAtoms::namespaceUri}, // NAMESPACE_URI
36 {0, 1, Expr::STRING_RESULT, nsGkAtoms::name}, // NAME
37 {0, 0, Expr::NUMBER_RESULT, nsGkAtoms::position}, // POSITION
39 {2, -1, Expr::STRING_RESULT, nsGkAtoms::concat}, // CONCAT
40 {2, 2, Expr::BOOLEAN_RESULT, nsGkAtoms::contains}, // CONTAINS
41 {0, 1, Expr::STRING_RESULT, nsGkAtoms::normalizeSpace}, // NORMALIZE_SPACE
42 {2, 2, Expr::BOOLEAN_RESULT, nsGkAtoms::startsWith}, // STARTS_WITH
43 {0, 1, Expr::STRING_RESULT, nsGkAtoms::string}, // STRING
44 {0, 1, Expr::NUMBER_RESULT, nsGkAtoms::stringLength}, // STRING_LENGTH
45 {2, 3, Expr::STRING_RESULT, nsGkAtoms::substring}, // SUBSTRING
46 {2, 2, Expr::STRING_RESULT, nsGkAtoms::substringAfter}, // SUBSTRING_AFTER
47 {2, 2, Expr::STRING_RESULT,
48 nsGkAtoms::substringBefore}, // SUBSTRING_BEFORE
49 {3, 3, Expr::STRING_RESULT, nsGkAtoms::translate}, // TRANSLATE
51 {0, 1, Expr::NUMBER_RESULT, nsGkAtoms::number}, // NUMBER
52 {1, 1, Expr::NUMBER_RESULT, nsGkAtoms::round}, // ROUND
53 {1, 1, Expr::NUMBER_RESULT, nsGkAtoms::floor}, // FLOOR
54 {1, 1, Expr::NUMBER_RESULT, nsGkAtoms::ceiling}, // CEILING
55 {1, 1, Expr::NUMBER_RESULT, nsGkAtoms::sum}, // SUM
57 {1, 1, Expr::BOOLEAN_RESULT, nsGkAtoms::boolean}, // BOOLEAN
58 {0, 0, Expr::BOOLEAN_RESULT, nsGkAtoms::_false}, // _FALSE
59 {1, 1, Expr::BOOLEAN_RESULT, nsGkAtoms::lang}, // LANG
60 {1, 1, Expr::BOOLEAN_RESULT, nsGkAtoms::_not}, // _NOT
61 {0, 0, Expr::BOOLEAN_RESULT, nsGkAtoms::_true} // _TRUE
65 * Evaluates this Expr based on the given context node and processor state
66 * @param context the context node for evaluation of this Expr
67 * @param ps the ContextState containing the stack information needed
68 * for evaluation
69 * @return the result of the evaluation
71 nsresult txCoreFunctionCall::evaluate(txIEvalContext* aContext,
72 txAExprResult** aResult) {
73 *aResult = nullptr;
75 if (!requireParams(descriptTable[mType].mMinParams,
76 descriptTable[mType].mMaxParams, aContext)) {
77 return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT;
80 nsresult rv = NS_OK;
81 switch (mType) {
82 case COUNT: {
83 RefPtr<txNodeSet> nodes;
84 rv = evaluateToNodeSet(mParams[0], aContext, getter_AddRefs(nodes));
85 NS_ENSURE_SUCCESS(rv, rv);
87 return aContext->recycler()->getNumberResult(nodes->size(), aResult);
89 case ID: {
90 RefPtr<txAExprResult> exprResult;
91 rv = mParams[0]->evaluate(aContext, getter_AddRefs(exprResult));
92 NS_ENSURE_SUCCESS(rv, rv);
94 RefPtr<txNodeSet> resultSet;
95 rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet));
96 NS_ENSURE_SUCCESS(rv, rv);
98 txXPathTreeWalker walker(aContext->getContextNode());
100 if (exprResult->getResultType() == txAExprResult::NODESET) {
101 txNodeSet* nodes =
102 static_cast<txNodeSet*>(static_cast<txAExprResult*>(exprResult));
103 int32_t i;
104 for (i = 0; i < nodes->size(); ++i) {
105 nsAutoString idList;
106 txXPathNodeUtils::appendNodeValue(nodes->get(i), idList);
107 nsWhitespaceTokenizer tokenizer(idList);
108 while (tokenizer.hasMoreTokens()) {
109 if (walker.moveToElementById(tokenizer.nextToken())) {
110 resultSet->add(walker.getCurrentPosition());
114 } else {
115 nsAutoString idList;
116 exprResult->stringValue(idList);
117 nsWhitespaceTokenizer tokenizer(idList);
118 while (tokenizer.hasMoreTokens()) {
119 if (walker.moveToElementById(tokenizer.nextToken())) {
120 resultSet->add(walker.getCurrentPosition());
125 *aResult = resultSet;
126 NS_ADDREF(*aResult);
128 return NS_OK;
130 case LAST: {
131 return aContext->recycler()->getNumberResult(aContext->size(), aResult);
133 case LOCAL_NAME:
134 case NAME:
135 case NAMESPACE_URI: {
136 // Check for optional arg
137 RefPtr<txNodeSet> nodes;
138 if (!mParams.IsEmpty()) {
139 rv = evaluateToNodeSet(mParams[0], aContext, getter_AddRefs(nodes));
140 NS_ENSURE_SUCCESS(rv, rv);
142 if (nodes->isEmpty()) {
143 aContext->recycler()->getEmptyStringResult(aResult);
145 return NS_OK;
149 const txXPathNode& node =
150 nodes ? nodes->get(0) : aContext->getContextNode();
151 switch (mType) {
152 case LOCAL_NAME: {
153 StringResult* strRes = nullptr;
154 rv = aContext->recycler()->getStringResult(&strRes);
155 NS_ENSURE_SUCCESS(rv, rv);
157 *aResult = strRes;
158 txXPathNodeUtils::getLocalName(node, strRes->mValue);
160 return NS_OK;
162 case NAMESPACE_URI: {
163 StringResult* strRes = nullptr;
164 rv = aContext->recycler()->getStringResult(&strRes);
165 NS_ENSURE_SUCCESS(rv, rv);
167 *aResult = strRes;
168 txXPathNodeUtils::getNamespaceURI(node, strRes->mValue);
170 return NS_OK;
172 case NAME: {
173 // XXX Namespace: namespaces have a name
174 if (txXPathNodeUtils::isAttribute(node) ||
175 txXPathNodeUtils::isElement(node) ||
176 txXPathNodeUtils::isProcessingInstruction(node)) {
177 StringResult* strRes = nullptr;
178 rv = aContext->recycler()->getStringResult(&strRes);
179 NS_ENSURE_SUCCESS(rv, rv);
181 *aResult = strRes;
182 txXPathNodeUtils::getNodeName(node, strRes->mValue);
183 } else {
184 aContext->recycler()->getEmptyStringResult(aResult);
187 return NS_OK;
189 default: {
190 MOZ_CRASH("Unexpected mType?!");
193 MOZ_CRASH("Inner mType switch should have returned!");
195 case POSITION: {
196 return aContext->recycler()->getNumberResult(aContext->position(),
197 aResult);
200 // String functions
202 case CONCAT: {
203 RefPtr<StringResult> strRes;
204 rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
205 NS_ENSURE_SUCCESS(rv, rv);
207 uint32_t i, len = mParams.Length();
208 for (i = 0; i < len; ++i) {
209 rv = mParams[i]->evaluateToString(aContext, strRes->mValue);
210 NS_ENSURE_SUCCESS(rv, rv);
213 NS_ADDREF(*aResult = strRes);
215 return NS_OK;
217 case CONTAINS: {
218 nsAutoString arg2;
219 rv = mParams[1]->evaluateToString(aContext, arg2);
220 NS_ENSURE_SUCCESS(rv, rv);
222 if (arg2.IsEmpty()) {
223 aContext->recycler()->getBoolResult(true, aResult);
224 } else {
225 nsAutoString arg1;
226 rv = mParams[0]->evaluateToString(aContext, arg1);
227 NS_ENSURE_SUCCESS(rv, rv);
229 aContext->recycler()->getBoolResult(FindInReadable(arg2, arg1),
230 aResult);
233 return NS_OK;
235 case NORMALIZE_SPACE: {
236 nsAutoString resultStr;
237 if (!mParams.IsEmpty()) {
238 rv = mParams[0]->evaluateToString(aContext, resultStr);
239 NS_ENSURE_SUCCESS(rv, rv);
240 } else {
241 txXPathNodeUtils::appendNodeValue(aContext->getContextNode(),
242 resultStr);
245 RefPtr<StringResult> strRes;
246 rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
247 NS_ENSURE_SUCCESS(rv, rv);
249 bool addSpace = false;
250 bool first = true;
251 strRes->mValue.SetCapacity(resultStr.Length());
252 char16_t c;
253 uint32_t src;
254 for (src = 0; src < resultStr.Length(); src++) {
255 c = resultStr.CharAt(src);
256 if (XMLUtils::isWhitespace(c)) {
257 addSpace = true;
258 } else {
259 if (addSpace && !first) strRes->mValue.Append(char16_t(' '));
261 strRes->mValue.Append(c);
262 addSpace = false;
263 first = false;
266 *aResult = strRes;
267 NS_ADDREF(*aResult);
269 return NS_OK;
271 case STARTS_WITH: {
272 nsAutoString arg2;
273 rv = mParams[1]->evaluateToString(aContext, arg2);
274 NS_ENSURE_SUCCESS(rv, rv);
276 bool result = false;
277 if (arg2.IsEmpty()) {
278 result = true;
279 } else {
280 nsAutoString arg1;
281 rv = mParams[0]->evaluateToString(aContext, arg1);
282 NS_ENSURE_SUCCESS(rv, rv);
284 result = StringBeginsWith(arg1, arg2);
287 aContext->recycler()->getBoolResult(result, aResult);
289 return NS_OK;
291 case STRING: {
292 RefPtr<StringResult> strRes;
293 rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
294 NS_ENSURE_SUCCESS(rv, rv);
296 if (!mParams.IsEmpty()) {
297 rv = mParams[0]->evaluateToString(aContext, strRes->mValue);
298 NS_ENSURE_SUCCESS(rv, rv);
299 } else {
300 txXPathNodeUtils::appendNodeValue(aContext->getContextNode(),
301 strRes->mValue);
304 NS_ADDREF(*aResult = strRes);
306 return NS_OK;
308 case STRING_LENGTH: {
309 nsAutoString resultStr;
310 if (!mParams.IsEmpty()) {
311 rv = mParams[0]->evaluateToString(aContext, resultStr);
312 NS_ENSURE_SUCCESS(rv, rv);
313 } else {
314 txXPathNodeUtils::appendNodeValue(aContext->getContextNode(),
315 resultStr);
317 rv = aContext->recycler()->getNumberResult(resultStr.Length(), aResult);
318 NS_ENSURE_SUCCESS(rv, rv);
320 return NS_OK;
322 case SUBSTRING: {
323 nsAutoString src;
324 rv = mParams[0]->evaluateToString(aContext, src);
325 NS_ENSURE_SUCCESS(rv, rv);
327 double start;
328 rv = evaluateToNumber(mParams[1], aContext, &start);
329 NS_ENSURE_SUCCESS(rv, rv);
331 // check for NaN or +/-Inf
332 if (std::isnan(start) || std::isinf(start) ||
333 start >= src.Length() + 0.5) {
334 aContext->recycler()->getEmptyStringResult(aResult);
336 return NS_OK;
339 start = floor(start + 0.5) - 1;
341 double end;
342 if (mParams.Length() == 3) {
343 rv = evaluateToNumber(mParams[2], aContext, &end);
344 NS_ENSURE_SUCCESS(rv, rv);
346 end += start;
347 if (std::isnan(end) || end < 0) {
348 aContext->recycler()->getEmptyStringResult(aResult);
350 return NS_OK;
353 if (end > src.Length())
354 end = src.Length();
355 else
356 end = floor(end + 0.5);
357 } else {
358 end = src.Length();
361 if (start < 0) start = 0;
363 if (start > end) {
364 aContext->recycler()->getEmptyStringResult(aResult);
366 return NS_OK;
369 return aContext->recycler()->getStringResult(
370 Substring(src, (uint32_t)start, (uint32_t)(end - start)), aResult);
372 case SUBSTRING_AFTER: {
373 nsAutoString arg1;
374 rv = mParams[0]->evaluateToString(aContext, arg1);
375 NS_ENSURE_SUCCESS(rv, rv);
377 nsAutoString arg2;
378 rv = mParams[1]->evaluateToString(aContext, arg2);
379 NS_ENSURE_SUCCESS(rv, rv);
381 if (arg2.IsEmpty()) {
382 return aContext->recycler()->getStringResult(arg1, aResult);
385 int32_t idx = arg1.Find(arg2);
386 if (idx == kNotFound) {
387 aContext->recycler()->getEmptyStringResult(aResult);
389 return NS_OK;
392 const nsAString& result = Substring(arg1, idx + arg2.Length());
393 return aContext->recycler()->getStringResult(result, aResult);
395 case SUBSTRING_BEFORE: {
396 nsAutoString arg2;
397 rv = mParams[1]->evaluateToString(aContext, arg2);
398 NS_ENSURE_SUCCESS(rv, rv);
400 if (arg2.IsEmpty()) {
401 aContext->recycler()->getEmptyStringResult(aResult);
403 return NS_OK;
406 nsAutoString arg1;
407 rv = mParams[0]->evaluateToString(aContext, arg1);
408 NS_ENSURE_SUCCESS(rv, rv);
410 int32_t idx = arg1.Find(arg2);
411 if (idx == kNotFound) {
412 aContext->recycler()->getEmptyStringResult(aResult);
414 return NS_OK;
417 return aContext->recycler()->getStringResult(StringHead(arg1, idx),
418 aResult);
420 case TRANSLATE: {
421 nsAutoString src;
422 rv = mParams[0]->evaluateToString(aContext, src);
423 NS_ENSURE_SUCCESS(rv, rv);
425 if (src.IsEmpty()) {
426 aContext->recycler()->getEmptyStringResult(aResult);
428 return NS_OK;
431 RefPtr<StringResult> strRes;
432 rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
433 NS_ENSURE_SUCCESS(rv, rv);
435 strRes->mValue.SetCapacity(src.Length());
437 nsAutoString oldChars, newChars;
438 rv = mParams[1]->evaluateToString(aContext, oldChars);
439 NS_ENSURE_SUCCESS(rv, rv);
441 rv = mParams[2]->evaluateToString(aContext, newChars);
442 NS_ENSURE_SUCCESS(rv, rv);
444 uint32_t i;
445 int32_t newCharsLength = (int32_t)newChars.Length();
446 for (i = 0; i < src.Length(); i++) {
447 int32_t idx = oldChars.FindChar(src.CharAt(i));
448 if (idx != kNotFound) {
449 if (idx < newCharsLength)
450 strRes->mValue.Append(newChars.CharAt((uint32_t)idx));
451 } else {
452 strRes->mValue.Append(src.CharAt(i));
456 NS_ADDREF(*aResult = strRes);
458 return NS_OK;
461 // Number functions
463 case NUMBER: {
464 double res;
465 if (!mParams.IsEmpty()) {
466 rv = evaluateToNumber(mParams[0], aContext, &res);
467 NS_ENSURE_SUCCESS(rv, rv);
468 } else {
469 nsAutoString resultStr;
470 txXPathNodeUtils::appendNodeValue(aContext->getContextNode(),
471 resultStr);
472 res = txDouble::toDouble(resultStr);
474 return aContext->recycler()->getNumberResult(res, aResult);
476 case ROUND: {
477 double dbl;
478 rv = evaluateToNumber(mParams[0], aContext, &dbl);
479 NS_ENSURE_SUCCESS(rv, rv);
481 if (std::isfinite(dbl)) {
482 if (mozilla::IsNegative(dbl) && dbl >= -0.5) {
483 dbl *= 0;
484 } else {
485 dbl = floor(dbl + 0.5);
489 return aContext->recycler()->getNumberResult(dbl, aResult);
491 case FLOOR: {
492 double dbl;
493 rv = evaluateToNumber(mParams[0], aContext, &dbl);
494 NS_ENSURE_SUCCESS(rv, rv);
496 if (std::isfinite(dbl) && !mozilla::IsNegativeZero(dbl)) dbl = floor(dbl);
498 return aContext->recycler()->getNumberResult(dbl, aResult);
500 case CEILING: {
501 double dbl;
502 rv = evaluateToNumber(mParams[0], aContext, &dbl);
503 NS_ENSURE_SUCCESS(rv, rv);
505 if (std::isfinite(dbl)) {
506 if (mozilla::IsNegative(dbl) && dbl > -1)
507 dbl *= 0;
508 else
509 dbl = ceil(dbl);
512 return aContext->recycler()->getNumberResult(dbl, aResult);
514 case SUM: {
515 RefPtr<txNodeSet> nodes;
516 nsresult rv =
517 evaluateToNodeSet(mParams[0], aContext, getter_AddRefs(nodes));
518 NS_ENSURE_SUCCESS(rv, rv);
520 double res = 0;
521 int32_t i;
522 for (i = 0; i < nodes->size(); ++i) {
523 nsAutoString resultStr;
524 txXPathNodeUtils::appendNodeValue(nodes->get(i), resultStr);
525 res += txDouble::toDouble(resultStr);
527 return aContext->recycler()->getNumberResult(res, aResult);
530 // Boolean functions
532 case BOOLEAN: {
533 bool result;
534 nsresult rv = mParams[0]->evaluateToBool(aContext, result);
535 NS_ENSURE_SUCCESS(rv, rv);
537 aContext->recycler()->getBoolResult(result, aResult);
539 return NS_OK;
541 case _FALSE: {
542 aContext->recycler()->getBoolResult(false, aResult);
544 return NS_OK;
546 case LANG: {
547 txXPathTreeWalker walker(aContext->getContextNode());
549 nsAutoString lang;
550 bool found;
551 do {
552 found = walker.getAttr(nsGkAtoms::lang, kNameSpaceID_XML, lang);
553 } while (!found && walker.moveToParent());
555 if (!found) {
556 aContext->recycler()->getBoolResult(false, aResult);
558 return NS_OK;
561 nsAutoString arg;
562 rv = mParams[0]->evaluateToString(aContext, arg);
563 NS_ENSURE_SUCCESS(rv, rv);
565 bool result =
566 StringBeginsWith(lang, arg, nsCaseInsensitiveStringComparator) &&
567 (lang.Length() == arg.Length() || lang.CharAt(arg.Length()) == '-');
569 aContext->recycler()->getBoolResult(result, aResult);
571 return NS_OK;
573 case _NOT: {
574 bool result;
575 rv = mParams[0]->evaluateToBool(aContext, result);
576 NS_ENSURE_SUCCESS(rv, rv);
578 aContext->recycler()->getBoolResult(!result, aResult);
580 return NS_OK;
582 case _TRUE: {
583 aContext->recycler()->getBoolResult(true, aResult);
585 return NS_OK;
589 aContext->receiveError(u"Internal error"_ns, NS_ERROR_UNEXPECTED);
590 return NS_ERROR_UNEXPECTED;
593 Expr::ResultType txCoreFunctionCall::getReturnType() {
594 return descriptTable[mType].mReturnType;
597 bool txCoreFunctionCall::isSensitiveTo(ContextSensitivity aContext) {
598 switch (mType) {
599 case COUNT:
600 case CONCAT:
601 case CONTAINS:
602 case STARTS_WITH:
603 case SUBSTRING:
604 case SUBSTRING_AFTER:
605 case SUBSTRING_BEFORE:
606 case TRANSLATE:
607 case ROUND:
608 case FLOOR:
609 case CEILING:
610 case SUM:
611 case BOOLEAN:
612 case _NOT:
613 case _FALSE:
614 case _TRUE: {
615 return argsSensitiveTo(aContext);
617 case ID: {
618 return (aContext & NODE_CONTEXT) || argsSensitiveTo(aContext);
620 case LAST: {
621 return !!(aContext & SIZE_CONTEXT);
623 case LOCAL_NAME:
624 case NAME:
625 case NAMESPACE_URI:
626 case NORMALIZE_SPACE:
627 case STRING:
628 case STRING_LENGTH:
629 case NUMBER: {
630 if (mParams.IsEmpty()) {
631 return !!(aContext & NODE_CONTEXT);
633 return argsSensitiveTo(aContext);
635 case POSITION: {
636 return !!(aContext & POSITION_CONTEXT);
638 case LANG: {
639 return (aContext & NODE_CONTEXT) || argsSensitiveTo(aContext);
643 MOZ_ASSERT_UNREACHABLE("how'd we get here?");
644 return true;
647 // static
648 bool txCoreFunctionCall::getTypeFromAtom(nsAtom* aName, eType& aType) {
649 uint32_t i;
650 for (i = 0; i < std::size(descriptTable); ++i) {
651 if (aName == descriptTable[i].mName) {
652 aType = static_cast<eType>(i);
654 return true;
658 return false;
661 #ifdef TX_TO_STRING
662 void txCoreFunctionCall::appendName(nsAString& aDest) {
663 aDest.Append(descriptTable[mType].mName->GetUTF16String());
665 #endif