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


Quelle  clang.rs   Sprache: unbekannt

 
// (C) Copyright 2016 Jethro G. Beekman
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
extern crate cexpr;
extern crate clang_sys;

use std::collections::HashMap;
use std::io::Write;
use std::str::{self, FromStr};
use std::{char, ffi, mem, ptr, slice};

use cexpr::assert_full_parse;
use cexpr::expr::{fn_macro_declaration, EvalResult, IdentifierParser};
use cexpr::literal::CChar;
use cexpr::token::Token;
use clang_sys::*;

// main testing routine
fn test_definition(
    ident: Vec<u8>,
    tokens: &[Token],
    idents: &mut HashMap<Vec<u8>, EvalResult>,
) -> bool {
    fn bytes_to_int(value: &[u8]) -> Option<EvalResult> {
        str::from_utf8(value)
            .ok()
            .map(|s| s.replace("n", "-"))
            .map(|s| s.replace("_", ""))
            .and_then(|v| i64::from_str(&v).ok())
            .map(::std::num::Wrapping)
            .map(Int)
    }

    use cexpr::expr::EvalResult::*;

    let display_name = String::from_utf8_lossy(&ident).into_owned();

    let functional;
    let test = {
        // Split name such as Str_test_string into (Str,test_string)
        let pos = ident
            .iter()
            .position(|c| *c == b'_')
            .expect(&format!("Invalid definition in testcase: {}", display_name));
        let mut expected = &ident[..pos];
        let mut value = &ident[(pos + 1)..];

        functional = expected == b"Fn";

        if functional {
            let ident = value;
            let pos = ident
                .iter()
                .position(|c| *c == b'_')
                .expect(&format!("Invalid definition in testcase: {}", display_name));
            expected = &ident[..pos];
            value = &ident[(pos + 1)..];
        }

        if expected == b"Str" {
            let mut splits = value.split(|c| *c == b'U');
            let mut s = Vec::with_capacity(value.len());
            s.extend_from_slice(splits.next().unwrap());
            for split in splits {
                let (chr, rest) = split.split_at(6);
                let chr = u32::from_str_radix(str::from_utf8(chr).unwrap(), 16).unwrap();
                write!(s, "{}", char::from_u32(chr).unwrap()).unwrap();
                s.extend_from_slice(rest);
            }
            Some(Str(s))
        } else if expected == b"Int" {
            bytes_to_int(value)
        } else if expected == b"Float" {
            str::from_utf8(value)
                .ok()
                .map(|s| s.replace("n", "-").replace("p", "."))
                .and_then(|v| f64::from_str(&v).ok())
                .map(Float)
        } else if expected == b"CharRaw" {
            str::from_utf8(value)
                .ok()
                .and_then(|v| u64::from_str(v).ok())
                .map(CChar::Raw)
                .map(Char)
        } else if expected == b"CharChar" {
            str::from_utf8(value)
                .ok()
                .and_then(|v| u32::from_str(v).ok())
                .and_then(char::from_u32)
                .map(CChar::Char)
                .map(Char)
        } else {
            Some(Invalid)
        }
        .expect(&format!("Invalid definition in testcase: {}", display_name))
    };

    let result = if functional {
        let mut fnidents;
        let expr_tokens;
        match fn_macro_declaration(&tokens) {
            Ok((rest, (_, args))) => {
                fnidents = idents.clone();
                expr_tokens = rest;
                for arg in args {
                    let val = match test {
                        Int(_) => bytes_to_int(&arg),
                        Str(_) => Some(Str(arg.to_owned())),
                        _ => unimplemented!(),
                    }
                    .expect(&format!(
                        "Invalid argument in functional macro testcase: {}",
                        display_name
                    ));
                    fnidents.insert(arg.to_owned(), val);
                }
            }
            e => {
                println!(
                    "Failed test for {}, unable to parse functional macro declaration: {:?}",
                    display_name, e
                );
                return false;
            }
        }
        assert_full_parse(IdentifierParser::new(&fnidents).expr(&expr_tokens))
    } else {
        IdentifierParser::new(idents)
            .macro_definition(&tokens)
            .map(|(i, (_, val))| (i, val))
    };

    match result {
        Ok((_, val)) => {
            if val == test {
                if let Some(_) = idents.insert(ident, val) {
                    panic!("Duplicate definition for testcase: {}", display_name);
                }
                true
            } else {
                println!(
                    "Failed test for {}, expected {:?}, got {:?}",
                    display_name, test, val
                );
                false
            }
        }
        e => {
            if test == Invalid {
                true
            } else {
                println!(
                    "Failed test for {}, expected {:?}, got {:?}",
                    display_name, test, e
                );
                false
            }
        }
    }
}

// support code for the clang lexer
unsafe fn clang_str_to_vec(s: CXString) -> Vec<u8> {
    let vec = ffi::CStr::from_ptr(clang_getCString(s))
        .to_bytes()
        .to_owned();
    clang_disposeString(s);
    vec
}

#[allow(non_upper_case_globals)]
unsafe fn token_clang_to_cexpr(tu: CXTranslationUnit, orig: &CXToken) -> Token {
    Token {
        kind: match clang_getTokenKind(*orig) {
            CXToken_Comment => cexpr::token::Kind::Comment,
            CXToken_Identifier => cexpr::token::Kind::Identifier,
            CXToken_Keyword => cexpr::token::Kind::Keyword,
            CXToken_Literal => cexpr::token::Kind::Literal,
            CXToken_Punctuation => cexpr::token::Kind::Punctuation,
            _ => panic!("invalid token kind: {:?}", *orig),
        },
        raw: clang_str_to_vec(clang_getTokenSpelling(tu, *orig)).into_boxed_slice(),
    }
}

extern "C" fn visit_children_thunk<F>(
    cur: CXCursor,
    parent: CXCursor,
    closure: CXClientData,
) -> CXChildVisitResult
where
    F: FnMut(CXCursor, CXCursor) -> CXChildVisitResult,
{
    unsafe { (&mut *(closure as *mut F))(cur, parent) }
}

unsafe fn visit_children<F>(cursor: CXCursor, mut f: F)
where
    F: FnMut(CXCursor, CXCursor) -> CXChildVisitResult,
{
    clang_visitChildren(
        cursor,
        visit_children_thunk::<F> as _,
        &mut f as *mut F as CXClientData,
    );
}

unsafe fn location_in_scope(r: CXSourceRange) -> bool {
    let start = clang_getRangeStart(r);
    let mut file = ptr::null_mut();
    clang_getSpellingLocation(
        start,
        &mut file,
        ptr::null_mut(),
        ptr::null_mut(),
        ptr::null_mut(),
    );
    clang_Location_isFromMainFile(start) != 0
        && clang_Location_isInSystemHeader(start) == 0
        && file != ptr::null_mut()
}

/// tokenize_range_adjust can be used to work around LLVM bug 9069
/// https://bugs.llvm.org//show_bug.cgi?id=9069
fn file_visit_macros<F: FnMut(Vec<u8>, Vec<Token>)>(
    file: &str,
    tokenize_range_adjust: bool,
    mut visitor: F,
) {
    unsafe {
        let tu = {
            let index = clang_createIndex(true as _, false as _);
            let cfile = ffi::CString::new(file).unwrap();
            let mut tu = mem::MaybeUninit::uninit();
            assert!(
                clang_parseTranslationUnit2(
                    index,
                    cfile.as_ptr(),
                    [b"-std=c11\0".as_ptr() as *const ::std::os::raw::c_char].as_ptr(),
                    1,
                    ptr::null_mut(),
                    0,
                    CXTranslationUnit_DetailedPreprocessingRecord,
                    &mut *tu.as_mut_ptr()
                ) == CXError_Success,
                "Failure reading test case {}",
                file
            );
            tu.assume_init()
        };
        visit_children(clang_getTranslationUnitCursor(tu), |cur, _parent| {
            if cur.kind == CXCursor_MacroDefinition {
                let mut range = clang_getCursorExtent(cur);
                if !location_in_scope(range) {
                    return CXChildVisit_Continue;
                }
                range.end_int_data -= if tokenize_range_adjust { 1 } else { 0 };
                let mut token_ptr = ptr::null_mut();
                let mut num = 0;
                clang_tokenize(tu, range, &mut token_ptr, &mut num);
                if token_ptr != ptr::null_mut() {
                    let tokens = slice::from_raw_parts(token_ptr, num as usize);
                    let tokens: Vec<_> = tokens
                        .iter()
                        .filter_map(|t| {
                            if clang_getTokenKind(*t) != CXToken_Comment {
                                Some(token_clang_to_cexpr(tu, t))
                            } else {
                                None
                            }
                        })
                        .collect();
                    clang_disposeTokens(tu, token_ptr, num);
                    visitor(clang_str_to_vec(clang_getCursorSpelling(cur)), tokens)
                }
            }
            CXChildVisit_Continue
        });
        clang_disposeTranslationUnit(tu);
    };
}

fn test_file(file: &str) -> bool {
    let mut idents = HashMap::new();
    let mut all_succeeded = true;
    file_visit_macros(file, fix_bug_9069(), |ident, tokens| {
        all_succeeded &= test_definition(ident, &tokens, &mut idents)
    });
    all_succeeded
}

fn fix_bug_9069() -> bool {
    fn check_bug_9069() -> bool {
        let mut token_sets = vec![];
        file_visit_macros(
            "tests/input/test_llvm_bug_9069.h",
            false,
            |ident, tokens| {
                assert_eq!(&ident, b"A");
                token_sets.push(tokens);
            },
        );
        assert_eq!(token_sets.len(), 2);
        token_sets[0] != token_sets[1]
    }

    use std::sync::atomic::{AtomicBool, Ordering};
    use std::sync::Once;

    static CHECK_FIX: Once = Once::new();
    static FIX: AtomicBool = AtomicBool::new(false);

    CHECK_FIX.call_once(|| FIX.store(check_bug_9069(), Ordering::SeqCst));

    FIX.load(Ordering::SeqCst)
}

macro_rules! test_file {
    ($f:ident) => {
        #[test]
        fn $f() {
            assert!(
                test_file(concat!("tests/input/", stringify!($f), ".h")),
                "test_file"
            )
        }
    };
}

test_file!(floats);
test_file!(chars);
test_file!(strings);
test_file!(int_signed);
test_file!(int_unsigned);
test_file!(fail);

[ Dauer der Verarbeitung: 0.3 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge