/* * This file is part of the API Extractor project. * * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2002-2005 Roberto Raggi * * Contact: PySide team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "binder.h" #include "lexer.h" #include "control.h" #include "symbol.h" #include "codemodel_finder.h" #include "class_compiler.h" #include "compiler_utils.h" #include "tokens.h" #include "dumptree.h" #include #include Binder::Binder(CodeModel *__model, LocationManager &__location, Control *__control) : _M_model(__model), _M_location(__location), _M_token_stream(&_M_location.token_stream), _M_control(__control), _M_current_function_type(CodeModel::Normal), type_cc(this), name_cc(this), decl_cc(this) { _M_qualified_types["char"] = QString(); _M_qualified_types["double"] = QString(); _M_qualified_types["float"] = QString(); _M_qualified_types["int"] = QString(); _M_qualified_types["long"] = QString(); _M_qualified_types["short"] = QString(); _M_qualified_types["void"] = QString(); } Binder::~Binder() { } FileModelItem Binder::run(AST *node) { FileModelItem old = _M_current_file; _M_current_access = CodeModel::Public; _M_current_file = model()->create(); updateItemPosition(_M_current_file->toItem(), node); visit(node); FileModelItem result = _M_current_file; _M_current_file = old; // restore return result; } ScopeModelItem Binder::currentScope() { if (_M_current_class) return model_static_cast(_M_current_class); else if (_M_current_namespace) return model_static_cast(_M_current_namespace); return model_static_cast(_M_current_file); } TemplateParameterList Binder::changeTemplateParameters(TemplateParameterList templateParameters) { TemplateParameterList old = _M_current_template_parameters; _M_current_template_parameters = templateParameters; return old; } CodeModel::FunctionType Binder::changeCurrentFunctionType(CodeModel::FunctionType functionType) { CodeModel::FunctionType old = _M_current_function_type; _M_current_function_type = functionType; return old; } CodeModel::AccessPolicy Binder::changeCurrentAccess(CodeModel::AccessPolicy accessPolicy) { CodeModel::AccessPolicy old = _M_current_access; _M_current_access = accessPolicy; return old; } NamespaceModelItem Binder::changeCurrentNamespace(NamespaceModelItem item) { NamespaceModelItem old = _M_current_namespace; _M_current_namespace = item; return old; } ClassModelItem Binder::changeCurrentClass(ClassModelItem item) { ClassModelItem old = _M_current_class; _M_current_class = item; return old; } FunctionDefinitionModelItem Binder::changeCurrentFunction(FunctionDefinitionModelItem item) { FunctionDefinitionModelItem old = _M_current_function; _M_current_function = item; return old; } int Binder::decode_token(std::size_t index) const { return _M_token_stream->kind(index); } CodeModel::AccessPolicy Binder::decode_access_policy(std::size_t index) const { switch (decode_token(index)) { case Token_class: return CodeModel::Private; case Token_struct: case Token_union: return CodeModel::Public; default: return CodeModel::Public; } } CodeModel::ClassType Binder::decode_class_type(std::size_t index) const { switch (decode_token(index)) { case Token_class: return CodeModel::Class; case Token_struct: return CodeModel::Struct; case Token_union: return CodeModel::Union; default: std::cerr << "** WARNING unrecognized class type" << std::endl; } return CodeModel::Class; } const NameSymbol *Binder::decode_symbol(std::size_t index) const { return _M_token_stream->symbol(index); } void Binder::visitAccessSpecifier(AccessSpecifierAST *node) { const ListNode *it = node->specs; if (!it) return; it = it->toFront(); const ListNode *end = it; do { switch (decode_token(it->element)) { default: break; case Token_public: changeCurrentAccess(CodeModel::Public); changeCurrentFunctionType(CodeModel::Normal); break; case Token_protected: changeCurrentAccess(CodeModel::Protected); changeCurrentFunctionType(CodeModel::Normal); break; case Token_private: changeCurrentAccess(CodeModel::Private); changeCurrentFunctionType(CodeModel::Normal); break; case Token_signals: changeCurrentAccess(CodeModel::Protected); changeCurrentFunctionType(CodeModel::Signal); break; case Token_slots: changeCurrentFunctionType(CodeModel::Slot); break; } it = it->next; } while (it != end); } void Binder::visitSimpleDeclaration(SimpleDeclarationAST *node) { visit(node->type_specifier); if (const ListNode *it = node->init_declarators) { it = it->toFront(); const ListNode *end = it; do { InitDeclaratorAST *init_declarator = it->element; declare_symbol(node, init_declarator); it = it->next; } while (it != end); } } void Binder::declare_symbol(SimpleDeclarationAST *node, InitDeclaratorAST *init_declarator) { DeclaratorAST *declarator = init_declarator->declarator; while (declarator && declarator->sub_declarator) declarator = declarator->sub_declarator; NameAST *id = declarator->id; if (!declarator->id) { std::cerr << "** WARNING expected a declarator id" << std::endl; return; } CodeModelFinder finder(model(), this); ScopeModelItem symbolScope = finder.resolveScope(id, currentScope()); if (!symbolScope) { name_cc.run(id); std::cerr << "** WARNING scope not found for symbol:" << qPrintable(name_cc.name()) << std::endl; return; } decl_cc.run(declarator); if (decl_cc.isFunction()) { name_cc.run(id->unqualified_name); FunctionModelItem fun = model()->create(); updateItemPosition(fun->toItem(), node); fun->setAccessPolicy(_M_current_access); fun->setFunctionType(_M_current_function_type); fun->setName(name_cc.name()); fun->setAbstract(init_declarator->initializer != 0); fun->setConstant(declarator->fun_cv != 0); fun->setTemplateParameters(_M_current_template_parameters); applyStorageSpecifiers(node->storage_specifiers, model_static_cast(fun)); applyFunctionSpecifiers(node->function_specifiers, fun); // build the type TypeInfo typeInfo = CompilerUtils::typeDescription(node->type_specifier, declarator, this); fun->setType(qualifyType(typeInfo, symbolScope->qualifiedName())); fun->setVariadics(decl_cc.isVariadics()); // ... and the signature foreach (DeclaratorCompiler::Parameter p, decl_cc.parameters()) { ArgumentModelItem arg = model()->create(); arg->setType(qualifyType(p.type, _M_context)); arg->setName(p.name); arg->setDefaultValue(p.defaultValue); if (p.defaultValue) arg->setDefaultValueExpression(p.defaultValueExpression); fun->addArgument(arg); } fun->setScope(symbolScope->qualifiedName()); symbolScope->addFunction(fun); } else { VariableModelItem var = model()->create(); updateItemPosition(var->toItem(), node); var->setTemplateParameters(_M_current_template_parameters); var->setAccessPolicy(_M_current_access); name_cc.run(id->unqualified_name); var->setName(name_cc.name()); TypeInfo typeInfo = CompilerUtils::typeDescription(node->type_specifier, declarator, this); if (declarator != init_declarator->declarator && init_declarator->declarator->parameter_declaration_clause) { typeInfo.setFunctionPointer(true); decl_cc.run(init_declarator->declarator); foreach (DeclaratorCompiler::Parameter p, decl_cc.parameters()) typeInfo.addArgument(p.type); } var->setType(qualifyType(typeInfo, _M_context)); applyStorageSpecifiers(node->storage_specifiers, model_static_cast(var)); var->setScope(symbolScope->qualifiedName()); symbolScope->addVariable(var); } } void Binder::visitFunctionDefinition(FunctionDefinitionAST *node) { Q_ASSERT(node->init_declarator); ScopeModelItem scope = currentScope(); InitDeclaratorAST *init_declarator = node->init_declarator; DeclaratorAST *declarator = init_declarator->declarator; // in the case of "void (func)()" or "void ((func))()" we need to // skip to the inner most. This is in line with how the declarator // node is generated in 'parser.cpp' while (declarator && declarator->sub_declarator) declarator = declarator->sub_declarator; Q_ASSERT(declarator->id); CodeModelFinder finder(model(), this); ScopeModelItem functionScope = finder.resolveScope(declarator->id, scope); if (!functionScope) { name_cc.run(declarator->id); std::cerr << "** WARNING scope not found for function definition:" << qPrintable(name_cc.name()) << std::endl << "\tdefinition *ignored*" << std::endl; return; } decl_cc.run(declarator); Q_ASSERT(!decl_cc.id().isEmpty()); FunctionDefinitionModelItem old = changeCurrentFunction(_M_model->create()); _M_current_function->setScope(functionScope->qualifiedName()); updateItemPosition(_M_current_function->toItem(), node); Q_ASSERT(declarator->id->unqualified_name); name_cc.run(declarator->id->unqualified_name); QString unqualified_name = name_cc.name(); _M_current_function->setName(unqualified_name); TypeInfo tmp_type = CompilerUtils::typeDescription(node->type_specifier, declarator, this); _M_current_function->setType(qualifyType(tmp_type, _M_context)); _M_current_function->setAccessPolicy(_M_current_access); _M_current_function->setFunctionType(_M_current_function_type); _M_current_function->setConstant(declarator->fun_cv); _M_current_function->setTemplateParameters(_M_current_template_parameters); applyStorageSpecifiers(node->storage_specifiers, model_static_cast(_M_current_function)); applyFunctionSpecifiers(node->function_specifiers, model_static_cast(_M_current_function)); _M_current_function->setVariadics(decl_cc.isVariadics()); foreach (DeclaratorCompiler::Parameter p, decl_cc.parameters()) { ArgumentModelItem arg = model()->create(); arg->setType(qualifyType(p.type, functionScope->qualifiedName())); arg->setName(p.name); arg->setDefaultValue(p.defaultValue); if (p.defaultValue) arg->setDefaultValueExpression(p.defaultValueExpression); _M_current_function->addArgument(arg); } functionScope->addFunctionDefinition(_M_current_function); FunctionModelItem prototype = model_static_cast(_M_current_function); FunctionModelItem declared = functionScope->declaredFunction(prototype); // try to find a function declaration for this definition.. if (!declared) { functionScope->addFunction(prototype); } else { applyFunctionSpecifiers(node->function_specifiers, declared); // fix the function type and the access policy _M_current_function->setAccessPolicy(declared->accessPolicy()); _M_current_function->setFunctionType(declared->functionType()); } changeCurrentFunction(old); } void Binder::visitTemplateDeclaration(TemplateDeclarationAST *node) { const ListNode *it = node->template_parameters; if (!it) { // QtScript: we want to visit the declaration still, so that // e.g. QMetaTypeId is added to the code model visit(node->declaration); return; } TemplateParameterList savedTemplateParameters = changeTemplateParameters(TemplateParameterList()); it = it->toFront(); const ListNode *end = it; TemplateParameterList templateParameters; do { TemplateParameterAST *parameter = it->element; TypeParameterAST *type_parameter = parameter->type_parameter; NameAST *name; if (!type_parameter) { // A hacky hack to work around missing support for parameter declarations in // templates. We just need the to get the name of the variable, since we // aren't actually compiling these anyway. We are still not supporting much // more, but we are refusing to fail for a few more declarations if (!parameter->parameter_declaration || !parameter->parameter_declaration->declarator || !parameter->parameter_declaration->declarator->id) { /*std::cerr << "** WARNING template declaration not supported ``"; Token const &tk = _M_token_stream->token ((int) node->start_token); Token const &end_tk = _M_token_stream->token ((int) node->declaration->start_token); std::cerr << std::string (&tk.text[tk.position], (end_tk.position) - tk.position) << "''" << std::endl << std::endl;*/ changeTemplateParameters(savedTemplateParameters); return; } name = parameter->parameter_declaration->declarator->id; } else { int tk = decode_token(type_parameter->type); if (tk != Token_typename && tk != Token_class) { /*std::cerr << "** WARNING template declaration not supported ``"; Token const &tk = _M_token_stream->token ((int) node->start_token); Token const &end_tk = _M_token_stream->token ((int) node->declaration->start_token); std::cerr << std::string (&tk.text[tk.position], (end_tk.position) - tk.position) << "''" << std::endl << std::endl;*/ changeTemplateParameters(savedTemplateParameters); return; } assert(tk == Token_typename || tk == Token_class); name = type_parameter->name; } TemplateParameterModelItem p = model()->create(); name_cc.run(name); p->setName(name_cc.name()); _M_current_template_parameters.append(p); it = it->next; } while (it != end); visit(node->declaration); changeTemplateParameters(savedTemplateParameters); } void Binder::visitTypedef(TypedefAST *node) { const ListNode *it = node->init_declarators; if (!it) return; it = it->toFront(); const ListNode *end = it; do { InitDeclaratorAST *init_declarator = it->element; it = it->next; Q_ASSERT(init_declarator->declarator); // the name decl_cc.run(init_declarator->declarator); QString alias_name = decl_cc.id(); if (alias_name.isEmpty()) { std::cerr << "** WARNING anonymous typedef not supported! ``"; Token const &tk = _M_token_stream->token((int) node->start_token); Token const &end_tk = _M_token_stream->token((int) node->end_token); std::cerr << std::string(&tk.text[tk.position], end_tk.position - tk.position) << "''" << std::endl << std::endl; continue; } // build the type TypeInfo typeInfo = CompilerUtils::typeDescription(node->type_specifier, init_declarator->declarator, this); DeclaratorAST *decl = init_declarator->declarator; while (decl && decl->sub_declarator) decl = decl->sub_declarator; if (decl != init_declarator->declarator && init_declarator->declarator->parameter_declaration_clause) { typeInfo.setFunctionPointer(true); decl_cc.run(init_declarator->declarator); foreach (DeclaratorCompiler::Parameter p, decl_cc.parameters()) typeInfo.addArgument(p.type); } ScopeModelItem scope = currentScope(); DeclaratorAST *declarator = init_declarator->declarator; CodeModelFinder finder(model(), this); ScopeModelItem typedefScope = finder.resolveScope(declarator->id, scope); TypeAliasModelItem typeAlias = model()->create (); updateItemPosition(typeAlias->toItem(), node); typeAlias->setName(alias_name); typeAlias->setType(qualifyType(typeInfo, currentScope()->qualifiedName())); typeAlias->setScope(typedefScope->qualifiedName()); _M_qualified_types[typeAlias->qualifiedName().join(".")] = QString(); currentScope()->addTypeAlias(typeAlias); } while (it != end); } void Binder::visitNamespace(NamespaceAST *node) { bool anonymous = (node->namespace_name == 0); ScopeModelItem scope = currentScope(); NamespaceModelItem old; if (!anonymous) { QString name = decode_symbol(node->namespace_name)->as_string(); QStringList qualified_name = scope->qualifiedName(); qualified_name += name; NamespaceModelItem ns = model_safe_cast(_M_model->findItem(qualified_name, _M_current_file->toItem())); if (!ns) { ns = _M_model->create(); updateItemPosition(ns->toItem(), node); ns->setName(name); ns->setScope(scope->qualifiedName()); } old = changeCurrentNamespace(ns); _M_context.append(name); } DefaultVisitor::visitNamespace(node); if (!anonymous) { Q_ASSERT(scope->kind() == _CodeModelItem::Kind_Namespace || scope->kind() == _CodeModelItem::Kind_File); _M_context.removeLast(); if (NamespaceModelItem ns = model_static_cast(scope)) ns->addNamespace(_M_current_namespace); changeCurrentNamespace(old); } } void Binder::visitForwardDeclarationSpecifier(ForwardDeclarationSpecifierAST *node) { name_cc.run(node->name); if (name_cc.name().isEmpty()) return; ScopeModelItem scope = currentScope(); _M_qualified_types[(scope->qualifiedName() + name_cc.qualifiedName()).join(".")] = QString(); } void Binder::visitClassSpecifier(ClassSpecifierAST *node) { ClassCompiler class_cc(this); class_cc.run(node); if (class_cc.name().isEmpty()) { // anonymous not supported return; } Q_ASSERT(node->name && node->name->unqualified_name); ScopeModelItem scope = currentScope(); ClassModelItem old = changeCurrentClass(_M_model->create()); updateItemPosition(_M_current_class->toItem(), node); _M_current_class->setName(class_cc.name()); QStringList baseClasses = class_cc.baseClasses(); TypeInfo info; for (int i = 0; i < baseClasses.size(); ++i) { info.setQualifiedName(baseClasses.at(i).split("::")); baseClasses[i] = qualifyType(info, scope->qualifiedName()).qualifiedName().join("::"); } _M_current_class->setBaseClasses(baseClasses); _M_current_class->setClassType(decode_class_type(node->class_key)); _M_current_class->setTemplateParameters(_M_current_template_parameters); if (!_M_current_template_parameters.isEmpty()) { QString name = _M_current_class->name(); name += "<"; for (int i = 0; i < _M_current_template_parameters.size(); ++i) { if (i > 0) name += ","; name += _M_current_template_parameters.at(i)->name(); } name += ">"; _M_current_class->setName(name); } CodeModel::AccessPolicy oldAccessPolicy = changeCurrentAccess(decode_access_policy(node->class_key)); CodeModel::FunctionType oldFunctionType = changeCurrentFunctionType(CodeModel::Normal); _M_current_class->setScope(scope->qualifiedName()); _M_qualified_types[_M_current_class->qualifiedName().join(".")] = QString(); scope->addClass(_M_current_class); name_cc.run(node->name->unqualified_name); _M_context.append(name_cc.name()); visitNodes(this, node->member_specs); _M_context.removeLast(); changeCurrentClass(old); changeCurrentAccess(oldAccessPolicy); changeCurrentFunctionType(oldFunctionType); } void Binder::visitLinkageSpecification(LinkageSpecificationAST *node) { DefaultVisitor::visitLinkageSpecification(node); } void Binder::visitUsing(UsingAST *node) { DefaultVisitor::visitUsing(node); } void Binder::visitEnumSpecifier(EnumSpecifierAST *node) { CodeModelFinder finder(model(), this); ScopeModelItem scope = currentScope(); ScopeModelItem enumScope = finder.resolveScope(node->name, scope); name_cc.run(node->name); QString name = name_cc.name(); if (name.isEmpty()) { // anonymous enum QString key = _M_context.join("::"); int current = ++_M_anonymous_enums[key]; name += QLatin1String("enum_"); name += QString::number(current); } _M_current_enum = model()->create(); _M_current_enum->setAccessPolicy(_M_current_access); updateItemPosition(_M_current_enum->toItem(), node); _M_current_enum->setName(name); _M_current_enum->setScope(enumScope->qualifiedName()); _M_qualified_types[_M_current_enum->qualifiedName().join(".")] = QString(); enumScope->addEnum(_M_current_enum); DefaultVisitor::visitEnumSpecifier(node); _M_current_enum = 0; } static QString strip_preprocessor_lines(const QString &name) { QStringList lst = name.split("\n"); QString s; for (int i = 0; i < lst.size(); ++i) { if (!lst.at(i).startsWith('#')) s += lst.at(i); } return s.trimmed(); } void Binder::visitEnumerator(EnumeratorAST *node) { Q_ASSERT(_M_current_enum); EnumeratorModelItem e = model()->create(); updateItemPosition(e->toItem(), node); e->setName(decode_symbol(node->id)->as_string()); if (ExpressionAST *expr = node->expression) { const Token &start_token = _M_token_stream->token((int) expr->start_token); const Token &end_token = _M_token_stream->token((int) expr->end_token); e->setValue(strip_preprocessor_lines(QString::fromUtf8(&start_token.text[start_token.position], (int)(end_token.position - start_token.position)).trimmed()).remove(' ')); } _M_current_enum->addEnumerator(e); } void Binder::visitUsingDirective(UsingDirectiveAST *node) { DefaultVisitor::visitUsingDirective(node); } void Binder::visitQEnums(QEnumsAST *node) { const Token &start = _M_token_stream->token((int) node->start_token); const Token &end = _M_token_stream->token((int) node->end_token); QStringList enum_list = QString::fromLatin1(start.text + start.position, end.position - start.position).split(' '); ScopeModelItem scope = currentScope(); for (int i = 0; i < enum_list.size(); ++i) scope->addEnumsDeclaration(enum_list.at(i)); } void Binder::visitQProperty(QPropertyAST *node) { const Token &start = _M_token_stream->token((int) node->start_token); const Token &end = _M_token_stream->token((int) node->end_token); QString property = QString::fromLatin1(start.text + start.position, end.position - start.position); _M_current_class->addPropertyDeclaration(property); } void Binder::applyStorageSpecifiers(const ListNode *it, MemberModelItem item) { if (!it) return; it = it->toFront(); const ListNode *end = it; do { switch (decode_token(it->element)) { default: break; case Token_friend: item->setFriend(true); break; case Token_auto: item->setAuto(true); break; case Token_register: item->setRegister(true); break; case Token_static: item->setStatic(true); break; case Token_extern: item->setExtern(true); break; case Token_mutable: item->setMutable(true); break; } it = it->next; } while (it != end); } void Binder::applyFunctionSpecifiers(const ListNode *it, FunctionModelItem item) { if (!it) return; it = it->toFront(); const ListNode *end = it; do { switch (decode_token(it->element)) { default: break; case Token_inline: item->setInline(true); break; case Token_virtual: item->setVirtual(true); break; case Token_explicit: item->setExplicit(true); break; case Token_Q_INVOKABLE: item->setInvokable(true); break; } it = it->next; } while (it != end); } TypeInfo Binder::qualifyType(const TypeInfo &type, const QStringList &context) const { // ### Potentially improve to use string list in the name table to if (!context.size()) { // ### We can assume that this means global namespace for now... return type; } else if (_M_qualified_types.contains(type.qualifiedName().join("."))) { return type; } else { QStringList expanded = context; expanded << type.qualifiedName(); if (_M_qualified_types.contains(expanded.join("."))) { TypeInfo modified_type = type; modified_type.setQualifiedName(expanded); return modified_type; } else { CodeModelItem scope = model()->findItem(context, _M_current_file->toItem()); if (ClassModelItem klass = model_dynamic_cast (scope)) { foreach (QString base, klass->baseClasses()) { QStringList ctx = context; ctx.removeLast(); ctx.append(base); TypeInfo qualified = qualifyType(type, ctx); if (qualified != type) return qualified; } } QStringList copy = context; copy.removeLast(); return qualifyType(type, copy); } } } void Binder::updateItemPosition(CodeModelItem item, AST *node) { QString filename; int line, column; assert(node); _M_location.positionAt(_M_token_stream->position(node->start_token), &line, &column, &filename); item->setFileName(filename); }