Commit e7766058 authored by Christian Hergert's avatar Christian Hergert
Browse files

clang: port clang indexer to new async API

This removes the "threaded" usage of IdeCodeIndexer and moves to
our new async/finish API.

It also creates a new CXIndex for each file, so we can more
reliably release memory after each parse. It probably means we
have to index some data more, but since this is a background
operation, I'd prefer to keep the memory usage lower at any
given time.
parent 0857b1de
......@@ -27,78 +27,168 @@
*/
struct _IdeClangCodeIndexEntries
{
GObject parent;
GObject parent;
CXTranslationUnit tu;
/*
* This is the index that was used to parse the translation unit. We are
* responsible for disposing the index when finalizing.
*/
CXIndex index;
GQueue cursors;
GQueue decl_cursors;
/*
* The unit that was parsed by the indexer. We traverse this to locate
* items within the file that are indexable. We also own the reference
* and must clang_disposeIndex() when finalizing.
*/
CXTranslationUnit unit;
gchar *main_file;
};
/*
* Queue of cursors as we iterate through the translation unit. These are
* GSlice allocated structures holding the raw CXCursor from the
* translation unit. Since we own the unit/index, these are safe for the
* lifetime of the object.
*/
GQueue cursors;
GQueue decl_cursors;
enum {
PROP_0,
PROP_MAIN_FILE,
PROP_UNIT,
N_PROPS
/* Path to the file that has been parsed. */
gchar *path;
};
static GParamSpec *properties [N_PROPS];
static void
cx_cursor_free (CXCursor *cursor)
{
g_slice_free (CXCursor, cursor);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (CXCursor, cx_cursor_free)
static void index_entries_iface_init (IdeCodeIndexEntriesInterface *iface);
static IdeSymbolKind
translate_kind (enum CXCursorKind cursor_kind)
{
switch ((int)cursor_kind)
{
case CXCursor_StructDecl:
return IDE_SYMBOL_STRUCT;
G_DEFINE_TYPE_EXTENDED (IdeClangCodeIndexEntries, ide_clang_code_index_entries, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE (IDE_TYPE_CODE_INDEX_ENTRIES, index_entries_iface_init))
case CXCursor_UnionDecl:
return IDE_SYMBOL_UNION;
case CXCursor_ClassDecl:
return IDE_SYMBOL_CLASS;
static void
cx_cursor_free (CXCursor *cursor)
case CXCursor_EnumDecl:
return IDE_SYMBOL_ENUM;
case CXCursor_FieldDecl:
return IDE_SYMBOL_FIELD;
case CXCursor_EnumConstantDecl:
return IDE_SYMBOL_ENUM_VALUE;
case CXCursor_FunctionDecl:
return IDE_SYMBOL_FUNCTION;
case CXCursor_CXXMethod:
return IDE_SYMBOL_METHOD;
case CXCursor_VarDecl:
case CXCursor_ParmDecl:
return IDE_SYMBOL_VARIABLE;
case CXCursor_TypedefDecl:
case CXCursor_NamespaceAlias:
case CXCursor_TypeAliasDecl:
return IDE_SYMBOL_ALIAS;
case CXCursor_Namespace:
return IDE_SYMBOL_NAMESPACE;
case CXCursor_FunctionTemplate:
case CXCursor_ClassTemplate:
return IDE_SYMBOL_TEMPLATE;
case CXCursor_MacroDefinition:
return IDE_SYMBOL_MACRO;
default:
return IDE_SYMBOL_NONE;
}
}
static const gchar *
get_symbol_prefix (IdeSymbolKind kind)
{
g_slice_free (CXCursor, cursor);
switch ((int)kind)
{
case IDE_SYMBOL_FUNCTION:
return "f\x1F";
case IDE_SYMBOL_STRUCT:
return "s\x1F";
case IDE_SYMBOL_VARIABLE:
return "v\x1F";
case IDE_SYMBOL_UNION:
return "u\x1F";
case IDE_SYMBOL_ENUM:
return "e\x1F";
case IDE_SYMBOL_CLASS:
return "c\x1F";
case IDE_SYMBOL_ENUM_VALUE:
return "a\x1F";
case IDE_SYMBOL_MACRO:
return "m\x1F";
default:
return "x\x1F";
}
}
/*
* Visit all children of a node and push those into cursors queue.
* push declaration cursor into decl_cursors queue only if its from the main file.
*/
static enum CXChildVisitResult
visitor (CXCursor cursor,
CXCursor parent,
CXClientData client_data)
{
IdeClangCodeIndexEntries *self = client_data;
enum CXCursorKind cursor_kind;
g_autoptr(CXCursor) child_cursor = NULL;
g_auto(CXString) cxpath = {0};
CXSourceLocation location;
const char *path;
CXFile file;
g_auto(CXString) cx_file_name = {0};
const char *file_name;
CXCursor *child_cursor;
g_assert (IDE_IS_CLANG_CODE_INDEX_ENTRIES (self));
g_assert (!clang_Cursor_isNull (cursor));
child_cursor = g_slice_new (CXCursor);
*child_cursor = cursor;
/*
* Visit all children of a node and push those into cursors queue. Push
* declaration cursor into decl_cursors queue only if its from the main
* file.
*/
child_cursor = g_slice_dup (CXCursor, &cursor);
g_queue_push_tail (&self->cursors, child_cursor);
location = clang_getCursorLocation (cursor);
clang_getSpellingLocation (location, &file, NULL, NULL, NULL);
cx_file_name = clang_getFileName (file);
file_name = clang_getCString (cx_file_name);
cxpath = clang_getFileName (file);
path = clang_getCString (cxpath);
cursor_kind = clang_getCursorKind (cursor);
if (!g_strcmp0 (file_name, self->main_file))
if (dzl_str_equal0 (path, self->path))
{
enum CXCursorKind cursor_kind = clang_getCursorKind (cursor);
if ((cursor_kind >= CXCursor_StructDecl && cursor_kind <= CXCursor_Namespace) ||
(cursor_kind >= CXCursor_Constructor && cursor_kind <= CXCursor_NamespaceAlias) ||
cursor_kind == CXCursor_TypeAliasDecl ||
cursor_kind == CXCursor_MacroDefinition)
{
g_queue_push_tail (&self->decl_cursors, child_cursor);
}
g_queue_push_tail (&self->decl_cursors, child_cursor);
}
/* TODO: Record MACRO EXPANSION FOR G_DEFINE_TYPE, G_DECLARE_TYPE */
......@@ -107,78 +197,80 @@ visitor (CXCursor cursor,
}
/*
* decl_cursors store declarations to be returned by this class. If decl_cursors
* is not empty then this function returns a declaration popped from queue,
* else this will do Breadth first traversal on AST till it finds a declaration.
* On next request when decl_cursors is empty it will continue traversal
* from where it has stopped in previously.
* decl_cursors store declarations to be returned by this class. If
* decl_cursors is not empty then this function returns a declaration popped
* from queue, else this will do Breadth first traversal on AST till it
* finds a declaration. On next request when decl_cursors is empty it will
* continue traversal from where it has stopped in previously.
*/
static IdeCodeIndexEntry *
ide_clang_code_index_entries_real_get_next_entry (IdeClangCodeIndexEntries *self,
gboolean *finish)
{
g_autoptr(CXCursor) cursor = NULL;
g_autofree gchar *name = NULL;
g_auto(CXString) cxname = {0};
g_auto(CXString) usr = {0};
CXSourceLocation location;
IdeSymbolFlags flags = IDE_SYMBOL_FLAGS_NONE;
IdeSymbolKind kind = IDE_SYMBOL_NONE;
enum CXLinkageKind linkage;
enum CXCursorKind cursor_kind;
const gchar *cname = NULL;
const gchar *prefix = NULL;
const gchar *key = NULL;
guint line = 0;
guint column = 0;
guint offset = 0;
enum CXLinkageKind linkage;
g_auto(CXString) cx_name = {0};
const gchar *cname = NULL;
gchar *prefix = NULL;
g_autofree gchar *name = NULL;
g_autofree gchar *key = NULL;
IdeSymbolKind kind = IDE_SYMBOL_NONE;
IdeSymbolFlags flags = IDE_SYMBOL_FLAGS_NONE;
CXCursor *cursor = NULL;
enum CXCursorKind cursor_kind;
g_assert (IDE_IS_CLANG_CODE_INDEX_ENTRIES (self));
g_assert (finish != NULL);
*finish = FALSE;
/* First declaration missing */
/* Traverse AST till atleast one declaration is found */
while (g_queue_is_empty (&self->decl_cursors))
{
g_autoptr(CXCursor) decl_cursor = NULL;
if (g_queue_is_empty (&self->cursors))
{
clang_disposeTranslationUnit (self->tu);
self->tu = NULL;
*finish = TRUE;
return NULL;
}
cursor = g_queue_pop_head (&self->cursors);
/* Resume visiting children.*/
clang_visitChildren (*cursor, visitor, self);
g_slice_free (CXCursor, cursor);
decl_cursor = g_queue_pop_head (&self->cursors);
clang_visitChildren (*decl_cursor, visitor, self);
}
cursor = g_queue_pop_head (&self->decl_cursors);
g_assert (!g_queue_is_empty (&self->decl_cursors));
cursor = g_queue_pop_head (&self->decl_cursors);
location = clang_getCursorLocation (*cursor);
clang_getSpellingLocation (location, NULL, &line, &column, &offset);
cx_name = clang_getCursorSpelling (*cursor);
cname = clang_getCString (cx_name);
if ((cname == NULL) || (cname[0] == '\0'))
/* Skip this item if its NULL, we'll get called again to fetch
* the next item. One possible chance for improvement here is
* to jump to the next item instead of returning here.
*/
cxname = clang_getCursorSpelling (*cursor);
cname = clang_getCString (cxname);
if (dzl_str_empty0 (cname))
return NULL;
cursor_kind = clang_getCursorKind (*cursor);
/*
* If current cursor is a type alias then resolve actual type of this recursively
* by resolving parent type.
* If current cursor is a type alias then resolve actual type of this
* recursively by resolving parent type.
*/
cursor_kind = clang_getCursorKind (*cursor);
if ((cursor_kind == CXCursor_TypedefDecl) ||
(cursor_kind == CXCursor_NamespaceAlias) || (cursor_kind == CXCursor_TypeAliasDecl))
(cursor_kind == CXCursor_NamespaceAlias) ||
(cursor_kind == CXCursor_TypeAliasDecl))
{
CXType type;
CXCursor temp = *cursor;
type = clang_getTypedefDeclUnderlyingType (temp);
CXType type = clang_getTypedefDeclUnderlyingType (temp);
while (CXType_Invalid != type.kind)
{
......@@ -189,98 +281,22 @@ ide_clang_code_index_entries_real_get_next_entry (IdeClangCodeIndexEntries *self
cursor_kind = clang_getCursorKind (temp);
}
/* Translate CXCursorKind to IdeSymbolKind */
switch ((int)cursor_kind)
{
case CXCursor_StructDecl:
kind = IDE_SYMBOL_STRUCT;
break;
case CXCursor_UnionDecl:
kind = IDE_SYMBOL_UNION;
break;
case CXCursor_ClassDecl:
kind = IDE_SYMBOL_CLASS;
break;
case CXCursor_EnumDecl:
kind = IDE_SYMBOL_ENUM;
break;
case CXCursor_FieldDecl:
kind = IDE_SYMBOL_FIELD;
break;
case CXCursor_EnumConstantDecl:
kind = IDE_SYMBOL_ENUM_VALUE;
break;
case CXCursor_FunctionDecl:
kind = IDE_SYMBOL_FUNCTION;
break;
case CXCursor_CXXMethod:
kind = IDE_SYMBOL_METHOD;
break;
case CXCursor_VarDecl:
case CXCursor_ParmDecl:
kind = IDE_SYMBOL_VARIABLE;
break;
case CXCursor_TypedefDecl:
case CXCursor_NamespaceAlias:
case CXCursor_TypeAliasDecl:
kind = IDE_SYMBOL_ALIAS;
break;
case CXCursor_Namespace:
kind = IDE_SYMBOL_NAMESPACE;
break;
case CXCursor_FunctionTemplate:
case CXCursor_ClassTemplate:
kind = IDE_SYMBOL_TEMPLATE;
break;
case CXCursor_MacroDefinition:
kind = IDE_SYMBOL_MACRO;
break;
default:
kind = IDE_SYMBOL_NONE;
break;
}
/* Add prefix to name so that filters can be applied */
if (kind == IDE_SYMBOL_FUNCTION)
prefix = "f\x1F";
else if (kind == IDE_SYMBOL_STRUCT)
prefix = "s\x1F";
else if (kind == IDE_SYMBOL_VARIABLE)
prefix = "v\x1F";
else if (kind == IDE_SYMBOL_UNION)
prefix = "u\x1F";
else if (kind == IDE_SYMBOL_ENUM)
prefix = "e\x1F";
else if (kind == IDE_SYMBOL_CLASS)
prefix = "c\x1F";
else if (kind == IDE_SYMBOL_ENUM_VALUE)
prefix = "a\x1F";
else if (kind == IDE_SYMBOL_MACRO)
prefix = "m\x1F";
else
prefix = "x\x1F";
kind = translate_kind (cursor_kind);
prefix = get_symbol_prefix (kind);
name = g_strconcat (prefix, cname, NULL);
if (clang_isCursorDefinition (*cursor))
flags |= IDE_SYMBOL_FLAGS_IS_DEFINITION;
linkage = clang_getCursorLinkage (*cursor);
if (linkage == CXLinkage_Internal)
{
flags |= IDE_SYMBOL_FLAGS_IS_STATIC;
}
flags |= IDE_SYMBOL_FLAGS_IS_STATIC;
else if (linkage == CXLinkage_NoLinkage)
{
flags |= IDE_SYMBOL_FLAGS_IS_MEMBER;
}
flags |= IDE_SYMBOL_FLAGS_IS_MEMBER;
else
{
g_auto(CXString) usr = {0};
usr = clang_getCursorUSR (*cursor);
key = g_strdup (clang_getCString (usr));
key = clang_getCString (usr);
}
return g_object_new (IDE_TYPE_CODE_INDEX_ENTRY,
......@@ -293,7 +309,6 @@ ide_clang_code_index_entries_real_get_next_entry (IdeClangCodeIndexEntries *self
NULL);
}
/* Retrives next IdeCodeIndexEntry. */
static IdeCodeIndexEntry *
ide_clang_code_index_entries_get_next_entry (IdeCodeIndexEntries *entries)
{
......@@ -301,15 +316,27 @@ ide_clang_code_index_entries_get_next_entry (IdeCodeIndexEntries *entries)
g_autoptr(IdeCodeIndexEntry) entry = NULL;
gboolean finish = FALSE;
g_assert (IDE_IS_MAIN_THREAD ());
g_assert (IDE_IS_CLANG_CODE_INDEX_ENTRIES (self));
entry = ide_clang_code_index_entries_real_get_next_entry (self, &finish);
while (entry == NULL && !finish)
do
entry = ide_clang_code_index_entries_real_get_next_entry (self, &finish);
while (entry == NULL && finish == FALSE);
g_assert (entry == NULL || IDE_IS_CODE_INDEX_ENTRY (entry));
return g_steal_pointer (&entry);
}
static void
index_entries_iface_init (IdeCodeIndexEntriesInterface *iface)
{
iface->get_next_entry = ide_clang_code_index_entries_get_next_entry;
}
G_DEFINE_TYPE_WITH_CODE (IdeClangCodeIndexEntries, ide_clang_code_index_entries, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (IDE_TYPE_CODE_INDEX_ENTRIES, index_entries_iface_init))
static void
ide_clang_code_index_entries_finalize (GObject *object)
{
......@@ -321,98 +348,18 @@ ide_clang_code_index_entries_finalize (GObject *object)
g_queue_foreach (&self->cursors, (GFunc)cx_cursor_free, NULL);
g_queue_clear (&self->cursors);
if (self->tu != NULL)
{
clang_disposeTranslationUnit (self->tu);
self->tu = NULL;
}
g_clear_pointer (&self->unit, clang_disposeTranslationUnit);
g_clear_pointer (&self->index, clang_disposeIndex);
G_OBJECT_CLASS(ide_clang_code_index_entries_parent_class)->finalize (object);
}
static void
ide_clang_code_index_entries_constructed (GObject *object)
{
IdeClangCodeIndexEntries *self = (IdeClangCodeIndexEntries *)object;
CXCursor root_cursor;
G_OBJECT_CLASS (ide_clang_code_index_entries_parent_class)->constructed (object);
root_cursor = clang_getTranslationUnitCursor (self->tu);
g_queue_push_head (&self->cursors, g_slice_dup (CXCursor, &root_cursor));
}
static void
ide_clang_code_index_entries_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
IdeClangCodeIndexEntries *self = (IdeClangCodeIndexEntries *)object;
switch (prop_id)
{
case PROP_MAIN_FILE:
self->main_file = g_strdup ((gchar *)g_value_get_pointer (value));
break;
case PROP_UNIT:
self->tu = g_value_get_pointer (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
ide_clang_code_index_entries_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
IdeClangCodeIndexEntries *self = (IdeClangCodeIndexEntries *)object;
switch (prop_id)
{
case PROP_MAIN_FILE:
g_value_set_pointer (value, self->main_file);
break;
case PROP_UNIT:
g_value_set_pointer (value, &self->tu);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
ide_clang_code_index_entries_class_init (IdeClangCodeIndexEntriesClass *klass)
{
GObjectClass *object_class = (GObjectClass *)klass;
object_class->finalize = ide_clang_code_index_entries_finalize;
object_class->constructed = ide_clang_code_index_entries_constructed;
object_class->set_property = ide_clang_code_index_entries_set_property;
object_class->get_property = ide_clang_code_index_entries_get_property;
properties [PROP_MAIN_FILE] =
g_param_spec_pointer ("main-file",
"Main File",
"Name of file from which TU is parsed.",
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
properties [PROP_UNIT] =
g_param_spec_pointer ("unit",
"Unit",
"Translation Unit from which index entries are to be generated",
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
index_entries_iface_init (IdeCodeIndexEntriesInterface *iface)
{
iface->get_next_entry = ide_clang_code_index_entries_get_next_entry;
}
static void
......@@ -420,15 +367,40 @@ ide_clang_code_index_entries_init (IdeClangCodeIndexEntries *self)
{
}
/**
* ide_clang_code_index_entries_new:
* @index: (transfer full): a #CXIndex to take ownership of
* @unit: (transfer full): a #CXTranslationUnit to take ownership of
* @path: the path of the file that was indexed
*
* Creates a new #IdeClangCodeIndexEntries that can be used to iterate
* the translation unit for interesting data.
*
* Returns: (transfer full): a new #IdeClangCodeIndexEntries
*
* Thread safety: this object may be created from any thread, but the
* ide_clang_code_index_entries_get_next_entry() may only be called
* from the main thread, as required by the base interface.
*/
IdeClangCodeIndexEntries *
ide_clang_code_index_entries_new (CXTranslationUnit unit,
const gchar *main_file)
ide_clang_code_index_entries_new (CXIndex index,
CXTranslationUnit unit,
const gchar *path)
{
g_return_val_if_fail ((unit != NULL), NULL);
g_return_val_if_fail ((main_file != NULL), NULL);
IdeClangCodeIndexEntries *self;
CXCursor root;
return g_object_new (IDE_TYPE_CLANG_CODE_INDEX_ENTRIES,
"unit", unit,
"main-file", main_file,
NULL);
g_return_val_if_fail (index != NULL, NULL);
g_return_val_if_fail (unit != NULL, NULL);
g_return_val_if_fail (path != NULL, NULL);
self = g_object_new (IDE_TYPE_CLANG_CODE_INDEX_ENTRIES, NULL);
self->index = index;
self->unit = unit;
self->path = g_strdup (path);
root = clang_getTranslationUnitCursor (unit);
g_queue_push_head (&self->cursors, g_slice_dup (CXCursor, &root));