[gnome-code-assistance] [backends/vala] Use external helper to parse



commit bf72e7c7a408bb22d27ed2f1ab50ea675533cd94
Author: Jesse van den Kieboom <jessevdk gmail com>
Date:   Wed Dec 4 17:06:16 2013 +0100

    [backends/vala] Use external helper to parse
    
    libvala has memory leaks which cannot be prevented at the
    moment. Therefore, move the actual parsing out of process.

 Makefile.am                            |    1 +
 backends/vala/Makefile.am              |   71 +++++++++--
 backends/vala/config.vala.in           |   26 ++++
 backends/vala/dbus.vala                |   96 ++++++++++++---
 backends/vala/diagnostics.vala         |   44 +++++---
 backends/vala/hashutils.vala           |   36 ++++++
 backends/vala/helper.vala              |  186 ++++++++++++++++++++++++++++
 backends/vala/makefileintegration.vala |   28 ++---
 backends/vala/rpc.vala                 |   21 +++
 backends/vala/service.vala             |  211 +++++++++++++++++++++++++++++--
 backends/vala/types.vala               |   14 ++-
 backends/vala/valaoptionparser.vala    |  151 ++++++++++++++++++++---
 configure.ac                           |    1 +
 13 files changed, 791 insertions(+), 95 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index bcbb933..2c53d34 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -32,6 +32,7 @@ BUILT_SOURCES =
 DISTCLEANFILES =
 gsettings_SCHEMAS =
 TESTS =
+noinst_PROGRAMS =
 
 include data/Makefile.am
 include backends/Makefile.am
diff --git a/backends/vala/Makefile.am b/backends/vala/Makefile.am
index 21a5abd..cbc9948 100644
--- a/backends/vala/Makefile.am
+++ b/backends/vala/Makefile.am
@@ -1,40 +1,83 @@
 valabackenddir = $(GCA_BACKENDS_EXEC_DIR)
-valabackend_PROGRAMS = backends/vala/vala
+
+noinst_LTLIBRARIES = backends/vala/libvalashared.la
+
+valabackend_PROGRAMS = backends/vala/vala backends/vala/valahelper
+
+backends_vala_libvalashared_la_SOURCES =       \
+       backends/vala/types.vala                \
+       backends/vala/rpc.vala                  \
+       backends/vala/hashutils.vala
 
 backends_vala_vala_SOURCES =                   \
        backends/vala/application.vala          \
-       backends/vala/types.vala                \
        backends/vala/dbus.vala                 \
-       backends/vala/diagnostics.vala          \
        backends/vala/document.vala             \
-       backends/vala/service.vala
+       backends/vala/service.vala              \
+       backends/vala/makefileintegration.vala  \
+       backends/vala/config.vala
 
-backends_vala_vala_VALAFLAGS =                 \
+backends_vala_valahelper_SOURCES =             \
+       backends/vala/valaoptionparser.vala     \
+       backends/vala/diagnostics.vala          \
+       backends/vala/helper.vala
+
+backends_vala_common_valaflags =               \
        --target-glib=2.36                      \
        --pkg gio-2.0                           \
        --pkg gee-0.8                           \
        --pkg $(BACKEND_VALA_LIBVALA)
 
-backends_vala_vala_CFLAGS = $(BACKEND_VALA_CFLAGS) -w
-backends_vala_vala_LDADD = $(BACKEND_VALA_LIBS)
-
 if ENABLE_DEBUG
-backends_vala_vala_VALAFLAGS += --debug
+backends_vala_common_valaflags += --debug -X -O0
 endif
 
+backends_vala_libvalashared_la_VALAFLAGS =     \
+       $(backends_vala_common_valaflags)       \
+       --library libvalashared                 \
+       --vapi backends/vala/libvalashared.vapi \
+       --header backends/vala/libvalashared.h  \
+       --includedir backends/vala
+
+backends_vala_vala_VALAFLAGS =                 \
+       $(backends_vala_common_valaflags)       \
+       --pkg libvalashared                     \
+       --vapidir backends/vala
+
+backends_vala_valahelper_VALAFLAGS =           \
+       $(backends_vala_common_valaflags)       \
+       --pkg libvalashared                     \
+       --vapidir backends/vala
+
+backends_vala_libvalashared_la_CFLAGS = $(BACKEND_VALA_CFLAGS) -w
+
+backends_vala_vala_CFLAGS = $(BACKEND_VALA_CFLAGS) -w
+backends_vala_vala_LDADD = $(BACKEND_VALA_LIBS) backends/vala/libvalashared.la
+
+backends_vala_valahelper_CFLAGS = $(BACKEND_VALA_CFLAGS) -w
+backends_vala_valahelper_LDADD = $(BACKEND_VALA_LIBS) backends/vala/libvalashared.la
+
 valabackendservicedir = $(datadir)/dbus-1/services
 valabackendservice_DATA = \
        backends/vala/org.gnome.CodeAssist.v1.vala.service
 
-CLEANFILES +=                                  \
-       $(backends_vala_vala_SOURCES:.vala=.c)  \
-       backends_vala_vala_vala.stamp
+CLEANFILES +=                                                  \
+       $(backends_vala_vala_SOURCES:.vala=.c)                  \
+       $(backends_vala_valahelper_SOURCES:.vala=.c)            \
+       $(backends_vala_libvalashared_la_SOURCES:.vala=.c)      \
+       backends/vala/libvalashared.h                           \
+       backends/vala/libvalashared.vapi                        \
+       backends_vala_vala_vala.stamp                           \
+       backends_vala_valahelper_vala.stamp                     \
+       backends_vala_libvalashared_la_vala.stamp
 
 GITIGNOREFILES +=                              \
        backends/vala/$(DEPDIR)                 \
+       backends/vala/.libs                     \
        backends/vala/.dirstamp                 \
-       backends/vala/*.o
+       backends/vala/*.o                       \
+       backends/vala/*.lo
 
-EXTRA_DIST += $(valabackendservide_DATA)
+EXTRA_DIST += $(valabackendservice_DATA)
 
 GITIGNOREDEPS += backends/vala/Makefile.am
diff --git a/backends/vala/config.vala.in b/backends/vala/config.vala.in
new file mode 100644
index 0000000..40faea1
--- /dev/null
+++ b/backends/vala/config.vala.in
@@ -0,0 +1,26 @@
+/*
+ * This file is part of gnome-code-assistance.
+ *
+ * Copyright (C) 2013 - Melissa Wen <melissa srw gmail com>
+ * Copyright (C) 2013 - Jesse van den Kieboom <jessevdk gnome org>
+ *
+ * gnome-code-assistance is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * gnome-code-assistance is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with gnome-code-assistance.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace Config
+{
+       public static const string BackendExecDir = "@backendexecdir@";
+}
+
+/* vi:ex:ts=4 */
diff --git a/backends/vala/dbus.vala b/backends/vala/dbus.vala
index d0fd6dc..fbcac3a 100644
--- a/backends/vala/dbus.vala
+++ b/backends/vala/dbus.vala
@@ -69,9 +69,9 @@ public class ServiceIface : Object
                d_server = server;
        }
 
-       public ObjectPath parse(string path, string data_path, SourceLocation cursor, HashTable<string, 
Variant> options, GLib.BusName sender) throws Error
+       public async ObjectPath parse(string path, string data_path, SourceLocation cursor, HashTable<string, 
Variant> options, GLib.BusName sender) throws Error
        {
-               return d_server.parse(path, data_path, cursor, options, sender);
+               return yield d_server.parse(path, data_path, cursor, options, sender);
        }
 
        public new void dispose(string path, GLib.BusName sender)
@@ -80,6 +80,22 @@ public class ServiceIface : Object
        }
 }
 
+[DBus (name = "org.gnome.CodeAssist.v1.Project")]
+public class ProjectIface : Object
+{
+       private Server d_server;
+
+       public ProjectIface(Server server)
+       {
+               d_server = server;
+       }
+
+       public async RemoteDocument[] parse_all(string path, OpenDocument[] documents, SourceLocation cursor, 
HashTable<string, Variant> options, GLib.BusName sender) throws Error
+       {
+               return yield d_server.parse_all(path, documents, cursor, options, sender);
+       }
+}
+
 public class Server
 {
        class ExportedDocument
@@ -150,7 +166,10 @@ public class Server
        {
                if (newowner == "" && d_apps.has_key(oldowner))
                {
-                       dispose_app(d_apps[oldowner]);
+                       lock(d_apps)
+                       {
+                               dispose_app(d_apps[oldowner]);
+                       }
                }
        }
 
@@ -285,35 +304,79 @@ public class Server
                return (ObjectPath)("/org/gnome/CodeAssist/v1/vala/%u/documents/%u".printf(app.id, doc.id));
        }
 
-       public ObjectPath parse(string path, string data_path, SourceLocation cursor, HashTable<string, 
Variant> options, GLib.BusName sender) throws Error
+       public async ObjectPath parse(string path, string data_path, SourceLocation cursor, HashTable<string, 
Variant> options, GLib.BusName sender) throws Error
        {
-               var app = ensure_app(sender);
-               var doc = ensure_document(app, path, data_path, cursor);
+               App app;
+               ExportedDocument doc;
 
-               app.service.parse(doc.document, options);
+               lock(d_apps)
+               {
+                       app = ensure_app(sender);
+                       doc = ensure_document(app, path, data_path, cursor);
+               }
+
+               yield app.service.parse(doc.document, options);
 
                return remote_document_path(app, doc.document);
        }
 
        public new void dispose(string path, GLib.BusName sender)
        {
-               if (d_apps.has_key(sender))
+               lock(d_apps)
                {
-                       var app = d_apps[sender];
-                       var cpath = clean_path(path);
-
-                       if (app.docs.has_key(cpath))
+                       if (d_apps.has_key(sender))
                        {
-                               dispose_document(app, app.docs[cpath]);
-                               app.docs.unset(cpath);
+                               var app = d_apps[sender];
+                               var cpath = clean_path(path);
 
-                               if (app.docs.size == 0)
+                               if (app.docs.has_key(cpath))
                                {
-                                       dispose_app(app);
+                                       dispose_document(app, app.docs[cpath]);
+                                       app.docs.unset(cpath);
+
+                                       if (app.docs.size == 0)
+                                       {
+                                               dispose_app(app);
+                                       }
                                }
                        }
                }
        }
+
+       public async RemoteDocument[] parse_all(string path, OpenDocument[] documents, SourceLocation cursor, 
HashTable<string, Variant> options, GLib.BusName sender) throws Error
+       {
+               App app;
+               ExportedDocument doc;
+               Document[] opendocs;
+
+               lock(d_apps)
+               {
+                       app = ensure_app(sender);
+                       doc = ensure_document(app, path, "", cursor);
+
+                       opendocs = new Document[documents.length];
+
+                       for (var i = 0; i < documents.length; i++)
+                       {
+                               var rdoc = ensure_document(app, documents[i].path, documents[i].data_path);
+                               opendocs[i] = rdoc.document;
+                       }
+               }
+
+               var retdocs = yield app.service.parse_all(doc.document, opendocs, options);
+
+               var ret = new RemoteDocument[retdocs.length];
+
+               for (var i = 0; i < retdocs.length; i++)
+               {
+                       ret[i] = RemoteDocument() {
+                               path = retdocs[i].client_path,
+                               remote_path = remote_document_path(app, retdocs[i])
+                       };
+               }
+
+               return ret;
+       }
 }
 
 class Transport
@@ -333,6 +396,7 @@ class Transport
                try
                {
                        conn.register_object("/org/gnome/CodeAssist/v1/vala", new ServiceIface(d_server));
+                       conn.register_object("/org/gnome/CodeAssist/v1/vala", new ProjectIface(d_server));
 
                        conn.register_object("/org/gnome/CodeAssist/v1/vala/document", new DocumentIface());
                        conn.register_object("/org/gnome/CodeAssist/v1/vala/document", new 
DiagnosticsIface());
diff --git a/backends/vala/diagnostics.vala b/backends/vala/diagnostics.vala
index e9c7dff..8ee7492 100644
--- a/backends/vala/diagnostics.vala
+++ b/backends/vala/diagnostics.vala
@@ -22,17 +22,33 @@ using Vala;
 
 class Diagnostics : Report
 {
-       private Diagnostic[] d_diagnostics;
-       public string path { get; set; }
+       private Gee.HashMap<string, Gee.ArrayList<Diagnostic?>> d_diagnostics;
 
-       public Diagnostics(string path)
+       public Diagnostics()
        {
                base();
 
-               this.path = path;
+               d_diagnostics = new Gee.HashMap<string, Gee.ArrayList<Diagnostic?>>();
+       }
+
+       public Diagnostic[] diagnostics_for_path(string path)
+       {
+               Gee.ArrayList<Diagnostic?>? diagnostics = d_diagnostics[path];
+
+               if (diagnostics == null)
+               {
+                       return new Diagnostic[0];
+               }
+
+               var ret = new Diagnostic[diagnostics.size];
+               ret.length = 0;
+
+               foreach (var d in diagnostics)
+               {
+                       ret += d;
+               }
 
-               d_diagnostics = new Diagnostic[20];
-               d_diagnostics.length = 0;
+               return ret;
        }
 
        private void diags_report(SourceReference? source, string message, Severity severity)
@@ -42,9 +58,12 @@ class Diagnostics : Report
                        return;
                }
 
-               if (source.file.filename != path)
+               Gee.ArrayList<Diagnostic?>? diagnostics = d_diagnostics[source.file.filename];
+
+               if (diagnostics == null)
                {
-                       return;
+                       diagnostics = new Gee.ArrayList<Diagnostic?>();
+                       d_diagnostics[source.file.filename] = diagnostics;
                }
 
                var start = SourceLocation() {
@@ -71,17 +90,12 @@ class Diagnostics : Report
                        end = end
                };
 
-               d_diagnostics += Diagnostic() {
+               diagnostics.add(Diagnostic() {
                        severity = severity,
                        fixits = new Fixit[] {},
                        locations = new SourceRange[] {range},
                        message = message
-               };
-       }
-
-       public Diagnostic[] diagnostics
-       {
-               get { return d_diagnostics; }
+               });
        }
 
        public override void err(SourceReference? source, string message)
diff --git a/backends/vala/hashutils.vala b/backends/vala/hashutils.vala
new file mode 100644
index 0000000..04191b3
--- /dev/null
+++ b/backends/vala/hashutils.vala
@@ -0,0 +1,36 @@
+/*
+ * This file is part of gnome-code-assistance.
+ *
+ * Copyright (C) 2013 - Jesse van den Kieboom
+ *
+ * gnome-code-assistance is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * gnome-code-assistance is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with gnome-code-assistance.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+public class HashUtils
+{
+       public class File
+       {
+               public static uint hash(GLib.File f)
+               {
+                       return f.hash();
+               }
+
+               public static bool equal(GLib.File f1, GLib.File f2)
+               {
+                       return f1.equal(f2);
+               }
+       }
+}
+
+/* vi:ts=4:ex */
diff --git a/backends/vala/helper.vala b/backends/vala/helper.vala
new file mode 100644
index 0000000..e9b9b88
--- /dev/null
+++ b/backends/vala/helper.vala
@@ -0,0 +1,186 @@
+/*
+ * This file is part of gnome-code-assistance.
+ *
+ * Copyright (C) 2013 - Jesse van den Kieboom
+ *
+ * gnome-code-assistance is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * gnome-code-assistance is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with gnome-code-assistance.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+using Vala;
+
+class Helper
+{
+       static void add_to_context(CodeContext context, string path, string? data_path)
+       {
+               if (data_path == null || data_path == path)
+               {
+                       context.add_source_filename(path, false, false);
+                       return;
+               }
+
+               SourceFile? sf = null;
+
+               if (path.has_suffix(".vala") || path.has_suffix(".gs"))
+               {
+                       sf = new SourceFile(context, SourceFileType.SOURCE, path, null, false);
+                       var nsref = new UsingDirective(new UnresolvedSymbol(null, "GLib", null));
+
+                       sf.add_using_directive(nsref);
+                       context.root.add_using_directive(nsref);
+               }
+               else if (path.has_suffix(".vapi") || path.has_suffix(".gir"))
+               {
+                       sf = new SourceFile(context, SourceFileType.PACKAGE, path, null, false);
+               }
+
+               if (sf != null)
+               {
+                       try
+                       {
+                               string c;
+                               FileUtils.get_contents(data_path, out c);
+
+                               sf.content = c;
+                       } catch {}
+
+                       context.add_source_file(sf);
+               }
+       }
+
+       private static bool has_errors(ParserOptions opts)
+       {
+               var c = opts.context;
+               return c.report.get_errors() > 0 || (opts.fatal_warnings && c.report.get_warnings() > 0);
+       }
+
+       private static void parse(ParserOptions opts)
+       {
+               var parser = new Parser();
+               parser.parse(opts.context);
+
+               var genie_parser = new Genie.Parser();
+               genie_parser.parse(opts.context);
+
+               if (!has_errors(opts))
+               {
+                       opts.context.check();
+               }
+       }
+
+       private static void extract_diagnostics(ParserOptions opts, Rpc.Document[] docs)
+       {
+               var d = opts.context.report as Diagnostics;
+
+               for (int i = 0; i < docs.length; i++)
+               {
+                       docs[i].diagnostics = d.diagnostics_for_path(docs[i].path);
+               }
+       }
+
+       private static ParserOptions create_context(Rpc.Parse parse, out Rpc.Document[] rpcdocs)
+       {
+               var opts = OptionParser.parse_and_apply(".", parse.args);
+               var sources = OptionParser.real_sources(".");
+
+               var c = opts.context;
+               CodeContext.push(c);
+
+               var docs = new Gee.HashMap<File, OpenDocument?>(HashUtils.File.hash, HashUtils.File.equal);
+               var retdocs = new Rpc.Document[0];
+
+               foreach (var doc in parse.documents)
+               {
+                       var f = File.new_for_path(doc.path);
+                       docs[f] = doc;
+               }
+
+               foreach (var source in sources)
+               {
+                       var f = File.new_for_path(source);
+                       var doc = docs[f];
+
+                       if (doc != null)
+                       {
+                               add_to_context(c, doc.path, doc.data_path);
+
+                               retdocs += Rpc.Document() {
+                                       path = doc.path
+                               };
+                       }
+                       else
+                       {
+                               add_to_context(c, source, null);
+                       }
+               }
+
+               CodeContext.pop();
+
+               rpcdocs = retdocs;
+               return opts;
+       }
+
+       private static Rpc.Reply run(Rpc.Parse cmd)
+       {
+               Rpc.Document[] docs;
+
+               var opts = create_context(cmd, out docs);
+
+               parse(opts);
+
+               extract_diagnostics(opts, docs);
+
+               return Rpc.Reply() {
+                       documents = docs
+               };
+       }
+
+       private static uint8[] read_all()
+       {
+               uint8[] buffer = new uint8[4096];
+               uint8[] ret = new uint8[4096];
+               ret.length = 0;
+
+               while (!stdin.eof())
+               {
+                       var n = stdin.read(buffer);
+
+                       for (var i = 0; i < n; i++)
+                       {
+                               ret += buffer[i];
+                       }
+               }
+
+               return ret;
+       }
+
+       public static void main(string[] args)
+       {
+               Rpc.Parse p = Rpc.Parse();
+               Variant dummy = p;
+
+               var inp = read_all();
+               var parse = Variant.new_from_data<void>(dummy.get_type(), inp, true);
+               p = (Rpc.Parse)parse;
+
+               var reply = run(p);
+               Variant ret = reply;
+
+               uint8[] data = new uint8[(int)ret.get_size()];
+               ret.store((void *)data);
+
+               stdout.write(data);
+       }
+}
+
+/* vi:ex:ts=4 */
diff --git a/backends/vala/makefileintegration.vala b/backends/vala/makefileintegration.vala
index d69e67e..74df33f 100644
--- a/backends/vala/makefileintegration.vala
+++ b/backends/vala/makefileintegration.vala
@@ -19,16 +19,6 @@
 
 class MakefileIntegration
 {
-       static uint file_hash(File f)
-       {
-               return f.hash();
-       }
-
-       static bool file_equal(File f1, File f2)
-       {
-               return f1.equal(f2);
-       }
-
        class Makefile
        {
                class Source
@@ -45,7 +35,7 @@ class MakefileIntegration
                public Makefile(File file)
                {
                        d_file = file;
-                       d_sources = new Gee.HashMap<File, Source>(file_hash, file_equal);
+                       d_sources = new Gee.HashMap<File, Source>(HashUtils.File.hash, HashUtils.File.equal);
 
                        update_mtime();
 
@@ -172,8 +162,8 @@ class MakefileIntegration
 
        public MakefileIntegration()
        {
-               d_cache = new Gee.HashMap<File, Makefile>(file_hash, file_equal);
-               d_file_to_makefile = new Gee.HashMap<File, Makefile>(file_hash, file_equal);
+               d_cache = new Gee.HashMap<File, Makefile>(HashUtils.File.hash, HashUtils.File.equal);
+               d_file_to_makefile = new Gee.HashMap<File, Makefile>(HashUtils.File.hash, 
HashUtils.File.equal);
        }
 
        public bool changed_for_file(File f)
@@ -210,15 +200,19 @@ class MakefileIntegration
                }
        }
 
-       public string[]? flags_for_file(File f)
+       public string[]? flags_for_file(File f, out string? wd)
        {
                var makefile = makefile_for(f);
 
+               wd = null;
+
                if (makefile == null)
                {
                        return null;
                }
 
+               wd = makefile.get_parent().get_path();
+
                var m = d_cache[makefile];
 
                if (m != null)
@@ -523,7 +517,7 @@ class MakefileIntegration
                }
 
                string[] retargs;
-               var sargs = outstr[pos+fakevalac.length:epos-pos];
+               var sargs = outstr[pos:epos];
 
                try
                {
@@ -546,8 +540,8 @@ class MakefileIntegration
 #if MAIN
 public static int main(string[] a){
        MakefileIntegration it = new MakefileIntegration();
-
-       stdout.printf("Flags: %s\n", string.joinv(", ", 
it.flags_for_file(File.new_for_commandline_arg(a[1]))));
+       string wd;
+       stdout.printf("Flags: %s\n", string.joinv(", ", it.flags_for_file(File.new_for_commandline_arg(a[1]), 
out wd)));
        return 0;
 }
 #endif
diff --git a/backends/vala/rpc.vala b/backends/vala/rpc.vala
new file mode 100644
index 0000000..0f8bdf8
--- /dev/null
+++ b/backends/vala/rpc.vala
@@ -0,0 +1,21 @@
+namespace Rpc
+{
+       public struct Parse
+       {
+               public string[] args;
+               public OpenDocument[] documents;
+       }
+
+       public struct Document
+       {
+               public string path;
+               public Diagnostic[] diagnostics;
+       }
+
+       public struct Reply
+       {
+               public Document[] documents;
+       }
+}
+
+/* vi:ex:ts=4 */
diff --git a/backends/vala/service.vala b/backends/vala/service.vala
index aa4f2b7..f292d84 100644
--- a/backends/vala/service.vala
+++ b/backends/vala/service.vala
@@ -20,33 +20,216 @@
 
 using Vala;
 
-public class Service : Object
+public class Service
 {
-       public void parse(Document doc, HashTable<string, Variant> options) throws Error
+       private MakefileIntegration d_makefile;
+
+       public Service()
+       {
+               d_makefile = new MakefileIntegration();
+       }
+
+       private char[] read_all(IOChannel f)
+       {
+               char[] buffer = new char[4096];
+               char[] ret = new char[4096];
+               ret.length = 0;
+
+               while (true)
+               {
+                       size_t n;
+                       IOStatus st;
+
+                       try
+                       {
+                               st = f.read_chars(buffer, out n);
+                       }
+                       catch
+                       {
+                               st = IOStatus.ERROR;
+                               n = 0;
+                       }
+
+                       for (var i = 0; i < n; i++)
+                       {
+                               ret += buffer[i];
+                       }
+
+                       if (st == IOStatus.EOF || st == IOStatus.ERROR)
+                       {
+                               break;
+                       }
+               }
+
+               return ret;
+       }
+
+       private async Rpc.Reply spawn_helper(Document[] documents, string wd, string[] flags)
+       {
+               SourceFunc cb = spawn_helper.callback;
+               var argv = new string[] {Path.build_filename(Config.BackendExecDir, "valahelper")};
+
+               Pid pid;
+               int inp, outp;
+
+               try
+               {
+                       Process.spawn_async_with_pipes(wd, argv, null, 0, null, out pid, out inp, out outp, 
null);
+               }
+               catch (SpawnError e)
+               {
+                       log("GcaVala", LogLevelFlags.LEVEL_DEBUG, "Failed to spawn helper: %s", e.message);
+                       return Rpc.Reply();
+               }
+
+               var outstr = new IOChannel.unix_new(outp);
+               var instr = new IOChannel.unix_new(inp);
+
+               char[] retb = new char[0];
+
+               Thread<void*>? reader = null;
+               Thread<void*>? writer = null;
+
+               try
+               {
+                       reader = new Thread<void *>.try("reader", () => {
+                               retb = read_all(outstr);
+                               return null;
+                       });
+               }
+               catch (Error e)
+               {
+                       log("GcaVala", LogLevelFlags.LEVEL_DEBUG, "Failed to create reader thread: %s", 
e.message);
+
+                       try
+                       {
+                               outstr.shutdown(false);
+                       } catch {}
+               }
+
+               try
+               {
+                       writer = new Thread<void*>.try("writer", () => {
+                               var odocs = new OpenDocument[documents.length];
+
+                               for (int i = 0; i < documents.length; i++)
+                               {
+                                       odocs[i].path = documents[i].path;
+                                       odocs[i].data_path = documents[i].data_path;
+                               }
+
+                               var cmd = Rpc.Parse() {
+                                       args = flags,
+                                       documents = odocs
+                               };
+
+                               Variant vv = cmd;
+                               char[] data = new char[(int)vv.get_size()];
+
+                               vv.store((void *)data);
+
+                               try
+                               {
+                                       size_t n;
+                                       instr.write_chars(data, out n);
+                               } catch {}
+
+                               try
+                               {
+                                       instr.shutdown(true);
+                               } catch {}
+
+                               return null;
+                       });
+               }
+               catch (Error e)
+               {
+                       log("GcaVala", LogLevelFlags.LEVEL_DEBUG, "Failed to create writer thread: %s", 
e.message);
+
+                       try
+                       {
+                               instr.shutdown(false);
+                       } catch {}
+               }
+
+               ChildWatch.add(pid, (p, st) => {
+                       Process.close_pid(p);
+                       cb();
+               });
+
+               yield;
+
+               if (writer != null)
+               {
+                       writer.join();
+               }
+
+               if (reader != null)
+               {
+                       reader.join();
+               }
+
+               try
+               {
+                       outstr.shutdown(false);
+                       instr.shutdown(false);
+               } catch {}
+
+               Rpc.Reply r = Rpc.Reply();
+               Variant dummy = r;
+
+               var reply = Variant.new_from_data<void>(dummy.get_type(), (uchar[])retb, true);
+               return (Rpc.Reply)reply;
+       }
+
+       private async Document[] parse_impl(Document doc, Document[] documents, HashTable<string, Variant> 
options)
        {
-               CodeContext context = new CodeContext();
+               var f = File.new_for_path(doc.path);
 
-               var diags = new Diagnostics(doc.path);
-               context.report = diags;
+               string wd;
+               var flags = d_makefile.flags_for_file(f, out wd);
 
-               CodeContext.push(context);
+               var reply = yield spawn_helper(documents, wd, flags);
 
-               string source;
-               FileUtils.get_contents(doc.data_path, out source);
+               var odocs = new Gee.HashMap<string, Document>();
 
-               var sf = new SourceFile(context, SourceFileType.SOURCE, doc.path, source, true);
-               context.add_source_file(sf);
+               foreach (var d in documents)
+               {
+                       odocs[d.path] = doc;
+               }
 
-               Parser ast = new Parser();
-               ast.parse(context);
+               var retdocs = new Document[reply.documents.length];
+               retdocs.length = 0;
 
-               CodeContext.pop();
+               foreach (var d in reply.documents)
+               {
+                       var odoc = odocs[d.path];
 
-               doc.diagnostics = diags.diagnostics;
+                       if (odoc != null)
+                       {
+                               odoc.diagnostics = d.diagnostics;
+                               retdocs += odoc;
+                       }
+               }
+
+               return retdocs;
+       }
+
+       public async void parse(Document doc, HashTable<string, Variant> options)
+       {
+               yield parse_impl(doc, new Document[] {doc}, options);
+       }
+
+       public async Document[] parse_all(Document doc, Document[] documents, HashTable<string, Variant> 
options)
+       {
+               return yield parse_impl(doc, documents, options);
        }
 
        public new void dispose(Document document)
        {
+               var f = File.new_for_path(document.path);
+
+               d_makefile.dispose(f);
        }
 }
 
diff --git a/backends/vala/types.vala b/backends/vala/types.vala
index 2a485a2..50c8021 100644
--- a/backends/vala/types.vala
+++ b/backends/vala/types.vala
@@ -54,7 +54,7 @@ public struct Fixit
        public string replacement;
 }
 
-enum Severity
+public enum Severity
 {
        NONE,
        INFO,
@@ -72,4 +72,16 @@ public struct Diagnostic
        public string message;
 }
 
+public struct RemoteDocument
+{
+       public string path;
+       public ObjectPath remote_path;
+}
+
+public struct OpenDocument
+{
+       public string path;
+       public string data_path;
+}
+
 /* vi:ex:ts=4 */
diff --git a/backends/vala/valaoptionparser.vala b/backends/vala/valaoptionparser.vala
index f88d6f9..03766a2 100644
--- a/backends/vala/valaoptionparser.vala
+++ b/backends/vala/valaoptionparser.vala
@@ -23,8 +23,15 @@
  */
 
 using GLib;
+using Vala;
 
-class Vala.OptionParser {
+struct ParserOptions
+{
+       public CodeContext context;
+       public bool fatal_warnings;
+}
+
+class OptionParser {
        static string basedir;
        static string directory;
        static bool version;
@@ -140,7 +147,7 @@ class Vala.OptionParser {
                { null }
        };
 
-       private static bool apply (global::Vala.CodeContext context, string cwd) {
+       private static ParserOptions apply (CodeContext context, string[] args, string wd) {
                context.assert = !disable_assert;
                context.checking = enable_checking;
                context.deprecated = deprecated;
@@ -164,18 +171,18 @@ class Vala.OptionParser {
                context.includedir = includedir;
                context.output = output;
                if (basedir == null) {
-                       context.basedir = CodeContext.realpath (cwd);
+                       context.basedir = wd;
                } else {
-                       context.basedir = CodeContext.realpath (basedir);
+                       context.basedir = realpath (wd, basedir);
                }
                if (directory != null) {
-                       context.directory = CodeContext.realpath (directory);
+                       context.directory = realpath (wd, directory);
                } else {
                        context.directory = context.basedir;
                }
-               context.vapi_directories = vapi_directories;
-               context.gir_directories = gir_directories;
-               context.metadata_directories = metadata_directories;
+               context.vapi_directories = realpaths(wd, vapi_directories);
+               context.gir_directories = realpaths(wd, gir_directories);
+               context.metadata_directories = realpaths(wd, metadata_directories);
                context.debug = debug;
                context.thread = thread;
                context.mem_profiler = mem_profiler;
@@ -235,34 +242,142 @@ class Vala.OptionParser {
 
                if (fast_vapis != null) {
                        foreach (string vapi in fast_vapis) {
-                               var rpath = CodeContext.realpath (vapi);
+                               var rpath = realpath (wd, vapi);
                                var source_file = new SourceFile (context, SourceFileType.FAST, rpath);
                                context.add_source_file (source_file);
                        }
                        context.use_fast_vapi = true;
                }
 
-               return (context.report.get_errors () > 0 || (fatal_warnings && context.report.get_warnings () 
0));
+               return ParserOptions() {
+                       fatal_warnings = fatal_warnings,
+                       context = context
+               };
+       }
+
+       public static string[] real_sources(string wd)
+       {
+               return realpaths(wd, sources);
+       }
+
+       private static string[] realpaths(string wd, string[] paths)
+       {
+               var ret = new string[paths.length];
+
+               for (var i = 0; i < paths.length; i++)
+               {
+                       ret[i] = realpath(wd, paths[i]);
+               }
+
+               return ret;
+       }
+
+       private static string realpath(string wd, string path)
+       {
+               var rpath = path;
+
+               if (!Path.is_absolute(path))
+               {
+                       rpath = Path.build_filename(wd, rpath);
+               }
+
+               return File.new_for_path(rpath).get_path();
+       }
+
+       private static void clear()
+       {
+               basedir = null;
+               directory = null;
+               version = false;
+               api_version = false;
+               sources = null;
+               vapi_directories = null;
+               gir_directories = null;
+               metadata_directories = null;
+               vapi_filename = null;
+               library = null;
+               gir = null;
+               packages = null;
+               fast_vapis = null;
+               target_glib = null;
+               gresources = null;
+
+               ccode_only = false;
+               header_filename = null;
+               use_header = false;
+               internal_header_filename = null;
+               internal_vapi_filename = null;
+               fast_vapi_filename = null;
+               symbols_filename = null;
+               includedir = null;
+               compile_only = false;
+               output = null;
+               debug = false;
+               thread = false;
+               mem_profiler = false;
+               disable_assert = false;
+               enable_checking = false;
+               deprecated = false;
+               experimental = false;
+               experimental_non_null = false;
+               gobject_tracing = false;
+               disable_warnings = false;
+               cc_command = null;
+               cc_options = null;
+               dump_tree = null;
+               save_temps = false;
+               defines = null;
+               quiet_mode = false;
+               verbose_mode = false;
+               profile = null;
+               nostdpkg = false;
+               enable_version_header = false;
+               disable_version_header = false;
+               fatal_warnings = false;
+               dependencies = null;
+
+               entry_point = null;
+               run_output = false;
        }
 
        public static bool parse(string[] args) {
+               string[] myargs = args;
+
                try {
+                       unowned string[] unargs = myargs;
+
+                       clear();
+
                        var opt_context = new OptionContext ("- Vala Compiler");
                        opt_context.set_help_enabled (false);
                        opt_context.add_main_entries (options, null);
                        opt_context.set_ignore_unknown_options (true);
-                       opt_context.parse (ref args);
-               } catch { return false; }
+                       opt_context.parse (ref unargs);
+               }
+               catch (Error e)
+               {
+                       log("GcaVala",
+                           LogLevelFlags.LEVEL_WARNING,
+                           "Failed to parse flags `%s': %s",
+                           string.joinv(", ", args), e.message);
+
+                       clear();
+                       return false;
+               }
 
                return true;
        }
 
-       public static bool parse_and_apply(string cwd, global::Vala.CodeContext context, string[] args) {
-               if (!parse(args))
-               {
-                       return false;
-               }
+       public static ParserOptions parse_and_apply(string wd, string[] args) {
+               parse(args);
+
+               var context = new CodeContext();
+               context.report = new Diagnostics();
+
+               CodeContext.push(context);
+               var ret = apply(context, args, wd);
+               CodeContext.pop();
 
-               return apply(context, cwd);
+               return ret;
        }
 }
diff --git a/configure.ac b/configure.ac
index 29287db..75efaa1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -586,6 +586,7 @@ backends/ruby/ruby
 backends/xml/org.gnome.CodeAssist.v1.xml.service
 backends/xml/xml
 backends/vala/org.gnome.CodeAssist.v1.vala.service
+backends/vala/config.vala
 backends/go/org.gnome.CodeAssist.v1.go.service
 backends/js/org.gnome.CodeAssist.v1.js.service
 backends/js/js


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]