int Parser::evaluateRelational(Expr* lhs, Expr* rhs) { if (lhs->tag() == TAG_literalString && rhs->tag() == TAG_literalString) return (((LiteralString*)lhs)->value)->compareTo(((LiteralString*)rhs)->value) < 0 ? 1 : 0; double l = evaluateToNumber(lhs); double r = evaluateToNumber(rhs); if (MathUtils::isNaN(l) || MathUtils::isNaN(r)) return -1; return l < r ? 1 : 0; }
uint32_t Parser::evaluateToUInt32(Expr* e) { if (e->tag() == TAG_literalUInt) return ((LiteralUInt*)e)->value; double d = evaluateToNumber(e); if (d == 0 || MathUtils::isNaN(d) || MathUtils::isInfinite(d)) return 0; d = (d < 0 ? -1 : 1) * floor(fabs(d)); d = fmod(d, 4294967296.0); return uint32_t(d); }
/* * Evaluates this Expr based on the given context node and processor state * @param context the context node for evaluation of this Expr * @param ps the ContextState containing the stack information needed * for evaluation * @return the result of the evaluation */ nsresult txCoreFunctionCall::evaluate(txIEvalContext* aContext, txAExprResult** aResult) { *aResult = nsnull; if (!requireParams(descriptTable[mType].mMinParams, descriptTable[mType].mMaxParams, aContext)) { return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; } nsresult rv = NS_OK; switch (mType) { case COUNT: { nsRefPtr<txNodeSet> nodes; rv = evaluateToNodeSet(mParams[0], aContext, getter_AddRefs(nodes)); NS_ENSURE_SUCCESS(rv, rv); return aContext->recycler()->getNumberResult(nodes->size(), aResult); } case ID: { nsRefPtr<txAExprResult> exprResult; rv = mParams[0]->evaluate(aContext, getter_AddRefs(exprResult)); NS_ENSURE_SUCCESS(rv, rv); nsRefPtr<txNodeSet> resultSet; rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet)); NS_ENSURE_SUCCESS(rv, rv); txXPathTreeWalker walker(aContext->getContextNode()); if (exprResult->getResultType() == txAExprResult::NODESET) { txNodeSet* nodes = static_cast<txNodeSet*> (static_cast<txAExprResult*> (exprResult)); PRInt32 i; for (i = 0; i < nodes->size(); ++i) { nsAutoString idList; txXPathNodeUtils::appendNodeValue(nodes->get(i), idList); nsWhitespaceTokenizer tokenizer(idList); while (tokenizer.hasMoreTokens()) { if (walker.moveToElementById(tokenizer.nextToken())) { resultSet->add(walker.getCurrentPosition()); } } } } else { nsAutoString idList; exprResult->stringValue(idList); nsWhitespaceTokenizer tokenizer(idList); while (tokenizer.hasMoreTokens()) { if (walker.moveToElementById(tokenizer.nextToken())) { resultSet->add(walker.getCurrentPosition()); } } } *aResult = resultSet; NS_ADDREF(*aResult); return NS_OK; } case LAST: { return aContext->recycler()->getNumberResult(aContext->size(), aResult); } case LOCAL_NAME: case NAME: case NAMESPACE_URI: { // Check for optional arg nsRefPtr<txNodeSet> nodes; if (!mParams.IsEmpty()) { rv = evaluateToNodeSet(mParams[0], aContext, getter_AddRefs(nodes)); NS_ENSURE_SUCCESS(rv, rv); if (nodes->isEmpty()) { aContext->recycler()->getEmptyStringResult(aResult); return NS_OK; } } const txXPathNode& node = nodes ? nodes->get(0) : aContext->getContextNode(); switch (mType) { case LOCAL_NAME: { StringResult* strRes = nsnull; rv = aContext->recycler()->getStringResult(&strRes); NS_ENSURE_SUCCESS(rv, rv); *aResult = strRes; txXPathNodeUtils::getLocalName(node, strRes->mValue); return NS_OK; } case NAMESPACE_URI: { StringResult* strRes = nsnull; rv = aContext->recycler()->getStringResult(&strRes); NS_ENSURE_SUCCESS(rv, rv); *aResult = strRes; txXPathNodeUtils::getNamespaceURI(node, strRes->mValue); return NS_OK; } case NAME: { // XXX Namespace: namespaces have a name if (txXPathNodeUtils::isAttribute(node) || txXPathNodeUtils::isElement(node) || txXPathNodeUtils::isProcessingInstruction(node)) { StringResult* strRes = nsnull; rv = aContext->recycler()->getStringResult(&strRes); NS_ENSURE_SUCCESS(rv, rv); *aResult = strRes; txXPathNodeUtils::getNodeName(node, strRes->mValue); } else { aContext->recycler()->getEmptyStringResult(aResult); } return NS_OK; } default: { break; } } } case POSITION: { return aContext->recycler()->getNumberResult(aContext->position(), aResult); } // String functions case CONCAT: { nsRefPtr<StringResult> strRes; rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes)); NS_ENSURE_SUCCESS(rv, rv); PRUint32 i, len = mParams.Length(); for (i = 0; i < len; ++i) { rv = mParams[i]->evaluateToString(aContext, strRes->mValue); NS_ENSURE_SUCCESS(rv, rv); } NS_ADDREF(*aResult = strRes); return NS_OK; } case CONTAINS: { nsAutoString arg2; rv = mParams[1]->evaluateToString(aContext, arg2); NS_ENSURE_SUCCESS(rv, rv); if (arg2.IsEmpty()) { aContext->recycler()->getBoolResult(PR_TRUE, aResult); } else { nsAutoString arg1; rv = mParams[0]->evaluateToString(aContext, arg1); NS_ENSURE_SUCCESS(rv, rv); aContext->recycler()->getBoolResult(FindInReadable(arg2, arg1), aResult); } return NS_OK; } case NORMALIZE_SPACE: { nsAutoString resultStr; if (!mParams.IsEmpty()) { rv = mParams[0]->evaluateToString(aContext, resultStr); NS_ENSURE_SUCCESS(rv, rv); } else { txXPathNodeUtils::appendNodeValue(aContext->getContextNode(), resultStr); } nsRefPtr<StringResult> strRes; rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes)); NS_ENSURE_SUCCESS(rv, rv); MBool addSpace = MB_FALSE; MBool first = MB_TRUE; strRes->mValue.SetCapacity(resultStr.Length()); PRUnichar c; PRUint32 src; for (src = 0; src < resultStr.Length(); src++) { c = resultStr.CharAt(src); if (XMLUtils::isWhitespace(c)) { addSpace = MB_TRUE; } else { if (addSpace && !first) strRes->mValue.Append(PRUnichar(' ')); strRes->mValue.Append(c); addSpace = MB_FALSE; first = MB_FALSE; } } *aResult = strRes; NS_ADDREF(*aResult); return NS_OK; } case STARTS_WITH: { nsAutoString arg2; rv = mParams[1]->evaluateToString(aContext, arg2); NS_ENSURE_SUCCESS(rv, rv); PRBool result = PR_FALSE; if (arg2.IsEmpty()) { result = PR_TRUE; } else { nsAutoString arg1; rv = mParams[0]->evaluateToString(aContext, arg1); NS_ENSURE_SUCCESS(rv, rv); result = StringBeginsWith(arg1, arg2); } aContext->recycler()->getBoolResult(result, aResult); return NS_OK; } case STRING: { nsRefPtr<StringResult> strRes; rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes)); NS_ENSURE_SUCCESS(rv, rv); if (!mParams.IsEmpty()) { rv = mParams[0]->evaluateToString(aContext, strRes->mValue); NS_ENSURE_SUCCESS(rv, rv); } else { txXPathNodeUtils::appendNodeValue(aContext->getContextNode(), strRes->mValue); } NS_ADDREF(*aResult = strRes); return NS_OK; } case STRING_LENGTH: { nsAutoString resultStr; if (!mParams.IsEmpty()) { rv = mParams[0]->evaluateToString(aContext, resultStr); NS_ENSURE_SUCCESS(rv, rv); } else { txXPathNodeUtils::appendNodeValue(aContext->getContextNode(), resultStr); } rv = aContext->recycler()->getNumberResult(resultStr.Length(), aResult); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } case SUBSTRING: { nsAutoString src; rv = mParams[0]->evaluateToString(aContext, src); NS_ENSURE_SUCCESS(rv, rv); double start; rv = evaluateToNumber(mParams[1], aContext, &start); NS_ENSURE_SUCCESS(rv, rv); // check for NaN or +/-Inf if (Double::isNaN(start) || Double::isInfinite(start) || start >= src.Length() + 0.5) { aContext->recycler()->getEmptyStringResult(aResult); return NS_OK; } start = floor(start + 0.5) - 1; double end; if (mParams.Length() == 3) { rv = evaluateToNumber(mParams[2], aContext, &end); NS_ENSURE_SUCCESS(rv, rv); end += start; if (Double::isNaN(end) || end < 0) { aContext->recycler()->getEmptyStringResult(aResult); return NS_OK; } if (end > src.Length()) end = src.Length(); else end = floor(end + 0.5); } else { end = src.Length(); } if (start < 0) start = 0; if (start > end) { aContext->recycler()->getEmptyStringResult(aResult); return NS_OK; } return aContext->recycler()->getStringResult( Substring(src, (PRUint32)start, (PRUint32)(end - start)), aResult); } case SUBSTRING_AFTER: { nsAutoString arg1; rv = mParams[0]->evaluateToString(aContext, arg1); NS_ENSURE_SUCCESS(rv, rv); nsAutoString arg2; rv = mParams[1]->evaluateToString(aContext, arg2); NS_ENSURE_SUCCESS(rv, rv); if (arg2.IsEmpty()) { return aContext->recycler()->getStringResult(arg1, aResult); } PRInt32 idx = arg1.Find(arg2); if (idx == kNotFound) { aContext->recycler()->getEmptyStringResult(aResult); return NS_OK; } const nsSubstring& result = Substring(arg1, idx + arg2.Length()); return aContext->recycler()->getStringResult(result, aResult); } case SUBSTRING_BEFORE: { nsAutoString arg2; rv = mParams[1]->evaluateToString(aContext, arg2); NS_ENSURE_SUCCESS(rv, rv); if (arg2.IsEmpty()) { aContext->recycler()->getEmptyStringResult(aResult); return NS_OK; } nsAutoString arg1; rv = mParams[0]->evaluateToString(aContext, arg1); NS_ENSURE_SUCCESS(rv, rv); PRInt32 idx = arg1.Find(arg2); if (idx == kNotFound) { aContext->recycler()->getEmptyStringResult(aResult); return NS_OK; } return aContext->recycler()->getStringResult(StringHead(arg1, idx), aResult); } case TRANSLATE: { nsAutoString src; rv = mParams[0]->evaluateToString(aContext, src); NS_ENSURE_SUCCESS(rv, rv); if (src.IsEmpty()) { aContext->recycler()->getEmptyStringResult(aResult); return NS_OK; } nsRefPtr<StringResult> strRes; rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes)); NS_ENSURE_SUCCESS(rv, rv); strRes->mValue.SetCapacity(src.Length()); nsAutoString oldChars, newChars; rv = mParams[1]->evaluateToString(aContext, oldChars); NS_ENSURE_SUCCESS(rv, rv); rv = mParams[2]->evaluateToString(aContext, newChars); NS_ENSURE_SUCCESS(rv, rv); PRUint32 i; PRInt32 newCharsLength = (PRInt32)newChars.Length(); for (i = 0; i < src.Length(); i++) { PRInt32 idx = oldChars.FindChar(src.CharAt(i)); if (idx != kNotFound) { if (idx < newCharsLength) strRes->mValue.Append(newChars.CharAt((PRUint32)idx)); } else { strRes->mValue.Append(src.CharAt(i)); } } NS_ADDREF(*aResult = strRes); return NS_OK; } // Number functions case NUMBER: { double res; if (!mParams.IsEmpty()) { rv = evaluateToNumber(mParams[0], aContext, &res); NS_ENSURE_SUCCESS(rv, rv); } else { nsAutoString resultStr; txXPathNodeUtils::appendNodeValue(aContext->getContextNode(), resultStr); res = Double::toDouble(resultStr); } return aContext->recycler()->getNumberResult(res, aResult); } case ROUND: { double dbl; rv = evaluateToNumber(mParams[0], aContext, &dbl); NS_ENSURE_SUCCESS(rv, rv); if (!Double::isNaN(dbl) && !Double::isInfinite(dbl)) { if (Double::isNeg(dbl) && dbl >= -0.5) { dbl *= 0; } else { dbl = floor(dbl + 0.5); } } return aContext->recycler()->getNumberResult(dbl, aResult); } case FLOOR: { double dbl; rv = evaluateToNumber(mParams[0], aContext, &dbl); NS_ENSURE_SUCCESS(rv, rv); if (!Double::isNaN(dbl) && !Double::isInfinite(dbl) && !(dbl == 0 && Double::isNeg(dbl))) { dbl = floor(dbl); } return aContext->recycler()->getNumberResult(dbl, aResult); } case CEILING: { double dbl; rv = evaluateToNumber(mParams[0], aContext, &dbl); NS_ENSURE_SUCCESS(rv, rv); if (!Double::isNaN(dbl) && !Double::isInfinite(dbl)) { if (Double::isNeg(dbl) && dbl > -1) { dbl *= 0; } else { dbl = ceil(dbl); } } return aContext->recycler()->getNumberResult(dbl, aResult); } case SUM: { nsRefPtr<txNodeSet> nodes; nsresult rv = evaluateToNodeSet(mParams[0], aContext, getter_AddRefs(nodes)); NS_ENSURE_SUCCESS(rv, rv); double res = 0; PRInt32 i; for (i = 0; i < nodes->size(); ++i) { nsAutoString resultStr; txXPathNodeUtils::appendNodeValue(nodes->get(i), resultStr); res += Double::toDouble(resultStr); } return aContext->recycler()->getNumberResult(res, aResult); } // Boolean functions case BOOLEAN: { PRBool result; nsresult rv = mParams[0]->evaluateToBool(aContext, result); NS_ENSURE_SUCCESS(rv, rv); aContext->recycler()->getBoolResult(result, aResult); return NS_OK; } case _FALSE: { aContext->recycler()->getBoolResult(PR_FALSE, aResult); return NS_OK; } case LANG: { txXPathTreeWalker walker(aContext->getContextNode()); nsAutoString lang; PRBool found; do { found = walker.getAttr(txXMLAtoms::lang, kNameSpaceID_XML, lang); } while (!found && walker.moveToParent()); if (!found) { aContext->recycler()->getBoolResult(PR_FALSE, aResult); return NS_OK; } nsAutoString arg; rv = mParams[0]->evaluateToString(aContext, arg); NS_ENSURE_SUCCESS(rv, rv); PRBool result = StringBeginsWith(lang, arg, txCaseInsensitiveStringComparator()) && (lang.Length() == arg.Length() || lang.CharAt(arg.Length()) == '-'); aContext->recycler()->getBoolResult(result, aResult); return NS_OK; } case _NOT: { PRBool result; rv = mParams[0]->evaluateToBool(aContext, result); NS_ENSURE_SUCCESS(rv, rv); aContext->recycler()->getBoolResult(!result, aResult); return NS_OK; } case _TRUE: { aContext->recycler()->getBoolResult(PR_TRUE, aResult); return NS_OK; } } aContext->receiveError(NS_LITERAL_STRING("Internal error"), NS_ERROR_UNEXPECTED); return NS_ERROR_UNEXPECTED; }
Expr* Parser::evaluateConfigDefinition(Str* ns, Expr* e) { // e is evaluated in an environment containing only config bindings. // ns is the default namespace: it will be used to qualify any unqualified name. switch (e->tag()) { case TAG_literalUndefined: case TAG_literalString: case TAG_literalNull: case TAG_literalUInt: case TAG_literalInt: case TAG_literalDouble: case TAG_literalBoolean: return e; case TAG_simpleName: { Expr* expr = findConfigBinding(ns, ((SimpleName*)e)->name); if (expr == NULL) compiler->syntaxError(expr->pos, SYNTAXERR_UNBOUND_CONST_NAME); return expr; } case TAG_qualifiedName: { QualifiedName* qname = (QualifiedName*)e; if (qname->qualifier->tag() != TAG_simpleName || qname->name->tag() != TAG_simpleName) compiler->syntaxError(e->pos, SYNTAXERR_UNBOUND_CONST_NAME); // Specifically an illegal const name Str* ns = ((SimpleName*)qname->qualifier)->name; Str* name = ((SimpleName*)qname->name)->name; Expr* value = findConfigBinding(ns, name); if (value == NULL) compiler->syntaxError(e->pos, SYNTAXERR_UNBOUND_CONST_NAME); return value; } case TAG_binaryExpr: { // CLARIFICATION: no short-circuiting // // We evaluate both sides of && and || in order to uncover // any undefined variables lurking in non-taken branches. BinaryExpr* binary = (BinaryExpr*)e; Expr* lhs = evaluateConfigDefinition(ns, binary->lhs); Expr* rhs = evaluateConfigDefinition(ns, binary->rhs); switch (binary->op) { case OPR_plus: if (lhs->tag() == TAG_literalString || rhs->tag() == TAG_literalString) { StringBuilder b(compiler); b.append(((LiteralString*)lhs)->value); b.append(((LiteralString*)rhs)->value); return boxString(b.str()); } return boxDouble(evaluateToNumber(lhs) + evaluateToNumber(rhs)); case OPR_minus: return boxDouble(evaluateToNumber(lhs) - evaluateToNumber(rhs)); case OPR_multiply: return boxDouble(evaluateToNumber(lhs) * evaluateToNumber(rhs)); case OPR_divide: return boxDouble(evaluateToNumber(lhs) / evaluateToNumber(rhs)); case OPR_remainder: return boxDouble(fmod(evaluateToNumber(lhs), evaluateToNumber(rhs))); case OPR_leftShift: return boxInt(evaluateToInt32(lhs) << (evaluateToUInt32(rhs) & 0x1F)); case OPR_rightShift: return boxInt(evaluateToInt32(lhs) >> (evaluateToUInt32(rhs) & 0x1F)); case OPR_rightShiftUnsigned: return boxUInt(evaluateToUInt32(lhs) >> (evaluateToUInt32(rhs) & 0x1F)); case OPR_bitwiseAnd: return boxInt(evaluateToInt32(lhs) & evaluateToInt32(rhs)); case OPR_bitwiseOr: return boxInt(evaluateToInt32(lhs) | evaluateToInt32(rhs)); case OPR_bitwiseXor: return boxInt(evaluateToInt32(lhs) ^ evaluateToInt32(rhs)); case OPR_logicalAnd: return boxBoolean(int(evaluateToBoolean(lhs)) + int(evaluateToBoolean(rhs)) == 2); case OPR_logicalOr: return boxBoolean(int(evaluateToBoolean(lhs)) + int(evaluateToBoolean(rhs)) != 0); case OPR_less: { int r = evaluateRelational(lhs, rhs); return boxBoolean(r == -1 || r == 0 ? false : true); } case OPR_greater: { int r = evaluateRelational(rhs, lhs); return boxBoolean(r == -1 || r == 0 ? false : true); } case OPR_lessOrEqual: { int r = evaluateRelational(rhs, lhs); return boxBoolean(r == -1 || r == 1 ? false : true); } case OPR_greaterOrEqual: { int r = evaluateRelational(lhs, rhs); return boxBoolean(r == -1 || r == 1 ? false : true); } case OPR_equal: case OPR_notEqual: case OPR_strictEqual: case OPR_strictNotEqual: { if (lhs->tag() == TAG_literalInt || lhs->tag() == TAG_literalUInt) lhs = boxDouble(evaluateToNumber(lhs)); if (rhs->tag() == TAG_literalInt || rhs->tag() == TAG_literalUInt) rhs = boxDouble(evaluateToNumber(rhs)); bool equality; if (binary->op == OPR_equal || binary->op == OPR_notEqual) equality = binary->op == OPR_equal; else equality = binary->op == OPR_strictEqual; if (lhs->tag() != rhs->tag()) { if (binary->op == OPR_equal || binary->op == OPR_notEqual) { if ((lhs->tag() == TAG_literalUndefined && rhs->tag() == TAG_literalNull) || (lhs->tag() == TAG_literalNull && rhs->tag() == TAG_literalUndefined)) return boxBoolean(true == equality); if ((lhs->tag() == TAG_literalString && rhs->tag() == TAG_literalDouble) || (lhs->tag() == TAG_literalDouble && rhs->tag() == TAG_literalString)) return boxBoolean((evaluateToNumber(lhs) == evaluateToNumber(rhs)) == equality); if (lhs->tag() == TAG_literalBoolean || rhs->tag() == TAG_literalBoolean) return boxBoolean((evaluateToBoolean(lhs) == evaluateToBoolean(rhs)) == equality); } return boxBoolean(false == equality); } if (lhs->tag() == TAG_literalUndefined || lhs->tag() == TAG_literalNull) return boxBoolean(true == equality); if (lhs->tag() == TAG_literalDouble) return boxBoolean((evaluateToNumber(lhs) == evaluateToNumber(rhs)) == equality); if (lhs->tag() == TAG_literalBoolean) return boxBoolean((evaluateToBoolean(lhs) == evaluateToBoolean(rhs)) == equality); if (lhs->tag() == TAG_literalString) return boxBoolean((evaluateToString(lhs) == evaluateToString(rhs)) == equality); failNonConstant(lhs); /*NOTREACHED*/ break; } default: // "as", "is", "in", ",", "=" compiler->syntaxError(position(), SYNTAXERR_ILLEGAL_OP_IN_CONSTEXPR); /*NOTREACHED*/ break; } /*NOTREACHED*/ break; } case TAG_unaryExpr: { // EXTENSION: typeof // // Supporting "typeof" makes some sort of sense (for example, the // operand of typeof can be a config constant that can take on // various values, and computing the name of the type into the // program can be useful). // // EXTENSION: void // // Supporting "void" probably does not make a lot of sense, but it // seems benign. UnaryExpr* unary = (UnaryExpr*)e; Expr* opd = evaluateConfigDefinition(ns, unary->expr); switch (unary->op) { case OPR_typeof: switch (opd->tag()) { case TAG_literalUndefined: return boxString("undefined"); case TAG_literalString: return boxString("string"); case TAG_literalNull: return boxString("object"); case TAG_literalUInt: case TAG_literalInt: case TAG_literalDouble: return boxString("number"); case TAG_literalBoolean: return boxString("boolean"); default: failNonConstant(opd); return NULL; } case OPR_bitwiseNot: return boxUInt(~evaluateToUInt32(opd)); case OPR_unminus: return boxDouble(-evaluateToNumber(opd)); case OPR_unplus: return boxDouble(evaluateToNumber(opd)); case OPR_not: return boxBoolean(!evaluateToBoolean(opd)); case OPR_void: return boxUndefined(); default: // "delete", "++", "--" compiler->syntaxError(position(), SYNTAXERR_ILLEGAL_OP_IN_CONSTEXPR); /*NOTREACHED*/ break; } /*NOTREACHED*/ break; } case TAG_conditionalExpr: { // EXTENSION: conditional operator // // It seems totally sensible to support "... ? ... : ...", though // it's not mentioned in the conditional compilation spec. // // We evaluate both arms in order to uncover references to undefined // configuration variables, same as for && and ||. ConditionalExpr* cond = (ConditionalExpr*)e; Expr* e1 = evaluateConfigDefinition(ns, cond->e1); Expr* e2 = evaluateConfigDefinition(ns, cond->e2); Expr* e3 = evaluateConfigDefinition(ns, cond->e3); return evaluateToBoolean(e1) ? e2 : e3; } default: // Property references, 'new', 'call' - lots of things compiler->syntaxError(position(), SYNTAXERR_ILLEGAL_OP_IN_CONSTEXPR); /*NOTREACHED*/ break; } /*NOTREACHED*/ return NULL; }
/* * Evaluates this Expr based on the given context node and processor state * @param context the context node for evaluation of this Expr * @param cs the ContextState containing the stack information needed * for evaluation * @return the result of the evaluation */ nsresult txFormatNumberFunctionCall::evaluate(txIEvalContext* aContext, txAExprResult** aResult) { *aResult = nullptr; if (!requireParams(2, 3, aContext)) return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; // Get number and format double value; txExpandedName formatName; nsresult rv = evaluateToNumber(mParams[0], aContext, &value); NS_ENSURE_SUCCESS(rv, rv); nsAutoString formatStr; rv = mParams[1]->evaluateToString(aContext, formatStr); NS_ENSURE_SUCCESS(rv, rv); if (mParams.Length() == 3) { nsAutoString formatQName; rv = mParams[2]->evaluateToString(aContext, formatQName); NS_ENSURE_SUCCESS(rv, rv); rv = formatName.init(formatQName, mMappings, false); NS_ENSURE_SUCCESS(rv, rv); } txDecimalFormat* format = mStylesheet->getDecimalFormat(formatName); if (!format) { nsAutoString err(NS_LITERAL_STRING("unknown decimal format")); #ifdef TX_TO_STRING err.AppendLiteral(" for: "); toString(err); #endif aContext->receiveError(err, NS_ERROR_XPATH_INVALID_ARG); return NS_ERROR_XPATH_INVALID_ARG; } // Special cases if (mozilla::IsNaN(value)) { return aContext->recycler()->getStringResult(format->mNaN, aResult); } if (value == mozilla::PositiveInfinity<double>()) { return aContext->recycler()->getStringResult(format->mInfinity, aResult); } if (value == mozilla::NegativeInfinity<double>()) { nsAutoString res; res.Append(format->mMinusSign); res.Append(format->mInfinity); return aContext->recycler()->getStringResult(res, aResult); } // Value is a normal finite number nsAutoString prefix; nsAutoString suffix; int minIntegerSize=0; int minFractionSize=0; int maxFractionSize=0; int multiplier=1; int groupSize=-1; uint32_t pos = 0; uint32_t formatLen = formatStr.Length(); bool inQuote; // Get right subexpression inQuote = false; if (mozilla::IsNegative(value)) { while (pos < formatLen && (inQuote || formatStr.CharAt(pos) != format->mPatternSeparator)) { if (formatStr.CharAt(pos) == FORMAT_QUOTE) inQuote = !inQuote; pos++; } if (pos == formatLen) { pos = 0; prefix.Append(format->mMinusSign); } else pos++; } // Parse the format string FormatParseState pState = Prefix; inQuote = false; char16_t c = 0; while (pos < formatLen && pState != Finished) { c=formatStr.CharAt(pos++); switch (pState) { case Prefix: case Suffix: if (!inQuote) { if (c == format->mPercent) { if (multiplier == 1) multiplier = 100; else { nsAutoString err(INVALID_PARAM_VALUE); #ifdef TX_TO_STRING err.AppendLiteral(": "); toString(err); #endif aContext->receiveError(err, NS_ERROR_XPATH_INVALID_ARG); return NS_ERROR_XPATH_INVALID_ARG; } } else if (c == format->mPerMille) { if (multiplier == 1) multiplier = 1000; else { nsAutoString err(INVALID_PARAM_VALUE); #ifdef TX_TO_STRING err.AppendLiteral(": "); toString(err); #endif aContext->receiveError(err, NS_ERROR_XPATH_INVALID_ARG); return NS_ERROR_XPATH_INVALID_ARG; } } else if (c == format->mDecimalSeparator || c == format->mGroupingSeparator || c == format->mZeroDigit || c == format->mDigit || c == format->mPatternSeparator) { pState = pState == Prefix ? IntDigit : Finished; pos--; break; } } if (c == FORMAT_QUOTE) inQuote = !inQuote; else if (pState == Prefix) prefix.Append(c); else suffix.Append(c); break; case IntDigit: if (c == format->mGroupingSeparator) groupSize=0; else if (c == format->mDigit) { if (groupSize >= 0) groupSize++; } else { pState = IntZero; pos--; } break; case IntZero: if (c == format->mGroupingSeparator) groupSize = 0; else if (c == format->mZeroDigit) { if (groupSize >= 0) groupSize++; minIntegerSize++; } else if (c == format->mDecimalSeparator) { pState = FracZero; } else { pState = Suffix; pos--; } break; case FracZero: if (c == format->mZeroDigit) { maxFractionSize++; minFractionSize++; } else { pState = FracDigit; pos--; } break; case FracDigit: if (c == format->mDigit) maxFractionSize++; else { pState = Suffix; pos--; } break; case Finished: break; } } // Did we manage to parse the entire formatstring and was it valid if ((c != format->mPatternSeparator && pos < formatLen) || inQuote || groupSize == 0) { nsAutoString err(INVALID_PARAM_VALUE); #ifdef TX_TO_STRING err.AppendLiteral(": "); toString(err); #endif aContext->receiveError(err, NS_ERROR_XPATH_INVALID_ARG); return NS_ERROR_XPATH_INVALID_ARG; } /* * FINALLY we're done with the parsing * now build the result string */ value = fabs(value) * multiplier; // Prefix nsAutoString res(prefix); int bufsize; if (value > 1) bufsize = (int)log10(value) + 30; else bufsize = 1 + 30; char* buf = new char[bufsize]; NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY); int bufIntDigits, sign; char* endp; PR_dtoa(value, 0, 0, &bufIntDigits, &sign, &endp, buf, bufsize-1); int buflen = endp - buf; int intDigits; intDigits = bufIntDigits > minIntegerSize ? bufIntDigits : minIntegerSize; if (groupSize < 0) groupSize = intDigits + 10; //to simplify grouping // XXX We shouldn't use SetLength. res.SetLength(res.Length() + intDigits + // integer digits 1 + // decimal separator maxFractionSize + // fractions (intDigits-1)/groupSize); // group separators int32_t i = bufIntDigits + maxFractionSize - 1; bool carry = (0 <= i+1) && (i+1 < buflen) && (buf[i+1] >= '5'); bool hasFraction = false; uint32_t resPos = res.Length()-1; // Fractions for (; i >= bufIntDigits; --i) { int digit; if (i >= buflen || i < 0) { digit = 0; } else { digit = buf[i] - '0'; } if (carry) { digit = (digit + 1) % 10; carry = digit == 0; } if (hasFraction || digit != 0 || i < bufIntDigits+minFractionSize) { hasFraction = true; res.SetCharAt((char16_t)(digit + format->mZeroDigit), resPos--); } else { res.Truncate(resPos--); } } // Decimal separator if (hasFraction) { res.SetCharAt(format->mDecimalSeparator, resPos--); } else { res.Truncate(resPos--); } // Integer digits for (i = 0; i < intDigits; ++i) { int digit; if (bufIntDigits-i-1 >= buflen || bufIntDigits-i-1 < 0) { digit = 0; } else { digit = buf[bufIntDigits-i-1] - '0'; } if (carry) { digit = (digit + 1) % 10; carry = digit == 0; } if (i != 0 && i%groupSize == 0) { res.SetCharAt(format->mGroupingSeparator, resPos--); } res.SetCharAt((char16_t)(digit + format->mZeroDigit), resPos--); } if (carry) { if (i%groupSize == 0) { res.Insert(format->mGroupingSeparator, resPos + 1); } res.Insert((char16_t)(1 + format->mZeroDigit), resPos + 1); } if (!hasFraction && !intDigits && !carry) { // If we havn't added any characters we add a '0' // This can only happen for formats like '##.##' res.Append(format->mZeroDigit); } delete [] buf; // Build suffix res.Append(suffix); return aContext->recycler()->getStringResult(res, aResult); } //-- evaluate