File indexing completed on 2024-05-05 16:11:46

0001 /*
0002  * predicate.cc - Copyright 2005 Frerich Raabe   <raabe@kde.org>
0003  *                Copyright 2010 Maksim Orlovich <maksim@kde.org>
0004  *
0005  * Redistribution and use in source and binary forms, with or without
0006  * modification, are permitted provided that the following conditions
0007  * are met:
0008  *
0009  * 1. Redistributions of source code must retain the above copyright
0010  *    notice, this list of conditions and the following disclaimer.
0011  * 2. Redistributions in binary form must reproduce the above copyright
0012  *    notice, this list of conditions and the following disclaimer in the
0013  *    documentation and/or other materials provided with the distribution.
0014  *
0015  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
0016  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0017  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
0018  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
0019  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
0020  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0021  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0022  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0023  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
0024  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0025  */
0026 #include "predicate.h"
0027 #include "functions.h"
0028 
0029 #include <QString>
0030 
0031 #include "xml/dom_nodeimpl.h"
0032 #include "xml/dom_nodelistimpl.h"
0033 #include "kjs/operations.h"
0034 #include "kjs/value.h"
0035 
0036 #include <math.h>
0037 
0038 using namespace DOM;
0039 using namespace khtml;
0040 using namespace khtml::XPath;
0041 
0042 Number::Number(double value)
0043     : m_value(value)
0044 {
0045 }
0046 
0047 bool Number::isConstant() const
0048 {
0049     return true;
0050 }
0051 
0052 QString Number::dump() const
0053 {
0054     return "<number>" + QString::number(m_value) + "</number>";
0055 }
0056 
0057 Value Number::doEvaluate() const
0058 {
0059     return Value(m_value);
0060 }
0061 
0062 String::String(const DOMString &value)
0063     : m_value(value)
0064 {
0065 }
0066 
0067 bool String::isConstant() const
0068 {
0069     return true;
0070 }
0071 
0072 QString String::dump() const
0073 {
0074     return "<string>" + m_value.string() + "</string>";
0075 }
0076 
0077 Value String::doEvaluate() const
0078 {
0079     return Value(m_value);
0080 }
0081 
0082 Value Negative::doEvaluate() const
0083 {
0084     Value p(subExpr(0)->evaluate());
0085     return Value(-p.toNumber());
0086 }
0087 
0088 QString Negative::dump() const
0089 {
0090     return "<negative>" + subExpr(0)->dump() + "</number>";
0091 }
0092 
0093 QString BinaryExprBase::dump() const
0094 {
0095     QString s = "<" + opName() + ">";
0096     s += "<operand>" + subExpr(0)->dump() + "</operand>";
0097     s += "<operand>" + subExpr(1)->dump() + "</operand>";
0098     s += "</" + opName() + ">";
0099     return s;
0100 }
0101 
0102 NumericOp::NumericOp(int _opCode, Expression *lhs, Expression *rhs) :
0103     opCode(_opCode)
0104 {
0105     addSubExpression(lhs);
0106     addSubExpression(rhs);
0107 }
0108 
0109 Value NumericOp::doEvaluate() const
0110 {
0111     Value lhs(subExpr(0)->evaluate());
0112     Value rhs(subExpr(1)->evaluate());
0113     double leftVal = lhs.toNumber(), rightVal = rhs.toNumber();
0114 
0115     switch (opCode) {
0116     case OP_Add:
0117         return Value(leftVal + rightVal);
0118     case OP_Sub:
0119         return Value(leftVal - rightVal);
0120     case OP_Mul:
0121         return Value(leftVal * rightVal);
0122     case OP_Div:
0123         if (rightVal == 0.0 || rightVal == -0.0) {
0124             if (leftVal == 0.0 || leftVal == -0.0) {
0125                 return Value(); // 0/0 = NaN
0126             } else {
0127                 // +/- Infinity.
0128                 using namespace std; // for signbit
0129                 if (signbit(leftVal) == signbit(rightVal)) {
0130                     return Value(KJS::Inf);
0131                 } else {
0132                     return Value(-KJS::Inf);
0133                 }
0134             }
0135         } else {
0136             return Value(leftVal / rightVal);
0137         }
0138     case OP_Mod:
0139         if (rightVal == 0.0 || rightVal == -0.0) {
0140             return Value();    //Divide by 0;
0141         } else {
0142             return Value(remainder(leftVal, rightVal));
0143         }
0144     default:
0145         assert(0);
0146         return Value();
0147     }
0148 }
0149 
0150 QString NumericOp::opName() const
0151 {
0152     switch (opCode) {
0153     case OP_Add:
0154         return QLatin1String("addition");
0155     case OP_Sub:
0156         return QLatin1String("subtraction");
0157     case OP_Mul:
0158         return QLatin1String("multiplication");
0159     case OP_Div:
0160         return QLatin1String("division");
0161     case OP_Mod:
0162         return QLatin1String("modulo");
0163     default:
0164         assert(0);
0165         return QString();
0166     }
0167 }
0168 
0169 RelationOp::RelationOp(int _opCode, Expression *lhs, Expression *rhs) :
0170     opCode(_opCode)
0171 {
0172     addSubExpression(lhs);
0173     addSubExpression(rhs);
0174 }
0175 
0176 static void stringify(const Value &val, WTF::Vector<DOMString> *out)
0177 {
0178     if (val.isString()) {
0179         out->append(val.toString());
0180     } else {
0181         assert(val.isNodeset());
0182 
0183         const DomNodeList &set = val.toNodeset();
0184         for (unsigned long i = 0; i < set->length(); ++i) {
0185             DOM::DOMString stringVal = stringValue(set->item(i));
0186             out->append(stringVal);
0187         }
0188     }
0189 }
0190 
0191 static void numify(const Value &val, WTF::Vector<double> *out)
0192 {
0193     if (val.isNumber()) {
0194         out->append(val.toNumber());
0195     } else {
0196         assert(val.isNodeset());
0197 
0198         const DomNodeList &set = val.toNodeset();
0199         for (unsigned long i = 0; i < set->length(); ++i) {
0200             DOM::DOMString stringVal = stringValue(set->item(i));
0201             out->append(Value(stringVal).toNumber());
0202         }
0203     }
0204 }
0205 
0206 Value RelationOp::doEvaluate() const
0207 {
0208     Value lhs(subExpr(0)->evaluate());
0209     Value rhs(subExpr(1)->evaluate());
0210 
0211     if (lhs.isNodeset() || rhs.isNodeset()) {
0212         // If both are nodesets, or one is a string our
0213         // comparisons are based on strings.
0214         if ((lhs.isNodeset() && rhs.isNodeset()) ||
0215                 (lhs.isString()  || rhs.isString())) {
0216 
0217             WTF::Vector<DOM::DOMString> leftStrings;
0218             WTF::Vector<DOM::DOMString> rightStrings;
0219 
0220             stringify(lhs, &leftStrings);
0221             stringify(rhs, &rightStrings);
0222 
0223             for (unsigned pl = 0; pl < leftStrings.size(); ++pl) {
0224                 for (unsigned pr = 0; pr < rightStrings.size(); ++pr) {
0225                     if (compareStrings(leftStrings[pl], rightStrings[pr])) {
0226                         return Value(true);
0227                     }
0228                 } // pr
0229             } // pl
0230             return Value(false);
0231         }
0232 
0233         // If one is a number, we do a number-based comparison
0234         if (lhs.isNumber() || rhs.isNumber()) {
0235             WTF::Vector<double> leftNums;
0236             WTF::Vector<double> rightNums;
0237 
0238             numify(lhs, &leftNums);
0239             numify(rhs, &rightNums);
0240 
0241             for (unsigned pl = 0; pl < leftNums.size(); ++pl) {
0242                 for (unsigned pr = 0; pr < rightNums.size(); ++pr) {
0243                     if (compareNumbers(leftNums[pl], rightNums[pr])) {
0244                         return Value(true);
0245                     }
0246                 } // pr
0247             } // pl
0248             return Value(false);
0249         }
0250 
0251         // Has to be a boolean-based comparison.
0252         // These ones are simpler, since we just convert the nodeset to a bool
0253         assert(lhs.isBoolean() || rhs.isBoolean());
0254 
0255         if (lhs.isNodeset()) {
0256             lhs = Value(lhs.toBoolean());
0257         } else {
0258             rhs = Value(rhs.toBoolean());
0259         }
0260     } // nodeset comparisons
0261 
0262     if (opCode == OP_EQ || opCode == OP_NE) {
0263         bool equal;
0264         if (lhs.isBoolean() || rhs.isBoolean()) {
0265             equal = (lhs.toBoolean() == rhs.toBoolean());
0266         } else if (lhs.isNumber() || rhs.isNumber()) {
0267             equal = (lhs.toNumber() == rhs.toNumber());
0268         } else {
0269             equal = (lhs.toString() == rhs.toString());
0270         }
0271 
0272         if (opCode == OP_EQ) {
0273             return Value(equal);
0274         } else {
0275             return Value(!equal);
0276         }
0277 
0278     }
0279 
0280     // For other ops, we always convert to numbers
0281     double leftVal = lhs.toNumber(), rightVal = rhs.toNumber();
0282     return Value(compareNumbers(leftVal, rightVal));
0283 }
0284 
0285 bool RelationOp::compareNumbers(double leftVal, double rightVal) const
0286 {
0287     switch (opCode) {
0288     case OP_GT:
0289         return leftVal > rightVal;
0290     case OP_GE:
0291         return leftVal >= rightVal;
0292     case OP_LT:
0293         return leftVal < rightVal;
0294     case OP_LE:
0295         return leftVal <= rightVal;
0296     case OP_EQ:
0297         return leftVal == rightVal;
0298     case OP_NE:
0299         return leftVal != rightVal;
0300     default:
0301         assert(0);
0302         return false;
0303     }
0304 }
0305 
0306 bool RelationOp::compareStrings(const DOM::DOMString &l, const DOM::DOMString &r) const
0307 {
0308     // String comparisons, as invoked within the nodeset case, are string-based
0309     // only for == and !=. Everything else still goes to numbers.
0310     if (opCode == OP_EQ) {
0311         return (l == r);
0312     }
0313     if (opCode == OP_NE) {
0314         return (l != r);
0315     }
0316 
0317     return compareNumbers(Value(l).toNumber(), Value(r).toNumber());
0318 }
0319 
0320 QString RelationOp::opName() const
0321 {
0322     switch (opCode) {
0323     case OP_GT:
0324         return QLatin1String("relationGT");
0325     case OP_GE:
0326         return QLatin1String("relationGE");
0327     case OP_LT:
0328         return QLatin1String("relationLT");
0329     case OP_LE:
0330         return QLatin1String("relationLE");
0331     case OP_EQ:
0332         return QLatin1String("relationEQ");
0333     case OP_NE:
0334         return QLatin1String("relationNE");
0335     default:
0336         assert(0);
0337         return QString();
0338     }
0339 }
0340 
0341 LogicalOp::LogicalOp(int _opCode, Expression *lhs, Expression *rhs) :
0342     opCode(_opCode)
0343 {
0344     addSubExpression(lhs);
0345     addSubExpression(rhs);
0346 }
0347 
0348 bool LogicalOp::shortCircuitOn() const
0349 {
0350     if (opCode == OP_And) {
0351         return false;    //false and foo
0352     } else {
0353         return true;    //true or bar
0354     }
0355 }
0356 
0357 bool LogicalOp::isConstant() const
0358 {
0359     return subExpr(0)->isConstant() &&
0360            subExpr(0)->evaluate().toBoolean() == shortCircuitOn();
0361 }
0362 
0363 QString LogicalOp::opName() const
0364 {
0365     if (opCode == OP_And) {
0366         return QLatin1String("conjunction");
0367     } else {
0368         return QLatin1String("disjunction");
0369     }
0370 }
0371 
0372 Value LogicalOp::doEvaluate() const
0373 {
0374     Value lhs(subExpr(0)->evaluate());
0375 
0376     // This is not only an optimization, https://www.w3.org/TR/xpath
0377     // dictates that we must do short-circuit evaluation
0378     bool lhsBool = lhs.toBoolean();
0379     if (lhsBool == shortCircuitOn()) {
0380         return Value(lhsBool);
0381     }
0382 
0383     return Value(subExpr(1)->evaluate().toBoolean());
0384 }
0385 
0386 QString Union::opName() const
0387 {
0388     return QLatin1String("union");
0389 }
0390 
0391 Value Union::doEvaluate() const
0392 {
0393     Value lhs = subExpr(0)->evaluate();
0394     Value rhs = subExpr(1)->evaluate();
0395     if (!lhs.isNodeset() || !rhs.isNodeset()) {
0396         qCWarning(KHTML_LOG) << "Union operator '|' works only with nodesets.";
0397         Expression::reportInvalidExpressionErr();
0398         return Value(new StaticNodeListImpl);
0399     }
0400 
0401     DomNodeList lhsNodes = lhs.toNodeset();
0402     DomNodeList rhsNodes = rhs.toNodeset();
0403     DomNodeList result = new StaticNodeListImpl;
0404 
0405     for (unsigned long n = 0; n < lhsNodes->length(); ++n) {
0406         result->append(lhsNodes->item(n));
0407     }
0408 
0409     for (unsigned long n = 0; n < rhsNodes->length(); ++n) {
0410         result->append(rhsNodes->item(n));
0411     }
0412 
0413     return Value(result);
0414 }
0415 
0416 Predicate::Predicate(Expression *expr)
0417     : m_expr(expr)
0418 {
0419 }
0420 
0421 Predicate::~Predicate()
0422 {
0423     delete m_expr;
0424 }
0425 
0426 bool Predicate::evaluate() const
0427 {
0428     Q_ASSERT(m_expr != nullptr);
0429 
0430     Value result(m_expr->evaluate());
0431 
0432     // foo[3] really means foo[position()=3]
0433     if (result.isNumber()) {
0434         return double(Expression::evaluationContext().position) == result.toNumber();
0435     }
0436 
0437     return result.toBoolean();
0438 }
0439 
0440 void Predicate::optimize()
0441 {
0442     m_expr->optimize();
0443 }
0444 
0445 QString Predicate::dump() const
0446 {
0447     return QString() + "<predicate>" + m_expr->dump() + "</predicate>";
0448 }
0449