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