Использование библиотеки анализакода OpenC++: модификация,улучшение, исправление ошибокАвтор: Андрей КарповДата: 12.01.200...
исполнения OpenTS [7] для языка программирования T++ (разработка Института программныхсистем РАН), статический анализатор ...
...};Модернизируйте таблицу "table" в файле lex.cc:static rw_table table[] = {      ...      { "__w64",            W64 }, ...
Обратите внимание, что поскольку мы решили отнести "__w64" к типам данных, то нампонадобился символ W для кодирования этог...
Вначале добавим новые лексемы, как было рассказано ранее, но теперь для этой целивоспользуемся функцией InitializeOtherKey...
4. Функция раскрытия полных путей к файламВ задачах анализа исходного кода большое количество функциональности связано с с...
fileDirectory = curDir;        } else {            assert(false);        }    }    algorithm::replace_all(fileDirectory, "...
if (fileName[0] == . && fileName[1] != .) {        const string &dir = GetInputFileDirectory();        if (!dir.empty())  ...
GetTypeBySufix( *from, size_t len) {    assert(from != NULL);    (len== 0)      ST_INT;   assert(!isdigit(*from));     suf...
return c - 0;    if (c >= a && c <= f)        return c - a + 0x0a;    if (c >= A && c <= F)        return c - A + 0x0a;   ...
}bool GetOct(const char *&from, size_t len,                  unsigned __int64 &retValue) {    unsigned __int64 c, n = 0;  ...
switch (c){    /* UCNs, hex escapes, and octal escapes      are processed separately.      */case u: case U:    // convert...
return false;    }    retValue = c;    return true;}//A, t, LA, xFEstatic bool GetCharLiteral(const char *from,           ...
return true;        }    }    return false;}// "string"static bool GetStringLiteral(const char *from, size_t len) {    if ...
case F: isReal = true; break;            case .: isReal = true; break;            case e: isReal = true; break;           ...
if (len == 4 && strncmp(from, "true", 4) == 0) {        retValue = 1;        return true;    }    if (len == 5 && strncmp(...
bool suffix_u = false;while (len != 0) { --len; const char c = *from++; switch(c) {   case 8: suffix_8 = true; break;   ca...
case I:        case i: suffix_i = true; break;        case U:        case u: suffix_u = true; break;        case L:       ...
return ST_ULONG;        else         return ST_LONG;    }    if (suffix_u)        return ST_UINT;    assert(suffix_i);    ...
const char *p = from + 1;    if (!GetOct(p, len, retValue)) {        return ST_UNKNOWN;    }    ptrdiff_t newLen = len - (...
return ST_UNKNOWN;    retValue = 1;    if (from == NULL || len == 0)     return ST_UNKNOWN;    if (GetCharLiteral(from, le...
for(;;){   c = file->Get();   if(c == ){       c = file->Get();       // Support: ""       if (c == r) {           c = fil...
if(c == ")                        /* line_number += nline; */ ;                    else{                        token_len ...
static bool isOperatorInTemplateArg(ptrdiff_t t) {    return t == AssignOp || t == EqualOp || t == LogOrOp ||          t =...
template <int z>unsigned TFoo(unsigned a) {return a + z;}enum EEnum { EE1, EE2 };b = TFoo < EE1 && EE2 > (2);*/ptrdiff_t n...
}               else if(u == 0 || u == ; || u == })                    return false;          }          t = lex->LookAhea...
body = 0;    while(lex->LookAhead(0) != }){         if(!rDefinition(def)){             if(!SyntaxError())                 ...
if (lex->CanLookAhead(0)) {         lex->LookAhead(0, t);     } else {         lex->LookAhead(-1, t);     }     if (lex->C...
kind = tdk_instantiation;        return true;    // ignore TEMPLATE    }    decl = new (GC)        PtreeTemplateDecl(new (...
if (args == 0)             // template < > declaration             kind = tdk_specialization;        else             // t...
}11. Поддержка объявлений вида const A (a)Библиотека OpenС++ не поддерживает объявление переменных вида "const A (a)". Для...
}    }}В приведенном коде используются некоторые вспомогательные функции, отсутствующие в этойстатье. Но вы можете найти и...
}13. Обработка конструкций "using" и "namespace" внутри функцийБиблиотека OpenC++ не "знает", что внутри функций можно исп...
15. Оптимизация функции LineNumber()Мы уже упоминали о функции Program::LineNumber(), говоря, что она возвращает имена фай...
while(pos > 0){ if (pos == oldLineNumberPos) {     line_number = oldLineNumber + nline;     assert(!oldFileName.empty()); ...
}             }             if(line_number >= 0 && filename_length > 0) {                 oldLineNumberPos = pos;         ...
16. Исправление ошибки при анализе директивы "#line"В некоторых случаях функция Program::ReadLineDirective() дает сбой, пр...
}          /* line_numberll be incremented soon */          line_number = num - 1;          if(is_blank(c)){              ...
Библиографический список  1. Зуев Е.А. Редкая профессия. PC Magazine/Russian Edition. Спецвыпуск N 5(75), 1997.      http:...
Upcoming SlideShare
Loading in …5
×

Использование библиотеки анализа кода OpenC++: модификация, улучшение, исправление ошибок

410 views

Published on

Данная статья представляет интерес для разработчиков, использующих или планирующих использовать библиотеку OpenC++ (OpenCxx). Автор рассказывает о своем опыте улучшения библиотеки OpenC++ и модификации библиотеки для решения специализированных задач.

Published in: Technology, Business
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
410
On SlideShare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
4
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Использование библиотеки анализа кода OpenC++: модификация, улучшение, исправление ошибок

  1. 1. Использование библиотеки анализакода OpenC++: модификация,улучшение, исправление ошибокАвтор: Андрей КарповДата: 12.01.2008АннотацияДанная статья представляет интерес для разработчиков, использующих или планирующихиспользовать библиотеку OpenC++ (OpenCxx). Автор рассказывает о своем опыте улучшениябиблиотеки OpenC++ и модификации библиотеки для решения специализированных задач.ВведениеВ форумах часто можно услышать, что синтаксических анализаторов ("парсеров") языка Си++ вмире огромное количество. В том числе и бесплатных. Или, что можно взять, например, YACC илегко реализовать свой анализатор. Не верьте, все не так просто [1, 2]. Особенно, если вспомнить,что разобрать синтаксис - это меньше половины дела. Необходимо реализовать структуры дляхранения дерева программы и семантических таблиц, содержащих информацию о различныхобъектах и областях их действия. Особенно это важно при разработке специализированныхприложений, связанных с обработкой и статическим анализом Си++ кода. Для их реализациинеобходимо сохранение полного дерева программы, что могут предоставить не многиебиблиотеки. Одной из них является открытая библиотека OpenC++ (OpenCxx) [3], о которой мы ипоговорим в этой статье.Хочется помочь разработчикам в освоении библиотеки OpenC++ и поделиться опытом еемодернизации и использования некоторых недочетов. Статья представляет собой сборниксоветов, каждый из которых посвящен исправлению какого-то дефекта или реализацииусовершенствования.Статья основывается на воспоминаниях об изменениях, которые были осуществлены вбиблиотеке VivaCore [4], основанной на базе OpenC++. Конечно, здесь отражена только малаячасть этих изменений. Вспомнить и описать их все будет непростой задачей. Например, описаниедобавления в библиотеку OpenC++ поддержки языка Си займет много места. Но Вы всегда можетеобратиться к исходным текстам библиотеки VivaCore и получить много интересной информации.Последнее, о чем хочется сказать, что библиотека OpenC++, к сожалению, на данный моментустарела и нуждается в серьезной доработке для поддержания современного стандарта языкаСи++. Поэтому, если Вы, например, собираетесь реализовать современный компилятор, то Вамлучше обратить внимание на GCC или посмотреть в сторону коммерческих библиотек [5, 6]. НоOpenC++ и сейчас остается хорошим и удобным инструментом для многих разработчиков вобласти систем специализированной обработки и модификации программного кода. Cиспользованием OpenC++ разработаны многие интересные решения. Например: среда
  2. 2. исполнения OpenTS [7] для языка программирования T++ (разработка Института программныхсистем РАН), статический анализатор кода Viva64 [8] или инструмент Synopsis для подготовкидокументации по исходному коду [9].Цель данной статьи - показать на примерах, как можно модифицировать и улучшить кодбиблиотеки OpenC++. Для этого в статье описано 15 модификаций библиотеки, связанных сисправлением ошибок или добавлением новой функциональности. Все они не только позволяютсделать библиотеку OpenC++ лучше, но и дают возможность глубже изучить принципы ее работы.Давайте познакомимся с ними.1. Пропуск ключевых слов среды разработки, не влияющих наобработку программыРазрабатывая анализатор кода под конкретную среду разработки, Вы наверняка столкнетесь с ееспецифическими языковыми конструкциями. Часто эти конструкции являются указаниями дляконкретного компилятора и могут не представлять для Вас никакого практического интереса. Нотакие конструкции не могут быть обработаны библиотекой OpenC++, так как не являются частьюязыка Си++. В этом случае одним из простых способов игнорировать их является добавлениеключевых слов в таблицу rw_table table с ключом Ignore. Пример:static rw_table table[] = { ... { "__ptr32", Ignore}, { "__ptr64", Ignore}, { "__unaligned", Ignore}, ...};При добавлении следует учитывать, что слова в таблице rw_table table должны быть расположеныв алфавитном порядке. Будьте аккуратны!2. Добавление новой лексемыЕсли Вы хотите добавить ключевое слово, которое следует обрабатывать, то Вам необходимосоздать новую лексему ("токен"). Рассмотрим пример добавления ключевого слова "__w64". Вначале создайте идентификатор новой лексемы (смотрите файл token-name.h), например, так:enum { Identifier = 258, Constant = 262, ... W64 = 346, // New token name
  3. 3. ...};Модернизируйте таблицу "table" в файле lex.cc:static rw_table table[] = { ... { "__w64", W64 }, ...};Наш следующий шаг - это создание класса для новой лексемы, который мы назовем LeafW64:namespace Opencxx{class LeafW64 : public LeafReserved {public: LeafW64(Token& t) : LeafReserved(t) {} LeafW64(char* str, ptrdiff_t len) : LeafReserved(str, len) {} ptrdiff_t What() { return W64; }};}Для создания объекта нам понадобится модифицировать функцию optIntegralTypeOrClassSpec():...case UNSIGNED : flag = U; kw = new (GC) LeafUNSIGNED(tk); break;case W64 : // NEW! flag = W; kw = new (GC) LeafW64(tk); break;...
  4. 4. Обратите внимание, что поскольку мы решили отнести "__w64" к типам данных, то нампонадобился символ W для кодирования этого типа. Более подробно с механизмомкодирования типов можно познакомиться в файле Encoding.cc.Вводя новый тип, мы должны помнить о необходимости модернизации таких функций, какнапример Parser::isTypeSpecifier().И, наконец, последний важный момент - это модификация функции Encoding::MakePtree:Ptree* Encoding::MakePtree(unsigned char*& encoded, Ptree* decl){ ... case W : typespec = PtreeUtil::Snoc(typespec, w64_t); break; ...}Естественно, это только пример, и добавление других лексем может потребовать гораздо большеработы. Хороший способ корректно добавить новую лексему - взять близкую к ней по смыслу, азатем найти и изучить все места в библиотеке OpenC++ где она используется.3. Пропуск сложных ключевых конструкций среды разработки, невлияющих на обработку программыМы уже рассмотрели способ, как пропустить единичные ключевые слова, не имеющие для нашейпрограммы смысловой нагрузки, но которые мешают разбору кода. К сожалению, иногда делообстоит сложнее. Возьмем в качестве демонстрации такие конструкции как __pragma и __noop,которые можно встретить в заголовочных файлах VisualC++:__forceinline DWORD HEAP_MAKE_TAG_FLAGS ( DWORD TagBase, DWORD Tag ){ __pragma(warning(push)) __pragma(warning(disable : 4548)) do{__noop(TagBase);} while((0,0) __pragma(warning(pop)) ); return ((DWORD)((TagBase) + ((Tag) << 18)));}Посмотреть описание конструкций __pragma и __noop можно в MSDN. Для нашей программыважно следующее: а) они нам не интересны; б) имеют параметры; в) мешают анализу кода.
  5. 5. Вначале добавим новые лексемы, как было рассказано ранее, но теперь для этой целивоспользуемся функцией InitializeOtherKeywords():static void InitializeOtherKeywords(bool recognizeOccExtensions){ ... verify(Lex::RecordKeyword("__pragma", MSPRAGMA)); verify(Lex::RecordKeyword("__noop", MS__NOOP)); ...}Решение заключается в модификации функции Lex::ReadToken таким образом, что, когда мывстречаем лексему DECLSPEC или MSPRAGMA, мы пропускаем ее. А затем пропускаем вселексемы, относящиеся к параметрам __pragma и __noop. Для пропуска всех лишних лексем мыиспользуем функцию SkipDeclspecToken() таким образом, как показано далее.ptrdiff_t Lex::ReadToken(char*& ptr, ptrdiff_t& len){ ... else if(t == DECLSPEC){ SkipDeclspecToken(); continue; } else if(t == MSPRAGMA) { // NEW SkipDeclspecToken(); continue; } else if(t == MS__NOOP) { //NEW SkipDeclspecToken(); continue; } ...}
  6. 6. 4. Функция раскрытия полных путей к файламВ задачах анализа исходного кода большое количество функциональности связано с созданиемдиагностических сообщений, а также навигацией по исходным файлам. Неудобство заключается втом, что имена файлов, возвращаемые такими функциями как Program::LineNumber(), могут бытьпредставлены в разнообразном виде. Вот несколько примеров:C:Program FilesMSVS 8VCatlmfcincludeafx.h.drawing.cppc:srcwxwindows-2.4.2samplesdrawingwx/defs.hBoostboost-1_33_1boost/variant/recursive_variant.hpp..FieldEdit2SrcamsEdit.cpp......srcbaseftbase.cПуть может быть полным или относительным. Могут использоваться различные разделители. Всеэто делает использование таких путей неудобным для обработки или для вывода винформационных сообщениях. Поэтому мы предлагаем реализацию функции FixFileName(),приводящую пути к единообразному полному виду. Используемая вспомогательная функцияGetInputFileDirectory() должна возвращать путь к каталогу, в котором находится обрабатываемыйфайл:const string &GetInputFileDirectory() { static string oldInputFileName; static string fileDirectory; string dir; VivaConfiguration &cfg = VivaConfiguration::Instance(); string inputFileName; cfg.GetInputFileName(inputFileName); if (oldInputFileName == inputFileName) return fileDirectory; oldInputFileName = inputFileName; filesystem::path inputFileNamePath(inputFileName,filesystem::native); fileDirectory = inputFileNamePath.branch_path().string(); if (fileDirectory.empty()) { TCHAR curDir[MAX_PATH]; if (GetCurrentDirectory(MAX_PATH, curDir) != 0) {
  7. 7. fileDirectory = curDir; } else { assert(false); } } algorithm::replace_all(fileDirectory, "/", ""); to_lower(fileDirectory); return fileDirectory;}typedef map<string, string> StrStrMap;typedef StrStrMap::iterator StrStrMapIt;void FixFileName(string &fileName) { static StrStrMap FileNamesMap; StrStrMapIt it = FileNamesMap.find(fileName); if (it != FileNamesMap.end()) { fileName = it->second; return; } string oldFileName = fileName; algorithm::replace_all(fileName, "/", ""); algorithm::replace_all(fileName, "", ""); filesystem::path tmpPath(fileName, filesystem::native); fileName = tmpPath.string(); algorithm::replace_all(fileName, "/", ""); to_lower(fileName); if (fileName.length() < 2) { assert(false); FileNamesMap.insert(make_pair(oldFileName, fileName)); return; }
  8. 8. if (fileName[0] == . && fileName[1] != .) { const string &dir = GetInputFileDirectory(); if (!dir.empty()) fileName.replace(0, 1, dir); FileNamesMap.insert(make_pair(oldFileName, fileName)); return; } if (isalpha(fileName[0]) && fileName[1] == : ) { FileNamesMap.insert(make_pair(oldFileName, fileName)); return; } const string &dir = GetInputFileDirectory(); if (dir.empty()) fileName.insert(0, "."); else { fileName.insert(0, ""); fileName.insert(0, dir); } FileNamesMap.insert(make_pair(oldFileName, fileName));}5. Получение значения числовых литераловВ системах построения документации по коду полезной может оказаться функция получениязначения числового литерала. Например, с ее помощью можно узнать и использовать тот факт,что аргумент функции "void foo(a = 99)" равен 99.Предлагаемая функция GetLiteralType() позволяет получить тип литерала и его значение, если онцелочисленный. Функция GetLiteralType() создана для получения наиболее часто необходимойинформации и не поддерживает редко используемые виды записи. Но если Вам, например, будетнеобходимо поддержать UCNs или получать значения типа double, то вы можете самостоятельнорасширить функциональность приведенных ниже функций:", 5) == 0) { retValue = 0; ; } ; } IsHexLiteral(*from, size_t len) { (len < 3) ; (from[0] != 0) ;(from[1] != x && from[1] != X) ; ; } SimpleType
  9. 9. GetTypeBySufix( *from, size_t len) { assert(from != NULL); (len== 0) ST_INT; assert(!isdigit(*from)); suffix_8 = ;suffix_16 = ; suffix_32 = ; suffix_64 = ; suffix_i = ;suffix_l = ; suffix_u = ; (len != 0) { --len; c =*from++; (c) { 8: suffix_8 = ; ; 1:(len == 0 || *from++ != 6) { assert();ST_UNKNOWN; } --len; suffix_16 = ; ;3: (len == 0 || *from++ != 2) { assert();ST_UNKNOWN; } --len; suffix_32 = ; ;6: (len == 0 || *from++ != 4) { assert();ST_UNKNOWN; } --len; suffix_64 = ; ;I: i: suffix_i = ; ; U: u: suffix_u = ; ;L: l: suffix_l = ; ; : assert();ST_UNKNOWN; } } assert(suffix_8 + suffix_16 + suffix_32 +suffix_64 <= 1); (suffix_8 || suffix_16) ST_LESS_INT;(suffix_32) { (suffix_u) ST_UINT; ST_INT; }(suffix_64) { (suffix_u) ST_UINT64; ST_INT64;} (suffix_l) { (suffix_u) ST_ULONG;ST_LONG; } (suffix_u) ST_UINT; assert(suffix_i);ST_INT; } SimpleType GetHexLiteral( *from, size_t len,&retValue) { assert(len >= 3); *p = from + 2; (!GetHex(p,len, retValue)) { ST_UNKNOWN; } ptrdiff_t newLen = len - (p -from); assert(newLen >= 0 && newLen < <ptrdiff_t>(len));GetTypeBySufix(p, newLen); } IsOctLiteral( *from, size_t len) {(len < 2) ; (from[0] != 0) ; ; } SimpleTypeGetOctLiteral( *from, size_t len,&retValue) { assert(len >= 2); *p = from + 1; (!GetOct(p,len, retValue)) { ST_UNKNOWN; } ptrdiff_t newLen = len - (p -from); assert(newLen >= 0 && newLen < <ptrdiff_t>(len));GetTypeBySufix(p, newLen); } SimpleType GetDecLiteral( *from, size_tlen, &retValue) { assert(len >= 1);*limit = from + len; n = 0; (from < limit) { c = *from;(c < 0 || c > 9) ; from++; n = n * 10 + (c - 0);} ptrdiff_t newLen = limit - from; (newLen == <ptrdiff_t>(len))ST_UNKNOWN; retValue = n; assert(newLen >= 0 && newLen <<ptrdiff_t>(len)); GetTypeBySufix(from, newLen); } SimpleTypeGetLiteralType( *from, size_t len,&retValue) { (from == NULL || len == 0) ST_UNKNOWN; retValue= 1; (from == NULL || len == 0) ST_UNKNOWN;(GetCharLiteral(from, len, retValue)) ST_LESS_INT;(GetStringLiteral(from, len)) ST_POINTER;(GetBoolLiteral(from, len, retValue)) ST_LESS_INT;(IsRealLiteral(from, len)) GetRealLiteral(from, len);(IsHexLiteral(from, len)) GetHexLiteral(from, len, retValue);(IsOctLiteral(from, len)) GetOctLiteral(from, len, retValue);GetDecLiteral(from, len, retValue); }unsigned __int64 GetHexValue(unsigned char c) { if (c >= 0 && c <= 9)
  10. 10. return c - 0; if (c >= a && c <= f) return c - a + 0x0a; if (c >= A && c <= F) return c - A + 0x0a; assert(false); return 0;}bool GetHex(const char *&from, size_t len, unsigned __int64 &retValue) { unsigned __int64 c, n = 0, overflow = 0; int digits_found = 0; const char *limit = from + len; while (from < limit) { c = *from; if (!isxdigit(c)) break; from++; overflow |= n ^ (n << 4 >> 4); n = (n << 4) + GetHexValue(c); digits_found = 1; } if (!digits_found) return false; if (overflow) { assert(false); } retValue = n; return true;
  11. 11. }bool GetOct(const char *&from, size_t len, unsigned __int64 &retValue) { unsigned __int64 c, n = 0; bool overflow = false; const char *limit = from + len; while (from < limit) { c = *from; if (c < 0 || c > 7) break; from++; overflow |= static_cast<bool>(n ^ (n << 3 >> 3)); n = (n << 3) + c - 0; } retValue = n; return true;}#define HOST_CHARSET_ASCIIbool GetEscape(const char *from, size_t len, unsigned __int64 &retValue) { /* Values of a b e f n r t v respectively. */ // HOST_CHARSET_ASCII static const char charconsts[] = { 7, 8, 27, 12, 10, 13, 9, 11 }; // HOST_CHARSET_EBCDIC //static const uchar charconsts[] = { 47, 22, 39, 12, 21, 13, 5, 11 }; unsigned char c; c = from[0];
  12. 12. switch (c){ /* UCNs, hex escapes, and octal escapes are processed separately. */case u: case U: // convert_ucn - not supported. Return: 65535. retValue = 0xFFFFui64; return true;case x: { const char *p = from + 1; return GetHex(p, len, retValue);}case 0: case 1: case 2: case 3:case 4: case 5: case 6: case 7: { const char *p = from + 1; return GetOct(p, len, retValue);}case : case : case ": case ?: break;case a: c = charconsts[0]; break;case b: c = charconsts[1]; break;case f: c = charconsts[3]; break;case n: c = charconsts[4]; break;case r: c = charconsts[5]; break;case t: c = charconsts[6]; break;case v: c = charconsts[7]; break;case e: case E: c = charconsts[2]; break;default: assert(false);
  13. 13. return false; } retValue = c; return true;}//A, t, LA, xFEstatic bool GetCharLiteral(const char *from, size_t len, unsigned __int64 &retValue) { if (len >= 3) { if (from[0] == && from[len - 1] == ) { unsigned char c = from[1]; if (c == ) { verify(GetEscape(from + 2, len - 3, retValue)); } else { retValue = c; } return true; } } if (len >= 4) { if (from[0] == L && from[1] == && from[len - 1] == ) { unsigned char c = from[2]; if (c == ) { verify(GetEscape(from + 3, len - 4, retValue)); } else { retValue = c; }
  14. 14. return true; } } return false;}// "string"static bool GetStringLiteral(const char *from, size_t len) { if (len >= 2) { if (from[0] == " && from[len - 1] == ") return true; } if (len >= 3) { if (from[0] == L && from[1] == " && from[len - 1] == ") return true; } return false;}bool IsRealLiteral(const char *from, size_t len) { if (len < 2) return false; bool isReal = false; bool digitFound = false; for (size_t i = 0; i != len; ++i) { unsigned char c = from[i]; switch(c) { case x: return false; case X: return false; case f: isReal = true; break;
  15. 15. case F: isReal = true; break; case .: isReal = true; break; case e: isReal = true; break; case E: isReal = true; break; case l: break; case -: break; case +: break; case L: break; default: if (!isdigit(c)) return false; digitFound = true; } } return isReal && digitFound;}SimpleType GetRealLiteral(const char *from, size_t len) { assert(len > 1); unsigned char rc1 = from[len - 1]; if (is_digit(rc1) || rc1 == . || rc1 == l || rc1 == L || rc1 == e || rc1 == E) return ST_DOUBLE; if (rc1 == f || rc1 == F) return ST_FLOAT; assert(false); return ST_UNKNOWN;}bool GetBoolLiteral(const char *from, size_t len, unsigned __int64 &retValue) {
  16. 16. if (len == 4 && strncmp(from, "true", 4) == 0) { retValue = 1; return true; } if (len == 5 && strncmp(from, "false", 5) == 0) { retValue = 0; return true; } return false;}bool IsHexLiteral(const char *from, size_t len) { if (len < 3) return false; if (from[0] != 0) return false; if (from[1] != x && from[1] != X) return false; return true;}SimpleType GetTypeBySufix(const char *from, size_t len) { assert(from != NULL); if (len == 0) return ST_INT; assert(!isdigit(*from)); bool suffix_8 = false; bool suffix_16 = false; bool suffix_32 = false; bool suffix_64 = false; bool suffix_i = false; bool suffix_l = false;
  17. 17. bool suffix_u = false;while (len != 0) { --len; const char c = *from++; switch(c) { case 8: suffix_8 = true; break; case 1: if (len == 0 || *from++ != 6) { assert(false); return ST_UNKNOWN; } --len; suffix_16 = true; break; case 3: if (len == 0 || *from++ != 2) { assert(false); return ST_UNKNOWN; } --len; suffix_32 = true; break; case 6: if (len == 0 || *from++ != 4) { assert(false); return ST_UNKNOWN; } --len; suffix_64 = true; break;
  18. 18. case I: case i: suffix_i = true; break; case U: case u: suffix_u = true; break; case L: case l: suffix_l = true; break; default: assert(false); return ST_UNKNOWN; }}assert(suffix_8 + suffix_16 + suffix_32 + suffix_64 <= 1);if (suffix_8 || suffix_16) return ST_LESS_INT;if (suffix_32) { if (suffix_u) return ST_UINT; else return ST_INT;}if (suffix_64) { if (suffix_u) return ST_UINT64; else return ST_INT64;}if (suffix_l) { if (suffix_u)
  19. 19. return ST_ULONG; else return ST_LONG; } if (suffix_u) return ST_UINT; assert(suffix_i); return ST_INT;}SimpleType GetHexLiteral(const char *from, size_t len, unsigned __int64 &retValue) { assert(len >= 3); const char *p = from + 2; if (!GetHex(p, len, retValue)) { return ST_UNKNOWN; } ptrdiff_t newLen = len - (p - from); assert(newLen >= 0 && newLen < static_cast<ptrdiff_t>(len)); return GetTypeBySufix(p, newLen);}bool IsOctLiteral(const char *from, size_t len) { if (len < 2) return false; if (from[0] != 0) return false; return true;}SimpleType GetOctLiteral(const char *from, size_t len, unsigned __int64 &retValue) { assert(len >= 2);
  20. 20. const char *p = from + 1; if (!GetOct(p, len, retValue)) { return ST_UNKNOWN; } ptrdiff_t newLen = len - (p - from); assert(newLen >= 0 && newLen < static_cast<ptrdiff_t>(len)); return GetTypeBySufix(p, newLen);}SimpleType GetDecLiteral(const char *from, size_t len, unsigned __int64 &retValue) { assert(len >= 1); const char *limit = from + len; unsigned __int64 n = 0; while (from < limit) { const char c = *from; if (c < 0 || c > 9) break; from++; n = n * 10 + (c - 0); } ptrdiff_t newLen = limit - from; if (newLen == static_cast<ptrdiff_t>(len)) return ST_UNKNOWN; retValue = n; assert(newLen >= 0 && newLen < static_cast<ptrdiff_t>(len)); return GetTypeBySufix(from, newLen);}SimpleType GetLiteralType(const char *from, size_t len, unsigned __int64 &retValue) { if (from == NULL || len == 0)
  21. 21. return ST_UNKNOWN; retValue = 1; if (from == NULL || len == 0) return ST_UNKNOWN; if (GetCharLiteral(from, len, retValue)) return ST_LESS_INT; if (GetStringLiteral(from, len)) return ST_POINTER; if (GetBoolLiteral(from, len, retValue)) return ST_LESS_INT; if (IsRealLiteral(from, len)) return GetRealLiteral(from, len); if (IsHexLiteral(from, len)) return GetHexLiteral(from, len, retValue); if (IsOctLiteral(from, len)) return GetOctLiteral(from, len, retValue); return GetDecLiteral(from, len, retValue);}6. Исправление функции обработки строковых литераловМы предлагаем модифицировать функцию Lex::ReadStrConst() так, как показано ниже. Этопозволит исправить две ошибки, связанные с обработкой разделенных строковых литералов.Первая ошибка возникает при обработке строк вида:const char *name = "VivaCore";Вторая - при обработке:const wchar_t *str = L"begin"L"end".Исправленный вариант функции:bool Lex::ReadStrConst(size_t top, bool isWcharStr){ char c;
  22. 22. for(;;){ c = file->Get(); if(c == ){ c = file->Get(); // Support: "" if (c == r) { c = file->Get(); if (c != n) return false; } else if(c == 0) return false; } else if(c == "</str>){ size_t pos = file->GetCurPos() + 1; ptrdiff_t nline = 0; do{ c = file->Get(); if(c == n) ++nline; } while(is_blank(c) || c == n); if (isWcharStr && c == L) { //Support: L"123" L"456" L "789". c = file->Get(); if(c == ") /* line_number += nline; */ ; else{ file->Unget(); return false; } } else {
  23. 23. if(c == ") /* line_number += nline; */ ; else{ token_len = ptrdiff_t(pos - top); file->Rewind(pos); return true; } } } else if(c == n || c == 0) return false; }}7. Частичное исправление обработки выражений вида "bool r = a < 1|| b > (int) 2;"В библиотеке OpenC++ существует ошибка, связанная с обработкой некоторых выражений,которые ошибочно воспринимаются как шаблоны. Например, в строке "bool r = a < 1 || b > (int) 2;"переменная "a" будет принята за имя шаблона, а дальше, дальше начнутся беды с синтаксическиманализом... Полноценное исправление данной ошибки требует существенных изменений и наданный момент не реализовано. Мы предлагаем промежуточное решение, исключающееосновную часть ошибок. Ниже приведены функции, которые нужно добавить илимодифицировать:bool VivaParser::MaybeTypeNameOrClassTemplate(Token &token) { if (m_env == NULL) { return true; } const char *ptr = token.GetPtr(); ptrdiff_t len = token.GetLen(); Bind *bind; bool isType = m_env->LookupType(ptr, len, bind); return isType;}
  24. 24. static bool isOperatorInTemplateArg(ptrdiff_t t) { return t == AssignOp || t == EqualOp || t == LogOrOp || t == LogAndOp || t == IncOp || t == RelOp;}/* template.args : < any* > template.args must be followed by ( or ::*/bool Parser::isTemplateArgs(){ ptrdiff_t i = 0; ptrdiff_t t = lex->LookAhead(i++); if(t == <){ ptrdiff_t n = 1; while(n > 0){ ptrdiff_t u = lex->LookAhead(i++); /* TODO. :( Fixing: bool r = a < 1 || b > (int) 2; Исправим не все случаи, но все равно станет лучше. Метод правки. Если нашли идентификатор рядом с оператором, то это явно не шаблон, так как внутри скобок может быть только тип или константное выражение. Пример, который все равно не работает: r = a < fooi() || 1 > (int) b; К сожалению, теперь неправильно обрабатывается приведенное ниже выражение, но таких случаев меньше, чем поправленных.
  25. 25. template <int z>unsigned TFoo(unsigned a) {return a + z;}enum EEnum { EE1, EE2 };b = TFoo < EE1 && EE2 > (2);*/ptrdiff_t next = lex->LookAhead(i);if (u == Identifier && isOperatorInTemplateArg(next)) return false;if (isOperatorInTemplateArg(u) && next == Identifier) return false;if(u == <) ++n;else if(u == >) --n;else if(u == (){ ptrdiff_t m = 1; while(m > 0){ ptrdiff_t v = lex->LookAhead(i++); if(v == () ++m; else if(v == )) --m; else if(v == 0 || v == ; || v == }) return false; }
  26. 26. } else if(u == 0 || u == ; || u == }) return false; } t = lex->LookAhead(i); return bool(t == Scope || t == (); } return false;}8. Улучшенная обработка ошибокК сожалению, реализованный в OpenC++ механизм обработки ошибок иногда приводит каварийному завершению программы. Проблемным местом в OpenC++ является код, аналогичныйэтому:if(!rDefinition(def)){ if(!SyntaxError()) return false; SkipTo(}); lex->GetToken(cp); // WARNING: crash in the same case. body = PtreeUtil::List(new Leaf(op), 0, new Leaf(cp)); return true;}Следует уделить внимание местам, где происходит обработка ошибок, и скорректировать, как этопоказано на примере функций Parser::rLinkageBody() и Parser::SyntaxError(). Общий смыслисправлений заключается в том, что после возникновения ошибки не следует сразу извлекатьследующую лексему, используя GetToken(), а нужно вначале проверить ее наличие, используяфункцию CanLookAhead():bool Parser::rLinkageBody(Ptree*& body){ Token op, cp; Ptree* def; if(lex->GetToken(op) != {) return false;
  27. 27. body = 0; while(lex->LookAhead(0) != }){ if(!rDefinition(def)){ if(!SyntaxError()) return false; // too many errors if (lex->CanLookAhead(1)) { SkipTo(}); lex->GetToken(cp); if (!lex->CanLookAhead(0)) return false; } else { return false; } body = PtreeUtil::List(new (GC) Leaf(op), 0, new (GC) Leaf(cp)); return true; // error recovery } body = PtreeUtil::Snoc(body, def); } lex->GetToken(cp); body = new (GC) PtreeBrace(new (GC) Leaf(op), body, new (GC) Leaf(cp)); return true;}bool Parser::SyntaxError(){ syntaxErrors_ = true; Token t, t2;
  28. 28. if (lex->CanLookAhead(0)) { lex->LookAhead(0, t); } else { lex->LookAhead(-1, t); } if (lex->CanLookAhead(1)) { lex->LookAhead(1, t2); } else { t2 = t; } SourceLocation location(GetSourceLocation(*this, t.ptr)); string token(t2.ptr, t2.len); errorLog_.Report(ParseErrorMsg(location, token)); return true;}9. Обновление функции rTemplateDecl2Не вдаваясь в детали, предлагаем Вам заменить функцию rTemplateDecl2() на предложенныйвариант. Это исключит некоторые ошибки при работе с шаблонными классами:bool Parser::rTemplateDecl2(Ptree*& decl, TemplateDeclKind &kind){ Token tk; Ptree *args = 0; if(lex->GetToken(tk) != TEMPLATE) return false; if(lex->LookAhead(0) != <) { if (lex->LookAhead(0) == CLASS) { // template instantiation decl = 0;
  29. 29. kind = tdk_instantiation; return true; // ignore TEMPLATE } decl = new (GC) PtreeTemplateDecl(new (GC) LeafReserved(tk));} else { decl = new (GC) PtreeTemplateDecl(new (GC) LeafReserved(tk)); if(lex->GetToken(tk) != <) return false; decl = PtreeUtil::Snoc(decl, new (GC) Leaf(tk)); if(!rTempArgList(args)) return false; if(lex->GetToken(tk) != >) return false;}decl = PtreeUtil::Nconc(decl, PtreeUtil::List(args, new (GC) Leaf(tk)));// ignore nested TEMPLATEwhile (lex->LookAhead(0) == TEMPLATE) { lex->GetToken(tk); if(lex->LookAhead(0) != <) break; lex->GetToken(tk); if(!rTempArgList(args)) return false; if(lex->GetToken(tk) != >) return false;}
  30. 30. if (args == 0) // template < > declaration kind = tdk_specialization; else // template < ... > declaration kind = tdk_decl; return true;}10. Определение позиции Ptree в тексте программыВ некоторых случаях бывает необходимо знать, где в тексте программы расположен код, изкоторого был построен определенный объект Ptree.Ниже предлагается функция, возвращающая адрес начала и конца области памяти с текстомпрограммы, из которой был создан указанный Ptree.void GetPtreePos(const Ptree *p, const char *&begin, const char *&end) { if (p == NULL) return; if (p->IsLeaf()) { const char *pos = p->GetLeafPosition(); if (begin == NULL) { begin = pos; } else { begin = min(begin, pos); } end = max(end, pos); } else { GetPtreePos(p->Car(), begin, end); GetPtreePos(p->Cdr(), begin, end); }
  31. 31. }11. Поддержка объявлений вида const A (a)Библиотека OpenС++ не поддерживает объявление переменных вида "const A (a)". Дляисправления этого недочета необходимо внутри функции Parser::rOtherDeclaration изменитьучасток кода:if(!rDeclarators(decl, type_encode, false)) return false;Вместо него следует использовать:if(!rDeclarators(decl, type_encode, false)) { // Support: const A (a); Lex::TokenIndex after_rDeclarators = lex->Save(); lex->Restore(before_rDeclarators); if (lex->CanLookAhead(3) && lex->CanLookAhead(-2)) { ptrdiff_t c_2 = lex->LookAhead(-2); ptrdiff_t c_1 = lex->LookAhead(-1); ptrdiff_t c0 = lex->LookAhead(0); ptrdiff_t c1 = lex->LookAhead(1); ptrdiff_t c2 = lex->LookAhead(2); ptrdiff_t c3 = lex->LookAhead(3); if (c_2 == CONST && c_1 == Identifier && c0 == ( && c1 == Identifier && c2 == ) && (c3 == ; || c3 == =)) { Lex::TokenContainer newEmptyContainer; ptrdiff_t pos = before_rDeclarators; lex->ReplaceTokens(pos + 2, pos + 3, newEmptyContainer); lex->ReplaceTokens(pos + 0, pos + 1, newEmptyContainer); lex->Restore(before_rDeclarators - 2); bool res = rDeclaration(statement); return res;
  32. 32. } }}В приведенном коде используются некоторые вспомогательные функции, отсутствующие в этойстатье. Но вы можете найти их в библиотеке VivaCore.12. Поддержка объявлений в классах функций вида T (min)() { }Иногда при программировании приходится использовать обходные пути для достижениярезультата. Например, широко известный макрос "max" часто приносит сложности приобъявлении в классе метода вида "T max() {return m;}". В этом случае прибегают к хитростям иобъявляют метод так: "T (max)() {return m;}". К сожалению, OpenC++ не понимает такиеобъявления внутри классов. Для исправления этого недочета следует модернизировать функциюParser::isConstructorDecl() следующим образом:bool Parser::isConstructorDecl(){ if(lex->LookAhead(0) != () return false; else{ // Support: T (min)() { } if (lex->LookAhead(1) == Identifier && lex->LookAhead(2) == ) && lex->LookAhead(3) == () return false; ptrdiff_t t = lex->LookAhead(1); if(t == * || t == & || t == () return false; // declarator else if(t == CONST || t == VOLATILE) return true; // constructor or declarator else if(isPtrToMember(1)) return false; // declarator (::*) else return true; // maybe constructor }
  33. 33. }13. Обработка конструкций "using" и "namespace" внутри функцийБиблиотека OpenC++ не "знает", что внутри функций можно использовать конструкции "using" и"namespace". Но это легко исправить, модернизируя функцию Parser::rStatement():bool Parser::rStatement(Ptree*& st){... case USING : return rUsing(st); case NAMESPACE : if (lex->LookAhead(2) == =) return rNamespaceAlias(st); return rExprStatement(st);...}14. Делаем "this" указателемКак известно, "this" является указателем. В OpenC++ это не так. Поэтому стоит исправить функциюWalker::TypeofThis(), чтобы исправить ошибку определения типа.Замените код:void Walker::TypeofThis(Ptree*, TypeInfo& t){ t.Set(env->LookupThis());}на:void Walker::TypeofThis(Ptree*, TypeInfo& t){ t.Set(env->LookupThis()); t.Reference();}
  34. 34. 15. Оптимизация функции LineNumber()Мы уже упоминали о функции Program::LineNumber(), говоря, что она возвращает имена файлов вразных форматах. И затем предложили функцию FixFileName() для исправления этой ситуации. Ноу функции LineNumber() есть еще один недостаток, связанный с медленной скоростью ее работы.Поэтому мы предлагаем оптимизированный вариант функции LineNumber():/* LineNumber() returns the line number of the line pointed to by PTR.*/size_t Program::LineNumber(const char* ptr, const char*& filename, ptrdiff_t& filename_length, const char *&beginLinePtr) const{ beginLinePtr = NULL; ptrdiff_t n; size_t len; size_t name; ptrdiff_t nline = 0; size_t pos = ptr - buf; size_t startPos = pos; if(pos > size){ // error? assert(false); filename = defaultname.c_str(); filename_length = defaultname.length(); beginLinePtr = buf; return 0; } ptrdiff_t line_number = -1; filename_length = 0;
  35. 35. while(pos > 0){ if (pos == oldLineNumberPos) { line_number = oldLineNumber + nline; assert(!oldFileName.empty()); filename = oldFileName.c_str(); filename_length = oldFileName.length(); assert(oldBeginLinePtr != NULL); if (beginLinePtr == NULL) beginLinePtr = oldBeginLinePtr; oldBeginLinePtr = beginLinePtr; oldLineNumber = line_number; oldLineNumberPos = startPos; return line_number; } switch(buf[--pos]) { case n : if (beginLinePtr == NULL) beginLinePtr = &(buf[pos]) + 1; ++nline; break; case # : len = 0; n = ReadLineDirective(pos, -1, name, len); if(n >= 0){ // unless #pragma if(line_number < 0) { line_number = n + nline; } if(len > 0 && filename_length == 0){ filename = (char*)Read(name); filename_length = len;
  36. 36. } } if(line_number >= 0 && filename_length > 0) { oldLineNumberPos = pos; oldBeginLinePtr = beginLinePtr; oldLineNumber = line_number; oldFileName = std::string(filename, filename_length); return line_number; } break; } } if(filename_length == 0){ filename = defaultname.c_str(); filename_length = defaultname.length(); oldFileName = std::string(filename, filename_length); } if (line_number < 0) { line_number = nline + 1; if (beginLinePtr == NULL) beginLinePtr = buf; oldBeginLinePtr = beginLinePtr; oldLineNumber = line_number; oldLineNumberPos = startPos; } return line_number;}
  37. 37. 16. Исправление ошибки при анализе директивы "#line"В некоторых случаях функция Program::ReadLineDirective() дает сбой, принимая за директиву"#line" посторонний текст. Исправленный вариант функции выглядит следующим образом:ptrdiff_t Program::ReadLineDirective(size_t i, ptrdiff_t line_number, size_t& filename, size_t& filename_length) const{ char c; do{ c = Ref(++i); } while(is_blank(c));#if defined(_MSC_VER) || defined(IRIX_CC) if(i + 5 <= GetSize() && strncmp(Read(i), "line ", 5) == 0) { i += 4; do{ c = Ref(++i); }while(is_blank(c)); } else { return -1; }#endif if(is_digit(c)){ /* # <line> <file> */ unsigned num = c - 0; for(;;){ c = Ref(++i); if(is_digit(c)) num = num * 10 + c - 0; else break;
  38. 38. } /* line_numberll be incremented soon */ line_number = num - 1; if(is_blank(c)){ do{ c = Ref(++i); }while(is_blank(c)); if(c == "){ size_t fname_start = i; do{ c = Ref(++i); } while(c != "); if(i > fname_start + 2){ filename = fname_start; filename_length = i - fname_start + 1; } } } } return line_number;}ЗаключениеКонечно, в этой статье описана малая часть возможных улучшений. Но хочется надеяться, что онибудут полезны разработчикам при использовании библиотеки OpenC++ и станут примерами,демонстрирующими как можно специализировать библиотеку для своих задач.Еще раз хочется напомнить, что показанные в этой статье и многие другие улучшения можнонайти в коде библиотеки VivaCore. Для многих задач библиотека VivaCore может оказатьсяудобнее, чем OpenC++.Если у Вас возникли вопросы, Вы хотите что-то добавить или прокомментировать, то нашакоманда Viva64.com [10] всегда рада и открыта к общению. Мы готовы обсудить возникшиевопросы, дать рекомендации и помочь в использовании библиотеки OpenC++ или VivaCore.Пишите нам!
  39. 39. Библиографический список 1. Зуев Е.А. Редкая профессия. PC Magazine/Russian Edition. Спецвыпуск N 5(75), 1997. http://www.viva64.com/go.php?url=43 2. Эллис М., Строуструп Б. Справочное руководство по языку программирования C++ с комментариями: Пер. с англ.- М.: Мир, 1992 - 445 с., илл. ISBN 5-03-002868-4. 3. OpenC++ library. http://www.viva64.com/go.php?url=16 4. Андрей Карпов, Евгений Рыжков. Сущность библиотеки анализа кода VivaCore. http://www.viva64.com/art-2-1-696420215.html 5. Semantic Designs site. http://www.viva64.com/go.php?url=19 6. Сайт компании Интерстрон. http://www.viva64.com/go.php?url=42 7. What is OpenTS? http://www.viva64.com/go.php?url=17 8. Евгений Рыжков. Viva64: что это и для кого? http://viva64.com/art-1-1-2081052208.html 9. Synopsis: A Source-code Introspection Tool. http://www.viva64.com/go.php?url=18 10. Сайт ООО "СиПроВер". http://www.Viva64.com

×