Commit 8a54185e authored by Christian Hergert's avatar Christian Hergert
Browse files

xml-pack: various correctness and leak fixes

This fixes various reference leaks and correctness issues that could load
to crashes under some scenarios. Much of the async task ownership has been
changed to use the paterns we use else where in Builder.
parent 48c82b4d
......@@ -25,14 +25,13 @@
#include "ide-xml-parser.h"
#include "ide-xml-rng-parser.h"
#include "ide-xml-sax.h"
#include "ide-xml-schema-cache-entry.h"
#include "ide-xml-schema.h"
#include "ide-xml-service.h"
#include "ide-xml-schema-cache-entry.h"
#include "ide-xml-tree-builder.h"
#include "ide-xml-tree-builder-utils-private.h"
#include "ide-xml-validator.h"
#include "ide-xml-tree-builder.h"
struct _IdeXmlTreeBuilder
{
IdeObject parent_instance;
......@@ -49,38 +48,54 @@ typedef struct
gint64 sequence;
} TreeBuilderState;
typedef struct
{
IdeXmlTreeBuilder *self;
GTask *task;
GPtrArray *schemas;
guint index;
} FetchSchemasState;
static void tree_builder_state_free (TreeBuilderState *state);
static void fetch_schema_state_free (FetchSchemasState *state);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (FetchSchemasState, fetch_schema_state_free)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (TreeBuilderState, tree_builder_state_free)
G_DEFINE_TYPE (IdeXmlTreeBuilder, ide_xml_tree_builder, IDE_TYPE_OBJECT)
static void
tree_builder_state_free (TreeBuilderState *state)
{
g_assert (state != NULL);
g_clear_pointer (&state->content, g_bytes_unref);
g_clear_pointer (&state->analysis, ide_xml_analysis_unref);
g_clear_object (&state->file);
g_slice_free (TreeBuilderState, state);
}
G_DEFINE_TYPE (IdeXmlTreeBuilder, ide_xml_tree_builder, IDE_TYPE_OBJECT)
static IdeDiagnostic *
create_diagnostic (IdeContext *context,
const gchar *msg,
GFile *file,
gint line,
gint col,
IdeDiagnosticSeverity severity)
create_diagnostic (IdeContext *context,
const gchar *msg,
GFile *file,
gint line,
gint col,
IdeDiagnosticSeverity severity)
{
IdeDiagnostic *diagnostic;
g_autoptr(IdeSourceLocation) loc = NULL;
g_autoptr(IdeFile) ifile = NULL;
g_assert (IDE_IS_CONTEXT (context));
g_assert (G_IS_FILE (file));
ifile = ide_file_new (context, file);
loc = ide_source_location_new (ifile,
line - 1,
col - 1,
0);
diagnostic = ide_diagnostic_new (severity, msg, loc);
return diagnostic;
return ide_diagnostic_new (severity, msg, loc);
}
static GBytes *
......@@ -88,50 +103,39 @@ ide_xml_tree_builder_get_file_content (IdeXmlTreeBuilder *self,
GFile *file,
gint64 *sequence)
{
IdeContext *context;
g_autoptr(GBytes) content = NULL;
IdeBufferManager *manager;
IdeUnsavedFiles *unsaved_files;
IdeUnsavedFile *uf;
IdeContext *context;
IdeBuffer *buffer;
gint64 sequence_tmp = -1;
GBytes *content = NULL;
g_assert (IDE_IS_XML_TREE_BUILDER (self));
g_assert (G_IS_FILE (file));
context = ide_object_get_context (IDE_OBJECT (self));
manager = ide_context_get_buffer_manager (context);
buffer = ide_buffer_manager_find_buffer (manager, file);
if (NULL != (buffer = ide_buffer_manager_find_buffer (manager, file)))
if (buffer != NULL)
{
content = ide_buffer_get_content (buffer);
unsaved_files = ide_context_get_unsaved_files (context);
if (NULL != (uf = ide_unsaved_files_get_unsaved_file (unsaved_files, file)))
sequence_tmp = ide_unsaved_file_get_sequence (uf);
sequence_tmp = ide_buffer_get_change_count (buffer);
}
if (sequence != NULL)
*sequence = sequence_tmp;
return content;
return g_steal_pointer (&content);
}
typedef struct _FetchSchemasState
{
IdeXmlTreeBuilder *self;
GTask *task;
GPtrArray *schemas;
guint index;
} FetchSchemasState;
static void
fetch_schema_state_free (FetchSchemasState *state)
{
g_clear_object (&state->self);
g_clear_pointer (&state->schemas, g_ptr_array_unref);
self->task = NULL;
g_assert (state != NULL);
g_clear_pointer (&state->schemas, g_ptr_array_unref);
g_clear_object (&state->self);
g_clear_object (&state->task);
g_slice_free (FetchSchemasState, state);
}
......@@ -141,92 +145,114 @@ fetch_schemas_cb (GObject *object,
gpointer user_data)
{
DzlTaskCache *schemas_cache = (DzlTaskCache *)object;
FetchSchemasState *state = (FetchSchemasState *)user_data;
g_autoptr (IdeXmlSchemaCacheEntry) cache_entry = NULL;
GTask *task = state->task;
guint count;
g_autoptr(FetchSchemasState) state = user_data;
g_autoptr(IdeXmlSchemaCacheEntry) cache_entry = NULL;
IdeXmlSchemaCacheEntry *entry;
GError *error = NULL;
g_autoptr(GError) error = NULL;
guint *count;
g_assert (DZL_IS_TASK_CACHE (schemas_cache));
g_assert (G_IS_TASK (result));
g_assert (G_IS_TASK (task));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (state != NULL);
g_assert (G_IS_TASK (state->task));
g_assert (IDE_IS_XML_TREE_BUILDER (state->self));
g_assert (state->schemas != NULL);
g_assert (state->index < state->schemas->len);
cache_entry = dzl_task_cache_get_finish (schemas_cache, result, &error);
entry = g_ptr_array_index (state->schemas, state->index);
cache_entry = dzl_task_cache_get_finish (schemas_cache, result, &error);
if (cache_entry->content != NULL)
entry->content = g_bytes_ref (cache_entry->content);
if (cache_entry != NULL)
{
if (cache_entry->content != NULL &&
entry->content != cache_entry->content)
{
g_clear_pointer (&entry->content, g_bytes_unref);
entry->content = g_bytes_ref (cache_entry->content);
}
if (cache_entry->error_message != NULL &&
entry->error_message != cache_entry->error_message)
{
g_free (error->message);
entry->error_message = g_strdup (cache_entry->error_message);
}
if (cache_entry->error_message != NULL)
entry->error_message = g_strdup (cache_entry->error_message);
if (cache_entry->schema != NULL &&
entry->schema != cache_entry->schema)
{
g_clear_pointer (&entry->schema, ide_xml_schema_unref);
entry->schema = ide_xml_schema_ref (cache_entry->schema);
}
if (cache_entry->schema != NULL)
entry->schema = ide_xml_schema_ref (cache_entry->schema);
entry->state = cache_entry->state;
entry->mtime = cache_entry->mtime;
}
entry->state = cache_entry->state;
entry->mtime = cache_entry->mtime;
count = g_task_get_task_data (state->task);
g_assert (count != NULL);
fetch_schema_state_free (state);
count = GPOINTER_TO_UINT (g_task_get_task_data (task));
--count;
g_task_set_task_data (task, GUINT_TO_POINTER (count), NULL);
(*count)--;
if (count == 0)
{
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
if (*count == 0)
g_task_return_boolean (state->task, TRUE);
}
static gboolean
static void
fetch_schemas_async (IdeXmlTreeBuilder *self,
GPtrArray *schemas,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
IdeContext *context;
g_autoptr(GTask) task = NULL;
g_autoptr(GPtrArray) schemas_copy = NULL;
IdeXmlService *service;
DzlTaskCache *schemas_cache;
GTask *task;
guint count = 0;
gboolean has_external_schemas = FALSE;
IdeContext *context;
guint *count = 0;
g_assert (IDE_IS_XML_TREE_BUILDER (self));
g_assert (schemas != NULL);
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
if (schemas->len == 0)
return FALSE;
/* TODO: use a worker thread */
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, fetch_schemas_async);
g_task_set_priority (task, G_PRIORITY_LOW);
count = g_new0 (guint, 1);
g_task_set_task_data (task, count, g_free);
/* Make a copy of schemas to ensure they cannot be changed
* during the lifetime of the operation, as the index within
* the array is stashed by the FetchSchemasState.
*/
schemas_copy = g_ptr_array_new_with_free_func ((GDestroyNotify)ide_xml_schema_cache_entry_unref);
context = ide_object_get_context (IDE_OBJECT (self));
service = ide_context_get_service_typed (context, IDE_TYPE_XML_SERVICE);
schemas_cache = ide_xml_service_get_schemas_cache (service);
for (gint i = 0; i < schemas->len; ++i)
for (guint i = 0; i < schemas->len; i++)
{
IdeXmlSchemaCacheEntry *entry;
IdeXmlSchemaCacheEntry *entry = g_ptr_array_index (schemas, i);
FetchSchemasState *state;
entry = g_ptr_array_index (schemas, i);
/* Check if it's an internal schema */
if (entry->file == NULL)
continue;
state = g_slice_new0 (FetchSchemasState);
state->self = g_object_ref (self);
state->schemas = g_ptr_array_ref (schemas);
state->task = task;
state->schemas = g_ptr_array_ref (schemas_copy);
state->task = g_object_ref (task);
(*count)++;
++count;
g_task_set_task_data (task, GUINT_TO_POINTER (count), NULL);
has_external_schemas = TRUE;
g_ptr_array_add (schemas_copy, ide_xml_schema_cache_entry_ref (entry));
state->index = schemas_copy->len - 1;
state->index = i;
/* TODO: peek schemas if it's already in cache */
dzl_task_cache_get_async (schemas_cache,
entry->file,
......@@ -236,10 +262,8 @@ fetch_schemas_async (IdeXmlTreeBuilder *self,
state);
}
if (!has_external_schemas)
if (*count == 0)
g_task_return_boolean (task, TRUE);
return TRUE;
}
static gboolean
......@@ -269,7 +293,6 @@ ide_xml_tree_builder_parse_worker (GTask *task,
const gchar *doc_data;
xmlDoc *doc;
gsize doc_size;
IdeXmlSchemaKind kind;
gint parser_flags;
g_assert (IDE_IS_XML_TREE_BUILDER (self));
......@@ -281,46 +304,48 @@ ide_xml_tree_builder_parse_worker (GTask *task,
return;
state = g_task_get_task_data (task);
g_assert (state != NULL);
g_assert (state->analysis != NULL);
schemas = ide_xml_analysis_get_schemas (state->analysis);
g_assert (schemas != NULL);
context = ide_object_get_context (IDE_OBJECT (self));
g_assert (IDE_IS_CONTEXT (context));
xmlInitParser ();
doc_data = g_bytes_get_data (state->content, &doc_size);
parser_flags = XML_PARSE_RECOVER | XML_PARSE_NOERROR | XML_PARSE_NOWARNING | XML_PARSE_COMPACT;
if (NULL != (doc = xmlReadMemory (doc_data,
doc_size,
NULL, NULL,
parser_flags)))
doc = xmlReadMemory (doc_data, doc_size, NULL, NULL, parser_flags);
if (doc != NULL)
{
doc->URL = (guchar *)g_file_get_uri (state->file);
for (gint i = 0; i < schemas->len; ++i)
for (guint i = 0; i < schemas->len; ++i)
{
IdeXmlSchemaCacheEntry *entry;
IdeXmlSchemaCacheEntry *entry = g_ptr_array_index (schemas, i);
const gchar *schema_data;
gsize schema_size;
g_autoptr (IdeDiagnostics) diagnostics = NULL;
g_autoptr (IdeDiagnostic) diagnostic = NULL;
g_autofree gchar *uri = NULL;
g_autofree gchar *msg = NULL;
gboolean schema_ret;
entry = g_ptr_array_index (schemas, i);
kind = entry->kind;
if (kind == SCHEMA_KIND_RNG || kind == SCHEMA_KIND_XML_SCHEMA)
if (entry->kind == SCHEMA_KIND_RNG || entry->kind == SCHEMA_KIND_XML_SCHEMA)
{
if (entry->content != NULL)
{
schema_data = g_bytes_get_data (entry->content, &schema_size);
schema_ret = ide_xml_validator_set_schema (self->validator,
kind,
entry->kind,
schema_data,
schema_size);
}
else
{
g_autoptr(IdeDiagnostic) diagnostic = NULL;
g_assert (entry->error_message != NULL);
diagnostic = create_diagnostic (context,
......@@ -333,17 +358,22 @@ ide_xml_tree_builder_parse_worker (GTask *task,
continue;
}
}
else if (kind == SCHEMA_KIND_DTD)
else if (entry->kind == SCHEMA_KIND_DTD)
{
schema_ret = ide_xml_validator_set_schema (self->validator, SCHEMA_KIND_DTD, NULL, 0);
schema_ret = ide_xml_validator_set_schema (self->validator, SCHEMA_KIND_DTD, NULL, 0);
}
else
g_assert_not_reached ();
if (!schema_ret)
{
g_autoptr(IdeDiagnostic) diagnostic = NULL;
g_autofree gchar *uri = NULL;
g_autofree gchar *msg = NULL;
uri = g_file_get_uri (entry->file);
msg = g_strdup_printf ("Can't parse the schema: '%s'", uri);
diagnostic = create_diagnostic (context,
msg,
state->file,
......@@ -356,6 +386,10 @@ ide_xml_tree_builder_parse_worker (GTask *task,
if (ide_xml_validator_validate (self->validator, doc, &diagnostics) != 0)
{
g_autoptr(IdeDiagnostic) diagnostic = NULL;
g_autofree gchar *uri = NULL;
g_autofree gchar *msg = NULL;
if (entry->file == NULL)
msg = g_strdup_printf ("Can't validate the internal schema");
else
......@@ -384,7 +418,9 @@ ide_xml_tree_builder_parse_worker (GTask *task,
g_debug ("can't create xmlDoc\n");
}
g_task_return_pointer (task, state->analysis, (GDestroyNotify)ide_xml_analysis_unref);
g_task_return_pointer (task,
g_steal_pointer (&state->analysis),
(GDestroyNotify)ide_xml_analysis_unref);
}
static void
......@@ -392,23 +428,18 @@ ide_xml_tree_builder_build_tree_cb2 (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
g_autoptr(GTask) task = user_data;
IdeXmlTreeBuilder *self = (IdeXmlTreeBuilder *)object;
g_autoptr(GError) error = NULL;
IdeXmlTreeBuilder *self;
g_assert (G_IS_TASK (result));
g_assert (G_IS_TASK (task));
g_autoptr(GTask) task = user_data;
self = g_task_get_source_object (task);
g_assert (IDE_IS_XML_TREE_BUILDER (self));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (G_IS_TASK (task));
if (!fetch_schemas_finish (self, result, &error))
{
g_task_return_error (task, g_steal_pointer (&error));
return;
}
g_task_run_in_thread (task, ide_xml_tree_builder_parse_worker);
g_task_return_error (task, g_steal_pointer (&error));
else
g_task_run_in_thread (task, ide_xml_tree_builder_parse_worker);
}
static void
......@@ -416,37 +447,51 @@ ide_xml_tree_builder_build_tree_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
IdeXmlTreeBuilder *self;
IdeXmlParser *parser = (IdeXmlParser *)object;
g_autoptr(IdeXmlAnalysis) analysis = NULL;
g_autoptr(GTask) task = user_data;
g_autoptr(GError) error = NULL;
IdeXmlTreeBuilder *self;
TreeBuilderState *state;
IdeXmlAnalysis *analysis;
GCancellable *cancellable;
g_assert (G_IS_TASK (result));
g_assert (IDE_IS_XML_PARSER (parser));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (G_IS_TASK (task));
self = g_task_get_source_object (task);
g_assert (IDE_IS_XML_TREE_BUILDER (self));
if (NULL == (analysis = ide_xml_parser_get_analysis_finish (self->parser, result, &error)))
state = g_task_get_task_data (task);
g_assert (state != NULL);
cancellable = g_task_get_cancellable (task);
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
analysis = ide_xml_parser_get_analysis_finish (self->parser, result, &error);
if (analysis == NULL)
{
g_task_return_error (task, g_steal_pointer (&error));
return;
}
state = g_task_get_task_data (task);
state->analysis = ide_xml_analysis_ref (analysis);
ide_xml_analysis_set_sequence (analysis, state->sequence);
if (analysis->schemas != NULL &&
fetch_schemas_async (self,
analysis->schemas,
g_task_get_cancellable (task),
ide_xml_tree_builder_build_tree_cb2,
g_object_ref (task)))
return;
if (analysis->schemas == NULL)
{
g_task_return_pointer (task,
g_steal_pointer (&analysis),
(GDestroyNotify)ide_xml_analysis_unref);
return;
}
g_task_return_pointer (task, analysis, (GDestroyNotify)ide_xml_analysis_unref);
fetch_schemas_async (self,
analysis->schemas,
cancellable,
ide_xml_tree_builder_build_tree_cb2,
g_steal_pointer (&task));
}
void
......@@ -457,9 +502,9 @@ ide_xml_tree_builder_build_tree_async (IdeXmlTreeBuilder *self,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
TreeBuilderState *state;
GBytes *content = NULL;
gint64 sequence;
g_autoptr(TreeBuilderState) state = NULL;
g_autoptr(GBytes) content = NULL;
gint64 sequence = 0;
g_return_if_fail (IDE_IS_XML_TREE_BUILDER (self));
g_return_if_fail (G_IS_FILE (file));
......@@ -467,8 +512,11 @@ ide_xml_tree_builder_build_tree_async (IdeXmlTreeBuilder *self,
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, ide_xml_tree_builder_build_tree_async);
g_task_set_priority (task, G_PRIORITY_LOW);
if (NULL == (content = ide_xml_tree_builder_get_file_content (self, file, &sequence)))
content = ide_xml_tree_builder_get_file_content (self, file, &sequence);
if (content == NULL)
{
g_task_return_new_error (task,
G_IO_ERROR,
......@@ -481,7 +529,10 @@ ide_xml_tree_builder_build_tree_async (IdeXmlTreeBuilder *self,
state->file = g_object_ref (file);
state->content = g_bytes_ref (content);
state->sequence = sequence;
g_task_set_task_data (task, state, (GDestroyNotify)tree_builder_state_free);
g_task_set_task_data (task,
g_steal_pointer (&state),
(GDestroyNotify)tree_builder_state_free);
ide_xml_parser_get_analysis_async (self->parser,
file,
......@@ -497,19 +548,10 @@ ide_xml_tree_builder_build_tree_finish (IdeXmlTreeBuilder *self,
GAsyncResult *result,
GError **error)
{
GTask *task = (GTask *)result;
g_return_val_if_fail (IDE_IS_XML_TREE_BUILDER (self), NULL);
g_return_val_if_fail (G_IS_TASK (task), NULL);
g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
return g_task_propagate_pointer (task, error);
}
IdeXmlTreeBuilder *
ide_xml_tree_builder_new (DzlTaskCache *schemas)
{
return g_object_new (IDE_TYPE_XML_TREE_BUILDER,
NULL);
return g_task_propagate_pointer (G_TASK (result), error);
}
static void
......
......@@ -18,7 +18,6 @@
#pragma once
#include <glib-object.h>
#include <ide.h>
#include "ide-xml-analysis.h"
......@@ -30,7 +29,6 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE (IdeXmlTreeBuilder, ide_xml_tree_builder, IDE, XML_TREE_BUILDER, IdeObject)
IdeXmlTreeBuilder *ide_xml_tree_builder_new (DzlTaskCache *schemas);
void ide_xml_tree_builder_build_tree_async (IdeXmlTreeBuilder *self,
GFile *file,
GCancellable *cancellable,
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment