Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  var.rs

  Sprache: Rust
 

//! Intermediate representation of variables.

use super::super::codegen::MacroTypeVariation;
use super::context::{BindgenContext, TypeId};
use super::dot::DotAttributes;
use super::function::cursor_mangling;
use super::int::IntKind;
use super::item::Item;
use super::ty::{FloatKind, TypeKind};
use crate::callbacks::{ItemInfo, ItemKind, MacroParsingBehavior};
use crate::clang;
use crate::clang::ClangToken;
use crate::parse::{ClangSubItemParser, ParseError, ParseResult};

use std::io;
use std::num::Wrapping;

/// The type for a constant variable.
#[derive(Debug)]
pub(crateenum VarType {
    /// A boolean.
    Bool(bool),
    /// An integer.
    Int(i64),
    /// A floating point number.
    Float(f64),
    /// A character.
    Char(u8),
    /// A string, not necessarily well-formed utf-8.
    String(Vec<u8>),
}

/// A `Var` is our intermediate representation of a variable.
#[derive(Debug)]
pub(cratestruct Var {
    /// The name of the variable.
    name: String,
    /// The mangled name of the variable.
    mangled_name: Option<String>,
    /// The link name of the variable.
    link_name: Option<String>,
    /// The type of the variable.
    ty: TypeId,
    /// The value of the variable, that needs to be suitable for `ty`.
    val: Option<VarType>,
    /// Whether this variable is const.
    is_const: bool,
}

impl Var {
    /// Construct a new `Var`.
    pub(cratefn new(
        name: String,
        mangled_name: Option<String>,
        link_name: Option<String>,
        ty: TypeId,
        val: Option<VarType>,
        is_const: bool,
    ) -> Var {
        assert!(!name.is_empty());
        Var {
            name,
            mangled_name,
            link_name,
            ty,
            val,
            is_const,
        }
    }

    /// Is this variable `const` qualified?
    pub(cratefn is_const(&self) -> bool {
        self.is_const
    }

    /// The value of this constant variable, if any.
    pub(cratefn val(&self) -> Option<&VarType> {
        self.val.as_ref()
    }

    /// Get this variable's type.
    pub(cratefn ty(&self) -> TypeId {
        self.ty
    }

    /// Get this variable's name.
    pub(cratefn name(&self) -> &str {
        &self.name
    }

    /// Get this variable's mangled name.
    pub(cratefn mangled_name(&self) -> Option<&str> {
        self.mangled_name.as_deref()
    }

    /// Get this variable's link name.
    pub fn link_name(&self) -> Option<&str> {
        self.link_name.as_deref()
    }
}

impl DotAttributes for Var {
    fn dot_attributes<W>(
        &self,
        _ctx: &BindgenContext,
        out: &mut W,
    ) -> io::Result<()>
    where
        W: io::Write,
    {
        if self.is_const {
            writeln!(out, "<tr><td>const</td><td>true</td></tr>")?;
        }

        if let Some(ref mangled) = self.mangled_name {
            writeln!(out, "<tr><td>mangled name</td><td>{mangled}</td></tr>")?;
        }

        Ok(())
    }
}

fn default_macro_constant_type(ctx: &BindgenContext, value: i64) -> IntKind {
    if value < 0 ||
        ctx.options().default_macro_constant_type ==
            MacroTypeVariation::Signed
    {
        if value < i64::from(i32::MIN) || value > i64::from(i32::MAX) {
            IntKind::I64
        } else if !ctx.options().fit_macro_constants ||
            value < i64::from(i16::MIN) ||
            value > i64::from(i16::MAX)
        {
            IntKind::I32
        } else if value < i64::from(i8::MIN) || value > i64::from(i8::MAX) {
            IntKind::I16
        } else {
            IntKind::I8
        }
    } else if value > i64::from(u32::MAX) {
        IntKind::U64
    } else if !ctx.options().fit_macro_constants || value > i64::from(u16::MAX)
    {
        IntKind::U32
    } else if value > i64::from(u8::MAX) {
        IntKind::U16
    } else {
        IntKind::U8
    }
}

/// Parses tokens from a `CXCursor_MacroDefinition` pointing into a function-like
/// macro, and calls the `func_macro` callback.
fn handle_function_macro(
    cursor: &clang::Cursor,
    callbacks: &dyn crate::callbacks::ParseCallbacks,
) {
    let is_closing_paren = |t: &ClangToken| {
        // Test cheap token kind before comparing exact spellings.
        t.kind == clang_sys::CXToken_Punctuation && t.spelling() == b")"
    };
    let tokens: Vec<_> = cursor.tokens().iter().collect();
    if let Some(boundary) = tokens.iter().position(is_closing_paren) {
        let mut spelled = tokens.iter().map(ClangToken::spelling);
        // Add 1, to convert index to length.
        let left = spelled.by_ref().take(boundary + 1);
        let left = left.collect::<Vec<_>>().concat();
        if let Ok(left) = String::from_utf8(left) {
            let right: Vec<_> = spelled.collect();
            callbacks.func_macro(&left, &right);
        }
    }
}

impl ClangSubItemParser for Var {
    fn parse(
        cursor: clang::Cursor,
        ctx: &mut BindgenContext,
    ) -> Result<ParseResult<Self>, ParseError> {
        use cexpr::expr::EvalResult;
        use cexpr::literal::CChar;
        use clang_sys::*;
        match cursor.kind() {
            CXCursor_MacroDefinition => {
                for callbacks in &ctx.options().parse_callbacks {
                    match callbacks.will_parse_macro(&cursor.spelling()) {
                        MacroParsingBehavior::Ignore => {
                            return Err(ParseError::Continue);
                        }
                        MacroParsingBehavior::Default => {}
                    }

                    if cursor.is_macro_function_like() {
                        handle_function_macro(&cursor, callbacks.as_ref());
                        // We handled the macro, skip macro processing below.
                        return Err(ParseError::Continue);
                    }
                }

                let value = parse_macro(ctx, &cursor);

                let Some((id, value)) = value else {
                    return Err(ParseError::Continue);
                };

                assert!(!id.is_empty(), "Empty macro name?");

                let previously_defined = ctx.parsed_macro(&id);

                // NB: It's important to "note" the macro even if the result is
                // not an integer, otherwise we might loose other kind of
                // derived macros.
                ctx.note_parsed_macro(id.clone(), value.clone());

                if previously_defined {
                    let name = String::from_utf8(id).unwrap();
                    duplicated_macro_diagnostic(&name, cursor.location(), ctx);
                    return Err(ParseError::Continue);
                }

                // NOTE: Unwrapping, here and above, is safe, because the
                // identifier of a token comes straight from clang, and we
                // enforce utf8 there, so we should have already panicked at
                // this point.
                let name = String::from_utf8(id).unwrap();
                let (type_kind, val) = match value {
                    EvalResult::Invalid => return Err(ParseError::Continue),
                    EvalResult::Float(f) => {
                        (TypeKind::Float(FloatKind::Double), VarType::Float(f))
                    }
                    EvalResult::Char(c) => {
                        let c = match c {
                            CChar::Char(c) => {
                                assert_eq!(c.len_utf8(), 1);
                                c as u8
                            }
                            CChar::Raw(c) => u8::try_from(c).unwrap(),
                        };

                        (TypeKind::Int(IntKind::U8), VarType::Char(c))
                    }
                    EvalResult::Str(val) => {
                        let char_ty = Item::builtin_type(
                            TypeKind::Int(IntKind::U8),
                            true,
                            ctx,
                        );
                        for callbacks in &ctx.options().parse_callbacks {
                            callbacks.str_macro(&name, &val);
                        }
                        (TypeKind::Pointer(char_ty), VarType::String(val))
                    }
                    EvalResult::Int(Wrapping(value)) => {
                        let kind = ctx
                            .options()
                            .last_callback(|c| c.int_macro(&name, value))
                            .unwrap_or_else(|| {
                                default_macro_constant_type(ctx, value)
                            });

                        (TypeKind::Int(kind), VarType::Int(value))
                    }
                };

                let ty = Item::builtin_type(type_kind, true, ctx);

                Ok(ParseResult::New(
                    Var::new(name, None, None, ty, Some(val), true),
                    Some(cursor),
                ))
            }
            CXCursor_VarDecl => {
                let mut name = cursor.spelling();
                if cursor.linkage() == CXLinkage_External {
                    if let Some(nm) = ctx.options().last_callback(|callbacks| {
                        callbacks.generated_name_override(ItemInfo {
                            name: name.as_str(),
                            kind: ItemKind::Var,
                        })
                    }) {
                        name = nm;
                    }
                }
                // No more changes to name
                let name = name;

                if name.is_empty() {
                    warn!("Empty constant name?");
                    return Err(ParseError::Continue);
                }

                let link_name = ctx.options().last_callback(|callbacks| {
                    callbacks.generated_link_name_override(ItemInfo {
                        name: name.as_str(),
                        kind: ItemKind::Var,
                    })
                });

                let ty = cursor.cur_type();

                // TODO(emilio): do we have to special-case constant arrays in
                // some other places?
                let is_const = ty.is_const() ||
                    ([CXType_ConstantArray, CXType_IncompleteArray]
                        .contains(&ty.kind()) &&
                        ty.elem_type()
                            .is_some_and(|element| element.is_const()));

                let ty = match Item::from_ty(&ty, cursor, None, ctx) {
                    Ok(ty) => ty,
                    Err(e) => {
                        assert!(
                            matches!(ty.kind(), CXType_Auto | CXType_Unexposed),
                            "Couldn't resolve constant type, and it \
                             wasn't an nondeductible auto type or unexposed \
                             type: {ty:?}"
                        );
                        return Err(e);
                    }
                };

                // Note: Ty might not be totally resolved yet, see
                // tests/headers/inner_const.hpp
                //
                // That's fine because in that case we know it's not a literal.
                let canonical_ty = ctx
                    .safe_resolve_type(ty)
                    .and_then(|t| t.safe_canonical_type(ctx));

                let is_integer = canonical_ty.is_some_and(|t| t.is_integer());
                let is_float = canonical_ty.is_some_and(|t| t.is_float());

                // TODO: We could handle `char` more gracefully.
                // TODO: Strings, though the lookup is a bit more hard (we need
                // to look at the canonical type of the pointee too, and check
                // is char, u8, or i8 I guess).
                let value = if is_integer {
                    let TypeKind::Int(kind) = *canonical_ty.unwrap().kind()
                    else {
                        unreachable!()
                    };

                    let mut val = cursor.evaluate().and_then(|v| v.as_int());
                    if val.is_none() || !kind.signedness_matches(val.unwrap()) {
                        val = get_integer_literal_from_cursor(&cursor);
                    }

                    val.map(|val| {
                        if kind == IntKind::Bool {
                            VarType::Bool(val != 0)
                        } else {
                            VarType::Int(val)
                        }
                    })
                } else if is_float {
                    cursor
                        .evaluate()
                        .and_then(|v| v.as_double())
                        .map(VarType::Float)
                } else {
                    cursor
                        .evaluate()
                        .and_then(|v| v.as_literal_string())
                        .map(VarType::String)
                };

                let mangling = cursor_mangling(ctx, &cursor);
                let var =
                    Var::new(name, mangling, link_name, ty, value, is_const);

                Ok(ParseResult::New(var, Some(cursor)))
            }
            _ => {
                /* TODO */
                Err(ParseError::Continue)
            }
        }
    }
}

/// This function uses a [`FallbackTranslationUnit`][clang::FallbackTranslationUnit] to parse each
/// macro that cannot be parsed by the normal bindgen process for `#define`s.
///
/// To construct the [`FallbackTranslationUnit`][clang::FallbackTranslationUnit], first precompiled
/// headers are generated for all input headers. An empty temporary `.c` file is generated to pass
/// to the translation unit. On the evaluation of each macro, a [`String`] is generated with the
/// new contents of the empty file and passed in for reparsing. The precompiled headers and
/// preservation of the [`FallbackTranslationUnit`][clang::FallbackTranslationUnit] across macro
/// evaluations are both optimizations that have significantly improved the performance.
fn parse_macro_clang_fallback(
    ctx: &mut BindgenContext,
    cursor: &clang::Cursor,
) -> Option<(Vec<u8>, cexpr::expr::EvalResult)> {
    if !ctx.options().clang_macro_fallback {
        return None;
    }

    let ftu = ctx.try_ensure_fallback_translation_unit()?;
    let contents = format!("int main() {{ {}; }}", cursor.spelling());
    ftu.reparse(&contents).ok()?;
    // Children of root node of AST
    let root_children = ftu.translation_unit().cursor().collect_children();
    // Last child in root is function declaration
    // Should be FunctionDecl
    let main_func = root_children.last()?;
    // Children should all be statements in function declaration
    let all_stmts = main_func.collect_children();
    // First child in all_stmts should be the statement containing the macro to evaluate
    // Should be CompoundStmt
    let macro_stmt = all_stmts.first()?;
    // Children should all be expressions from the compound statement
    let paren_exprs = macro_stmt.collect_children();
    // First child in all_exprs is the expression utilizing the given macro to be evaluated
    // Should  be ParenExpr
    let paren = paren_exprs.first()?;

    Some((
        cursor.spelling().into_bytes(),
        cexpr::expr::EvalResult::Int(Wrapping(paren.evaluate()?.as_int()?)),
    ))
}

/// Try and parse a macro using all the macros parsed until now.
fn parse_macro(
    ctx: &mut BindgenContext,
    cursor: &clang::Cursor,
) -> Option<(Vec<u8>, cexpr::expr::EvalResult)> {
    use cexpr::expr;

    let mut cexpr_tokens = cursor.cexpr_tokens();

    for callbacks in &ctx.options().parse_callbacks {
        callbacks.modify_macro(&cursor.spelling(), &mut cexpr_tokens);
    }

    let parser = expr::IdentifierParser::new(ctx.parsed_macros());

    match parser.macro_definition(&cexpr_tokens) {
        Ok((_, (id, val))) => Some((id.into(), val)),
        _ => parse_macro_clang_fallback(ctx, cursor),
    }
}

fn parse_int_literal_tokens(cursor: &clang::Cursor) -> Option<i64> {
    use cexpr::expr;
    use cexpr::expr::EvalResult;

    let cexpr_tokens = cursor.cexpr_tokens();

    // TODO(emilio): We can try to parse other kinds of literals.
    match expr::expr(&cexpr_tokens) {
        Ok((_, EvalResult::Int(Wrapping(val)))) => Some(val),
        _ => None,
    }
}

fn get_integer_literal_from_cursor(cursor: &clang::Cursor) -> Option<i64> {
    use clang_sys::*;
    let mut value = None;
    cursor.visit(|c| {
        match c.kind() {
            CXCursor_IntegerLiteral | CXCursor_UnaryOperator => {
                value = parse_int_literal_tokens(&c);
            }
            CXCursor_UnexposedExpr => {
                value = get_integer_literal_from_cursor(&c);
            }
            _ => (),
        }
        if value.is_some() {
            CXChildVisit_Break
        } else {
            CXChildVisit_Continue
        }
    });
    value
}

fn duplicated_macro_diagnostic(
    macro_name: &str,
    _location: clang::SourceLocation,
    _ctx: &BindgenContext,
) {
    warn!("Duplicated macro definition: {macro_name}");

    #[cfg(feature = "experimental")]
    // FIXME (pvdrz & amanjeev): This diagnostic message shows way too often to be actually
    // useful. We have to change the logic where this function is called to be able to emit this
    // message only when the duplication is an actual issue.
    //
    // If I understood correctly, `bindgen` ignores all `#undef` directives. Meaning that this:
    // ```c
    // #define FOO 1
    // #undef FOO
    // #define FOO 2
    // ```
    //
    // Will trigger this message even though there's nothing wrong with it.
    #[allow(clippy::overly_complex_bool_expr)]
    if false && _ctx.options().emit_diagnostics {
        use crate::diagnostics::{get_line, Diagnostic, Level, Slice};
        use std::borrow::Cow;

        let mut slice = Slice::default();
        let mut source = Cow::from(macro_name);

        let (file, line, col, _) = _location.location();
        if let Some(filename) = file.name() {
            if let Ok(Some(code)) = get_line(&filename, line) {
                source = code.into();
            }
            slice.with_location(filename, line, col);
        }

        slice.with_source(source);

        Diagnostic::default()
            .with_title("Duplicated macro definition.", Level::Warning)
            .add_slice(slice)
            .add_annotation("This macro had a duplicate.", Level::Note)
            .display();
    }
}

Messung V0.5 in Prozent
C=74 H=93 G=83

¤ Dauer der Verarbeitung: 0.7 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Statistik
#Sources=434850
#Domains=661743