txCoreFunctionCall.cpp (19630B)
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/. */ 5 6 #include <math.h> 7 8 #include "mozilla/FloatingPoint.h" 9 #include "nsGkAtoms.h" 10 #include "nsWhitespaceTokenizer.h" 11 #include "txExpr.h" 12 #include "txIXPathContext.h" 13 #include "txNodeSet.h" 14 #include "txStringUtils.h" 15 #include "txXMLUtils.h" 16 #include "txXPathTreeWalker.h" 17 18 using namespace mozilla; 19 20 struct txCoreFunctionDescriptor { 21 const int8_t mMinParams; 22 const int8_t mMaxParams; 23 const Expr::ResultType mReturnType; 24 const nsStaticAtom* const mName; 25 }; 26 27 // This must be ordered in the same order as txCoreFunctionCall::eType. 28 // If you change one, change the other. 29 static const txCoreFunctionDescriptor descriptTable[] = { 30 {1, 1, Expr::NUMBER_RESULT, nsGkAtoms::count}, // COUNT 31 {1, 1, Expr::NODESET_RESULT, nsGkAtoms::id}, // ID 32 {0, 0, Expr::NUMBER_RESULT, nsGkAtoms::last}, // LAST 33 {0, 1, Expr::STRING_RESULT, nsGkAtoms::localName}, // LOCAL_NAME 34 {0, 1, Expr::STRING_RESULT, nsGkAtoms::namespaceUri}, // NAMESPACE_URI 35 {0, 1, Expr::STRING_RESULT, nsGkAtoms::name}, // NAME 36 {0, 0, Expr::NUMBER_RESULT, nsGkAtoms::position}, // POSITION 37 38 {2, -1, Expr::STRING_RESULT, nsGkAtoms::concat}, // CONCAT 39 {2, 2, Expr::BOOLEAN_RESULT, nsGkAtoms::contains}, // CONTAINS 40 {0, 1, Expr::STRING_RESULT, nsGkAtoms::normalizeSpace}, // NORMALIZE_SPACE 41 {2, 2, Expr::BOOLEAN_RESULT, nsGkAtoms::startsWith}, // STARTS_WITH 42 {0, 1, Expr::STRING_RESULT, nsGkAtoms::string}, // STRING 43 {0, 1, Expr::NUMBER_RESULT, nsGkAtoms::stringLength}, // STRING_LENGTH 44 {2, 3, Expr::STRING_RESULT, nsGkAtoms::substring}, // SUBSTRING 45 {2, 2, Expr::STRING_RESULT, nsGkAtoms::substringAfter}, // SUBSTRING_AFTER 46 {2, 2, Expr::STRING_RESULT, 47 nsGkAtoms::substringBefore}, // SUBSTRING_BEFORE 48 {3, 3, Expr::STRING_RESULT, nsGkAtoms::translate}, // TRANSLATE 49 50 {0, 1, Expr::NUMBER_RESULT, nsGkAtoms::number}, // NUMBER 51 {1, 1, Expr::NUMBER_RESULT, nsGkAtoms::round}, // ROUND 52 {1, 1, Expr::NUMBER_RESULT, nsGkAtoms::floor}, // FLOOR 53 {1, 1, Expr::NUMBER_RESULT, nsGkAtoms::ceiling}, // CEILING 54 {1, 1, Expr::NUMBER_RESULT, nsGkAtoms::sum}, // SUM 55 56 {1, 1, Expr::BOOLEAN_RESULT, nsGkAtoms::boolean}, // BOOLEAN 57 {0, 0, Expr::BOOLEAN_RESULT, nsGkAtoms::_false}, // _FALSE 58 {1, 1, Expr::BOOLEAN_RESULT, nsGkAtoms::lang}, // LANG 59 {1, 1, Expr::BOOLEAN_RESULT, nsGkAtoms::_not}, // _NOT 60 {0, 0, Expr::BOOLEAN_RESULT, nsGkAtoms::_true} // _TRUE 61 }; 62 63 /* 64 * Evaluates this Expr based on the given context node and processor state 65 * @param context the context node for evaluation of this Expr 66 * @param ps the ContextState containing the stack information needed 67 * for evaluation 68 * @return the result of the evaluation 69 */ 70 nsresult txCoreFunctionCall::evaluate(txIEvalContext* aContext, 71 txAExprResult** aResult) { 72 *aResult = nullptr; 73 74 if (!requireParams(descriptTable[mType].mMinParams, 75 descriptTable[mType].mMaxParams, aContext)) { 76 return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; 77 } 78 79 nsresult rv = NS_OK; 80 switch (mType) { 81 case COUNT: { 82 RefPtr<txNodeSet> nodes; 83 rv = evaluateToNodeSet(mParams[0], aContext, getter_AddRefs(nodes)); 84 NS_ENSURE_SUCCESS(rv, rv); 85 86 return aContext->recycler()->getNumberResult(nodes->size(), aResult); 87 } 88 case ID: { 89 RefPtr<txAExprResult> exprResult; 90 rv = mParams[0]->evaluate(aContext, getter_AddRefs(exprResult)); 91 NS_ENSURE_SUCCESS(rv, rv); 92 93 RefPtr<txNodeSet> resultSet; 94 rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet)); 95 NS_ENSURE_SUCCESS(rv, rv); 96 97 txXPathTreeWalker walker(aContext->getContextNode()); 98 99 if (exprResult->getResultType() == txAExprResult::NODESET) { 100 txNodeSet* nodes = 101 static_cast<txNodeSet*>(static_cast<txAExprResult*>(exprResult)); 102 int32_t i; 103 for (i = 0; i < nodes->size(); ++i) { 104 nsAutoString idList; 105 txXPathNodeUtils::appendNodeValue(nodes->get(i), idList); 106 nsWhitespaceTokenizer tokenizer(idList); 107 while (tokenizer.hasMoreTokens()) { 108 if (walker.moveToElementById(tokenizer.nextToken())) { 109 resultSet->add(walker.getCurrentPosition()); 110 } 111 } 112 } 113 } else { 114 nsAutoString idList; 115 exprResult->stringValue(idList); 116 nsWhitespaceTokenizer tokenizer(idList); 117 while (tokenizer.hasMoreTokens()) { 118 if (walker.moveToElementById(tokenizer.nextToken())) { 119 resultSet->add(walker.getCurrentPosition()); 120 } 121 } 122 } 123 124 *aResult = resultSet; 125 NS_ADDREF(*aResult); 126 127 return NS_OK; 128 } 129 case LAST: { 130 return aContext->recycler()->getNumberResult(aContext->size(), aResult); 131 } 132 case LOCAL_NAME: 133 case NAME: 134 case NAMESPACE_URI: { 135 // Check for optional arg 136 RefPtr<txNodeSet> nodes; 137 if (!mParams.IsEmpty()) { 138 rv = evaluateToNodeSet(mParams[0], aContext, getter_AddRefs(nodes)); 139 NS_ENSURE_SUCCESS(rv, rv); 140 141 if (nodes->isEmpty()) { 142 aContext->recycler()->getEmptyStringResult(aResult); 143 144 return NS_OK; 145 } 146 } 147 148 const txXPathNode& node = 149 nodes ? nodes->get(0) : aContext->getContextNode(); 150 switch (mType) { 151 case LOCAL_NAME: { 152 StringResult* strRes = nullptr; 153 rv = aContext->recycler()->getStringResult(&strRes); 154 NS_ENSURE_SUCCESS(rv, rv); 155 156 *aResult = strRes; 157 txXPathNodeUtils::getLocalName(node, strRes->mValue); 158 159 return NS_OK; 160 } 161 case NAMESPACE_URI: { 162 StringResult* strRes = nullptr; 163 rv = aContext->recycler()->getStringResult(&strRes); 164 NS_ENSURE_SUCCESS(rv, rv); 165 166 *aResult = strRes; 167 txXPathNodeUtils::getNamespaceURI(node, strRes->mValue); 168 169 return NS_OK; 170 } 171 case NAME: { 172 // XXX Namespace: namespaces have a name 173 if (txXPathNodeUtils::isAttribute(node) || 174 txXPathNodeUtils::isElement(node) || 175 txXPathNodeUtils::isProcessingInstruction(node)) { 176 StringResult* strRes = nullptr; 177 rv = aContext->recycler()->getStringResult(&strRes); 178 NS_ENSURE_SUCCESS(rv, rv); 179 180 *aResult = strRes; 181 txXPathNodeUtils::getNodeName(node, strRes->mValue); 182 } else { 183 aContext->recycler()->getEmptyStringResult(aResult); 184 } 185 186 return NS_OK; 187 } 188 default: { 189 MOZ_CRASH("Unexpected mType?!"); 190 } 191 } 192 MOZ_CRASH("Inner mType switch should have returned!"); 193 } 194 case POSITION: { 195 return aContext->recycler()->getNumberResult(aContext->position(), 196 aResult); 197 } 198 199 // String functions 200 201 case CONCAT: { 202 RefPtr<StringResult> strRes; 203 rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes)); 204 NS_ENSURE_SUCCESS(rv, rv); 205 206 uint32_t i, len = mParams.Length(); 207 for (i = 0; i < len; ++i) { 208 rv = mParams[i]->evaluateToString(aContext, strRes->mValue); 209 NS_ENSURE_SUCCESS(rv, rv); 210 } 211 212 NS_ADDREF(*aResult = strRes); 213 214 return NS_OK; 215 } 216 case CONTAINS: { 217 nsAutoString arg2; 218 rv = mParams[1]->evaluateToString(aContext, arg2); 219 NS_ENSURE_SUCCESS(rv, rv); 220 221 if (arg2.IsEmpty()) { 222 aContext->recycler()->getBoolResult(true, aResult); 223 } else { 224 nsAutoString arg1; 225 rv = mParams[0]->evaluateToString(aContext, arg1); 226 NS_ENSURE_SUCCESS(rv, rv); 227 228 aContext->recycler()->getBoolResult(FindInReadable(arg2, arg1), 229 aResult); 230 } 231 232 return NS_OK; 233 } 234 case NORMALIZE_SPACE: { 235 nsAutoString resultStr; 236 if (!mParams.IsEmpty()) { 237 rv = mParams[0]->evaluateToString(aContext, resultStr); 238 NS_ENSURE_SUCCESS(rv, rv); 239 } else { 240 txXPathNodeUtils::appendNodeValue(aContext->getContextNode(), 241 resultStr); 242 } 243 244 RefPtr<StringResult> strRes; 245 rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes)); 246 NS_ENSURE_SUCCESS(rv, rv); 247 248 bool addSpace = false; 249 bool first = true; 250 strRes->mValue.SetCapacity(resultStr.Length()); 251 char16_t c; 252 uint32_t src; 253 for (src = 0; src < resultStr.Length(); src++) { 254 c = resultStr.CharAt(src); 255 if (XMLUtils::isWhitespace(c)) { 256 addSpace = true; 257 } else { 258 if (addSpace && !first) strRes->mValue.Append(char16_t(' ')); 259 260 strRes->mValue.Append(c); 261 addSpace = false; 262 first = false; 263 } 264 } 265 *aResult = strRes; 266 NS_ADDREF(*aResult); 267 268 return NS_OK; 269 } 270 case STARTS_WITH: { 271 nsAutoString arg2; 272 rv = mParams[1]->evaluateToString(aContext, arg2); 273 NS_ENSURE_SUCCESS(rv, rv); 274 275 bool result = false; 276 if (arg2.IsEmpty()) { 277 result = true; 278 } else { 279 nsAutoString arg1; 280 rv = mParams[0]->evaluateToString(aContext, arg1); 281 NS_ENSURE_SUCCESS(rv, rv); 282 283 result = StringBeginsWith(arg1, arg2); 284 } 285 286 aContext->recycler()->getBoolResult(result, aResult); 287 288 return NS_OK; 289 } 290 case STRING: { 291 RefPtr<StringResult> strRes; 292 rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes)); 293 NS_ENSURE_SUCCESS(rv, rv); 294 295 if (!mParams.IsEmpty()) { 296 rv = mParams[0]->evaluateToString(aContext, strRes->mValue); 297 NS_ENSURE_SUCCESS(rv, rv); 298 } else { 299 txXPathNodeUtils::appendNodeValue(aContext->getContextNode(), 300 strRes->mValue); 301 } 302 303 NS_ADDREF(*aResult = strRes); 304 305 return NS_OK; 306 } 307 case STRING_LENGTH: { 308 nsAutoString resultStr; 309 if (!mParams.IsEmpty()) { 310 rv = mParams[0]->evaluateToString(aContext, resultStr); 311 NS_ENSURE_SUCCESS(rv, rv); 312 } else { 313 txXPathNodeUtils::appendNodeValue(aContext->getContextNode(), 314 resultStr); 315 } 316 rv = aContext->recycler()->getNumberResult(resultStr.Length(), aResult); 317 NS_ENSURE_SUCCESS(rv, rv); 318 319 return NS_OK; 320 } 321 case SUBSTRING: { 322 nsAutoString src; 323 rv = mParams[0]->evaluateToString(aContext, src); 324 NS_ENSURE_SUCCESS(rv, rv); 325 326 double start; 327 rv = evaluateToNumber(mParams[1], aContext, &start); 328 NS_ENSURE_SUCCESS(rv, rv); 329 330 // check for NaN or +/-Inf 331 if (std::isnan(start) || std::isinf(start) || 332 start >= src.Length() + 0.5) { 333 aContext->recycler()->getEmptyStringResult(aResult); 334 335 return NS_OK; 336 } 337 338 start = floor(start + 0.5) - 1; 339 340 double end; 341 if (mParams.Length() == 3) { 342 rv = evaluateToNumber(mParams[2], aContext, &end); 343 NS_ENSURE_SUCCESS(rv, rv); 344 345 end += start; 346 if (std::isnan(end) || end < 0) { 347 aContext->recycler()->getEmptyStringResult(aResult); 348 349 return NS_OK; 350 } 351 352 if (end > src.Length()) 353 end = src.Length(); 354 else 355 end = floor(end + 0.5); 356 } else { 357 end = src.Length(); 358 } 359 360 if (start < 0) start = 0; 361 362 if (start > end) { 363 aContext->recycler()->getEmptyStringResult(aResult); 364 365 return NS_OK; 366 } 367 368 return aContext->recycler()->getStringResult( 369 Substring(src, (uint32_t)start, (uint32_t)(end - start)), aResult); 370 } 371 case SUBSTRING_AFTER: { 372 nsAutoString arg1; 373 rv = mParams[0]->evaluateToString(aContext, arg1); 374 NS_ENSURE_SUCCESS(rv, rv); 375 376 nsAutoString arg2; 377 rv = mParams[1]->evaluateToString(aContext, arg2); 378 NS_ENSURE_SUCCESS(rv, rv); 379 380 if (arg2.IsEmpty()) { 381 return aContext->recycler()->getStringResult(arg1, aResult); 382 } 383 384 int32_t idx = arg1.Find(arg2); 385 if (idx == kNotFound) { 386 aContext->recycler()->getEmptyStringResult(aResult); 387 388 return NS_OK; 389 } 390 391 const nsAString& result = Substring(arg1, idx + arg2.Length()); 392 return aContext->recycler()->getStringResult(result, aResult); 393 } 394 case SUBSTRING_BEFORE: { 395 nsAutoString arg2; 396 rv = mParams[1]->evaluateToString(aContext, arg2); 397 NS_ENSURE_SUCCESS(rv, rv); 398 399 if (arg2.IsEmpty()) { 400 aContext->recycler()->getEmptyStringResult(aResult); 401 402 return NS_OK; 403 } 404 405 nsAutoString arg1; 406 rv = mParams[0]->evaluateToString(aContext, arg1); 407 NS_ENSURE_SUCCESS(rv, rv); 408 409 int32_t idx = arg1.Find(arg2); 410 if (idx == kNotFound) { 411 aContext->recycler()->getEmptyStringResult(aResult); 412 413 return NS_OK; 414 } 415 416 return aContext->recycler()->getStringResult(StringHead(arg1, idx), 417 aResult); 418 } 419 case TRANSLATE: { 420 nsAutoString src; 421 rv = mParams[0]->evaluateToString(aContext, src); 422 NS_ENSURE_SUCCESS(rv, rv); 423 424 if (src.IsEmpty()) { 425 aContext->recycler()->getEmptyStringResult(aResult); 426 427 return NS_OK; 428 } 429 430 RefPtr<StringResult> strRes; 431 rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes)); 432 NS_ENSURE_SUCCESS(rv, rv); 433 434 strRes->mValue.SetCapacity(src.Length()); 435 436 nsAutoString oldChars, newChars; 437 rv = mParams[1]->evaluateToString(aContext, oldChars); 438 NS_ENSURE_SUCCESS(rv, rv); 439 440 rv = mParams[2]->evaluateToString(aContext, newChars); 441 NS_ENSURE_SUCCESS(rv, rv); 442 443 uint32_t i; 444 int32_t newCharsLength = (int32_t)newChars.Length(); 445 for (i = 0; i < src.Length(); i++) { 446 int32_t idx = oldChars.FindChar(src.CharAt(i)); 447 if (idx != kNotFound) { 448 if (idx < newCharsLength) 449 strRes->mValue.Append(newChars.CharAt((uint32_t)idx)); 450 } else { 451 strRes->mValue.Append(src.CharAt(i)); 452 } 453 } 454 455 NS_ADDREF(*aResult = strRes); 456 457 return NS_OK; 458 } 459 460 // Number functions 461 462 case NUMBER: { 463 double res; 464 if (!mParams.IsEmpty()) { 465 rv = evaluateToNumber(mParams[0], aContext, &res); 466 NS_ENSURE_SUCCESS(rv, rv); 467 } else { 468 nsAutoString resultStr; 469 txXPathNodeUtils::appendNodeValue(aContext->getContextNode(), 470 resultStr); 471 res = txDouble::toDouble(resultStr); 472 } 473 return aContext->recycler()->getNumberResult(res, aResult); 474 } 475 case ROUND: { 476 double dbl; 477 rv = evaluateToNumber(mParams[0], aContext, &dbl); 478 NS_ENSURE_SUCCESS(rv, rv); 479 480 if (std::isfinite(dbl)) { 481 if (mozilla::IsNegative(dbl) && dbl >= -0.5) { 482 dbl *= 0; 483 } else { 484 dbl = floor(dbl + 0.5); 485 } 486 } 487 488 return aContext->recycler()->getNumberResult(dbl, aResult); 489 } 490 case FLOOR: { 491 double dbl; 492 rv = evaluateToNumber(mParams[0], aContext, &dbl); 493 NS_ENSURE_SUCCESS(rv, rv); 494 495 if (std::isfinite(dbl) && !mozilla::IsNegativeZero(dbl)) dbl = floor(dbl); 496 497 return aContext->recycler()->getNumberResult(dbl, aResult); 498 } 499 case CEILING: { 500 double dbl; 501 rv = evaluateToNumber(mParams[0], aContext, &dbl); 502 NS_ENSURE_SUCCESS(rv, rv); 503 504 if (std::isfinite(dbl)) { 505 if (mozilla::IsNegative(dbl) && dbl > -1) 506 dbl *= 0; 507 else 508 dbl = ceil(dbl); 509 } 510 511 return aContext->recycler()->getNumberResult(dbl, aResult); 512 } 513 case SUM: { 514 RefPtr<txNodeSet> nodes; 515 nsresult rv = 516 evaluateToNodeSet(mParams[0], aContext, getter_AddRefs(nodes)); 517 NS_ENSURE_SUCCESS(rv, rv); 518 519 double res = 0; 520 int32_t i; 521 for (i = 0; i < nodes->size(); ++i) { 522 nsAutoString resultStr; 523 txXPathNodeUtils::appendNodeValue(nodes->get(i), resultStr); 524 res += txDouble::toDouble(resultStr); 525 } 526 return aContext->recycler()->getNumberResult(res, aResult); 527 } 528 529 // Boolean functions 530 531 case BOOLEAN: { 532 bool result; 533 nsresult rv = mParams[0]->evaluateToBool(aContext, result); 534 NS_ENSURE_SUCCESS(rv, rv); 535 536 aContext->recycler()->getBoolResult(result, aResult); 537 538 return NS_OK; 539 } 540 case _FALSE: { 541 aContext->recycler()->getBoolResult(false, aResult); 542 543 return NS_OK; 544 } 545 case LANG: { 546 txXPathTreeWalker walker(aContext->getContextNode()); 547 548 nsAutoString lang; 549 bool found; 550 do { 551 found = walker.getAttr(nsGkAtoms::lang, kNameSpaceID_XML, lang); 552 } while (!found && walker.moveToParent()); 553 554 if (!found) { 555 aContext->recycler()->getBoolResult(false, aResult); 556 557 return NS_OK; 558 } 559 560 nsAutoString arg; 561 rv = mParams[0]->evaluateToString(aContext, arg); 562 NS_ENSURE_SUCCESS(rv, rv); 563 564 bool result = 565 StringBeginsWith(lang, arg, nsCaseInsensitiveStringComparator) && 566 (lang.Length() == arg.Length() || lang.CharAt(arg.Length()) == '-'); 567 568 aContext->recycler()->getBoolResult(result, aResult); 569 570 return NS_OK; 571 } 572 case _NOT: { 573 bool result; 574 rv = mParams[0]->evaluateToBool(aContext, result); 575 NS_ENSURE_SUCCESS(rv, rv); 576 577 aContext->recycler()->getBoolResult(!result, aResult); 578 579 return NS_OK; 580 } 581 case _TRUE: { 582 aContext->recycler()->getBoolResult(true, aResult); 583 584 return NS_OK; 585 } 586 } 587 588 aContext->receiveError(u"Internal error"_ns, NS_ERROR_UNEXPECTED); 589 return NS_ERROR_UNEXPECTED; 590 } 591 592 Expr::ResultType txCoreFunctionCall::getReturnType() { 593 return descriptTable[mType].mReturnType; 594 } 595 596 bool txCoreFunctionCall::isSensitiveTo(ContextSensitivity aContext) { 597 switch (mType) { 598 case COUNT: 599 case CONCAT: 600 case CONTAINS: 601 case STARTS_WITH: 602 case SUBSTRING: 603 case SUBSTRING_AFTER: 604 case SUBSTRING_BEFORE: 605 case TRANSLATE: 606 case ROUND: 607 case FLOOR: 608 case CEILING: 609 case SUM: 610 case BOOLEAN: 611 case _NOT: 612 case _FALSE: 613 case _TRUE: { 614 return argsSensitiveTo(aContext); 615 } 616 case ID: { 617 return (aContext & NODE_CONTEXT) || argsSensitiveTo(aContext); 618 } 619 case LAST: { 620 return !!(aContext & SIZE_CONTEXT); 621 } 622 case LOCAL_NAME: 623 case NAME: 624 case NAMESPACE_URI: 625 case NORMALIZE_SPACE: 626 case STRING: 627 case STRING_LENGTH: 628 case NUMBER: { 629 if (mParams.IsEmpty()) { 630 return !!(aContext & NODE_CONTEXT); 631 } 632 return argsSensitiveTo(aContext); 633 } 634 case POSITION: { 635 return !!(aContext & POSITION_CONTEXT); 636 } 637 case LANG: { 638 return (aContext & NODE_CONTEXT) || argsSensitiveTo(aContext); 639 } 640 } 641 642 MOZ_ASSERT_UNREACHABLE("how'd we get here?"); 643 return true; 644 } 645 646 // static 647 bool txCoreFunctionCall::getTypeFromAtom(nsAtom* aName, eType& aType) { 648 uint32_t i; 649 for (i = 0; i < std::size(descriptTable); ++i) { 650 if (aName == descriptTable[i].mName) { 651 aType = static_cast<eType>(i); 652 653 return true; 654 } 655 } 656 657 return false; 658 } 659 660 #ifdef TX_TO_STRING 661 void txCoreFunctionCall::appendName(nsAString& aDest) { 662 aDest.Append(descriptTable[mType].mName->GetUTF16String()); 663 } 664 #endif