bool Parser::evaluateConfigReference(QualifiedName* qname) { if (qname == NULL) return true; AvmAssert(isConfigReference(qname)); Str* ns = ((SimpleName*)qname->qualifier)->name; Str* name = ((SimpleName*)qname->name)->name; Expr* value = findConfigBinding(ns, name); if (value != NULL) return evaluateToBoolean(value); compiler->syntaxError(qname->pos, SYNTAXERR_UNBOUND_CONST_NAME); /*NOTREACHED*/ return false; }
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 ps the ContextState containing the stack information needed * for evaluation * @return the result of the evaluation **/ nsresult BooleanFunctionCall::evaluate(txIEvalContext* aContext, txAExprResult** aResult) { *aResult = nsnull; txListIterator iter(¶ms); switch (mType) { case TX_BOOLEAN: { if (!requireParams(1, 1, aContext)) return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; aContext->recycler()->getBoolResult( evaluateToBoolean((Expr*)iter.next(), aContext), aResult); return NS_OK; } case TX_LANG: { if (!requireParams(1, 1, aContext)) return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; 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; evaluateToString((Expr*)iter.next(), aContext, arg); PRBool result = arg.Equals(Substring(lang, 0, arg.Length()), txCaseInsensitiveStringComparator()) && (lang.Length() == arg.Length() || lang.CharAt(arg.Length()) == '-'); aContext->recycler()->getBoolResult(result, aResult); return NS_OK; } case TX_NOT: { if (!requireParams(1, 1, aContext)) return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; aContext->recycler()->getBoolResult( !evaluateToBoolean((Expr*)iter.next(), aContext), aResult); return NS_OK; } case TX_TRUE: { if (!requireParams(0, 0, aContext)) return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; aContext->recycler()->getBoolResult(PR_TRUE, aResult); return NS_OK; } case TX_FALSE: { if (!requireParams(0, 0, aContext)) return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT; aContext->recycler()->getBoolResult(PR_FALSE, aResult); return NS_OK; } } aContext->receiveError(NS_LITERAL_STRING("Internal error"), NS_ERROR_UNEXPECTED); return NS_ERROR_UNEXPECTED; }