bool ProgramScriptAction::check(const QString &xmlFile, const TypeRule *rule, SymFactory *factory) { Q_UNUSED(rule); Q_UNUSED(factory); // Delete old program if (_program) { delete _program; _program = 0; } QScriptSyntaxCheckResult result = QScriptEngine::checkSyntax(_srcCode); if (result.state() != QScriptSyntaxCheckResult::Valid) { typeRuleError2(xmlFile, srcLine() + result.errorLineNumber() - 1, result.errorColumnNumber(), QString("Syntax error: %1") .arg(result.errorMessage())); } else { // Wrap the code into a function so that the return statement // is available to the code QString code = QString("function %1() {\n%2\n}") .arg(js::inlinefunc).arg(_srcCode); _program = new QScriptProgram(code, xmlFile, srcLine() - 1); } return true; }
bool FuncCallScriptAction::check(const QString &xmlFile, const TypeRule *rule, SymFactory *factory) { Q_UNUSED(rule); Q_UNUSED(factory); // Delete old program if (_program) { delete _program; _program = 0; } // Read the contents of the script file QFile scriptFile(_scriptFile); if (!scriptFile.open(QFile::ReadOnly)) ioError(QString("Error opening file \"%1\" for reading.") .arg(_scriptFile)); _program = new QScriptProgram(scriptFile.readAll(), _scriptFile); // Basic syntax check QScriptSyntaxCheckResult result = QScriptEngine::checkSyntax(_program->sourceCode()); if (result.state() != QScriptSyntaxCheckResult::Valid) { typeRuleError2(xmlFile, srcLine(), -1, QString("Syntax error in file %1 line %2 column %3: %4") .arg(ShellUtil::shortFileName(_scriptFile)) .arg(result.errorLineNumber()) .arg(result.errorColumnNumber()) .arg(result.errorMessage())); } // Check if the function exists ScriptEngine eng(0); ScriptEngine::FuncExistsResult ret = eng.functionExists(_function, *_program); if (ret == ScriptEngine::feRuntimeError) { QString err; if (eng.hasUncaughtException()) { err = QString("Uncaught exception at line %0: %1") .arg(eng.uncaughtExceptionLineNumber()) .arg(eng.lastError()); QStringList bt = eng.uncaughtExceptionBacktrace(); for (int i = 0; i < bt.size(); ++i) err += "\n " + bt[i]; } else err = eng.lastError(); typeRuleError2(xmlFile, srcLine(), -1, QString("Runtime error in file %1: %2") .arg(ShellUtil::shortFileName(_scriptFile)) .arg(err)); } else if (ret == ScriptEngine::feDoesNotExist) { typeRuleError2(xmlFile, srcLine(), -1, QString("Function \"%1\" is not defined in file \"%2\".") .arg(_function) .arg(ShellUtil::shortFileName(_scriptFile))); } return true; }
TEST_FIXTURE(RouterFixture, throws_choose_error_exception_when_unknown_type) { std::stringstream srcLine("__*fklsajdf"); this->repeat([&] { return srcLine.get(); }, 2); CHECK_THROW(this->router.choose(srcLine.get()), husky::types::TypeChooseError); }
TEST_FIXTURE(RouterFixture, works_with_a_double) { std::stringstream srcLine(", 123.32 sfds"); this->repeat([&] { return srcLine.get(); }, 2); this->type = this->router.choose(srcLine.get()); CHECK_EQUAL(husky::types::number::id, this->type->id()); }
bool ExpressionAction::checkExprComplexity(const QString& xmlFile, const QString &what, const QString &expr) const { static const QString illegal("{};\"?"); QRegExp rx("[" + QRegExp::escape(illegal) + "]") ; if (expr.contains(rx)) typeRuleError2(xmlFile, srcLine(), -1, QString("The %1 contains one of the following " "unsupported characters: %2") .arg(what) .arg(illegal)); return true; }
bool ExpressionAction::check(const QString &xmlFile, const TypeRule *rule, SymFactory *factory) { for (int i = 0; i < _exprList.size(); ++i) delete _exprList[i]; _exprList.clear(); _srcType = 0; _targetType = 0; _expr = 0; checkExprComplexity(xmlFile, _srcTypeStr, "source type"); checkExprComplexity(xmlFile, _targetTypeStr, "target type"); checkExprComplexity(xmlFile, _exprStr, "expression"); QString srcId; // Check target type bool targetUsesId = false; _targetType = parseTypeStr(xmlFile, 0, factory, "target type", _targetTypeStr, srcId, &targetUsesId); if (_targetType) _targetType = _targetType->dereferencedBaseType(BaseType::trLexical); // Check source type bool srcUsesId = false; _srcType = parseTypeStr(xmlFile, rule, factory, "source type", _srcTypeStr, srcId, &srcUsesId); // Is the srcId valid? if (srcId.isEmpty()) typeRuleError2(xmlFile, srcLine(), -1, QString("The source type does not specify an identifier:" " %1") .arg(_srcTypeStr)); // Make sure the expression contains the srcId if (!_exprStr.contains(QRegExp("\\b" + srcId + "\\b"))) typeRuleError2(xmlFile, srcLine(), -1, QString("The expression does not use identifier \"%1\" " "which was defined in the source type.") .arg(srcId)); // Check complete expression AbstractSyntaxTree ast; ASTBuilder builder(&ast, factory); QString codeStr; // If the type was specified via ID, we have to use the pseudo typedef here if (srcUsesId) codeStr = QString("__0x%0__ %1;").arg((uint)_srcType->id(), 0, 16).arg(srcId); else codeStr = _srcTypeStr + ";"; codeStr += " int __dest__ = " + _exprStr + ";"; if (builder.buildFrom(codeStr.toAscii()) != 0) typeRuleError2(xmlFile, srcLine(), -1, QString("Syntax error in expression: %1") .arg(codeStr)); // We should finde exatcely one initializer QList<const ASTNode*> init_nodes = ASTNodeFinder::find(nt_initializer, &ast); if (init_nodes.isEmpty()) typeRuleError2(xmlFile, srcLine(), -1, QString("Error parsing expression: %1") .arg(codeStr)); // Try to evaluate expression ASTTypeEvaluator t_eval(&ast, factory->memSpecs().sizeofLong, factory->memSpecs().sizeofPointer, factory); ASTExpressionEvaluator e_eval(&t_eval, factory); ASTNodeNodeHash ptsTo; _expr = e_eval.exprOfNode(init_nodes.first(), ptsTo); if (_expr) // Expression belongs to the evaluator, we need to clone it _expr = _expr->copy(_exprList); else typeRuleError2(xmlFile, srcLine(), -1, QString("Error evaluating expression: %1") .arg(QString(_exprStr))); _altRefType.setId(_targetType->id()); _altRefType.setExpr(_expr); return true; }
const BaseType* ExpressionAction::parseTypeStr( const QString &xmlFile, const TypeRule *rule, SymFactory *factory, const QString& what, const QString& typeStr, QString& id, bool *usesTypeId) const { // Did we get a type name or a type ID? if (typeStr.startsWith("0x")) { if (usesTypeId) *usesTypeId = true; QStringList typeStrParts = typeStr.split(QRegExp("\\s+")); if (typeStrParts.isEmpty() || typeStrParts.size() > 2) typeRuleError2(xmlFile, srcLine(), -1, QString("The specified %0 '%1' is invalid (expected " "a type ID and an identifier, e.g. '0x89ab " "src').") .arg(what) .arg(typeStr)); QString typeIdStr = typeStrParts.first(); bool ok = false; int typeId = typeIdStr.right(typeIdStr.size() - 2).toUInt(&ok, 16); if (!ok) typeRuleError2(xmlFile, srcLine(), -1, QString("The type ID '%0' for the %1 is invalid.") .arg(typeIdStr) .arg(what)); const BaseType* ret = factory->findBaseTypeById(typeId); if (!ret) typeRuleError2(xmlFile, srcLine(), -1, QString("The type ID '%0' for the %1 does not exist.") .arg(typeIdStr) .arg(what)); if (typeStrParts.size() > 1) id = typeStrParts.last(); return ret; } else if (usesTypeId) *usesTypeId = false; // We got a type name expression, so parse it with the C parser AbstractSyntaxTree ast; ASTBuilder builder(&ast, factory); // Parse the code QByteArray code = QString("void %1(%2);").arg("__FUNCTION__").arg(typeStr).toAscii(); if (builder.buildFrom(code) != 0) typeRuleError3(xmlFile, srcLine(), -1, TypeRuleException::ecSyntaxError, QString("Syntax error in %0: %1") .arg(what) .arg(typeStr)); QList<const ASTNode*> paramNodes = ASTNodeFinder::find(nt_parameter_declaration, &ast); // We might have more than one parameter for function pointer types, but the // first one is the one in __FUNCTION__() assert(paramNodes.size() > 0); const ASTNode* paramNode = paramNodes.first(); // Do we have a declarator with or without an identifier? id.clear(); const ASTNode* d_ad = paramNode->u.parameter_declaration.declarator_list ? paramNode->u.parameter_declaration.declarator_list->item : 0; if (d_ad && d_ad->type == nt_declarator) { // Find the direct declaratior with identifier const ASTNode* dd = d_ad->u.declarator.direct_declarator; while (dd && !dd->u.direct_declarator.identifier) { dd = dd->u.direct_declarator.declarator ->u.declarator.direct_declarator; } assert(dd != 0); assert(dd->u.direct_declarator.identifier); id = ast.antlrTokenToStr(dd->u.direct_declarator.identifier); } KernelSourceTypeEvaluator t_eval(&ast, factory); // Evaluate type of parameter_declaration ASTType* astType = t_eval.typeofNode(paramNode); if (!astType) typeRuleError2(xmlFile, srcLine(), -1, QString("Error parsing expression: %1").arg(QString(code))); // Search correspondig BaseType FoundBaseTypes found = factory->findBaseTypesForAstType(astType, &t_eval, false); if (found.types.isEmpty()) typeRuleError2(xmlFile, srcLine(), -1, QString("Cannot find %1: %2").arg(what).arg(QString(code))); else if (found.types.size() > 1) { static const int type_ambiguous = -1; static const int no_type_found = -2; int match_idx = type_ambiguous; if (rule) { match_idx = no_type_found; // Find a unique type that matches the filter for (int i = 0; i < found.types.size(); ++i) { if (rule->match(found.types.at(i)) || rule->match(found.typesNonPtr.at(i))) { if (match_idx < 0) match_idx = i; else { const BaseType* t1 = found.typesNonPtr.at(match_idx)->dereferencedBaseType(BaseType::trLexical); const BaseType* t2 = found.typesNonPtr.at(i)->dereferencedBaseType(BaseType::trLexical); // Compare the hashes of the non-lexical types, only if // they are different the type is ambiguous. if (t1->hash() != t2->hash()) { match_idx = type_ambiguous; break; } else { // Prefer the type that exactly corresponds to the // ASTType if (found.typesNonPtr.at(i)->type() == found.astTypeNonPtr->type()) match_idx = i; } } } } } if (match_idx >= 0) return found.types.at(match_idx); else if (match_idx == type_ambiguous) typeRuleError3(xmlFile, srcLine(), -1, TypeRuleException::ecTypeAmbiguous, QString("The %1 is ambiguous, %2 types found for: %3") .arg(what) .arg(found.types.size()) .arg(typeStr)); else typeRuleError3(xmlFile, srcLine(), -1, TypeRuleException::ecNotCompatible, QString("Cannot find any compatible type for the %1: %2") .arg(what).arg(typeStr)); } else return found.types.first(); return 0; }