From 1cfc3f2d81ba330d0ae1552a2f2e8c9f8ee465d8 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 18 May 2017 10:28:55 -0400
Subject: [PATCH 08/22] FIXME: initial dummy implementation of listening on a
 port

---
 gcc/Makefile.in          |   2 +
 gcc/common.opt           |   4 +
 gcc/http-server.c        | 259 +++++++++++++++++++++++++++++++++++++++++++++++
 gcc/http-server.h        |  55 ++++++++++
 gcc/json-rpc.c           |  13 +++
 gcc/json-rpc.h           |   1 +
 gcc/json.c               |  26 ++++-
 gcc/json.h               |   1 +
 gcc/lsp-main.c           | 103 +++++++++++++++++++
 gcc/lsp.h                |  26 +++++
 gcc/selftest-run-tests.c |   1 +
 gcc/selftest.h           |   1 +
 gcc/toplev.c             |   4 +
 13 files changed, 495 insertions(+), 1 deletion(-)
 create mode 100644 gcc/http-server.c
 create mode 100644 gcc/http-server.h
 create mode 100644 gcc/lsp-main.c
 create mode 100644 gcc/lsp.h

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 046a7c2..38677e6 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1326,6 +1326,7 @@ OBJS = \
 	hsa-regalloc.o \
 	hsa-brig.o \
 	hsa-dump.o \
+	http-server.o \
 	hw-doloop.o \
 	hwint.o \
 	ifcvt.o \
@@ -1374,6 +1375,7 @@ OBJS = \
 	loop-unroll.o \
 	lower-subreg.o \
 	lsp.o \
+	lsp-main.o \
 	lra.o \
 	lra-assigns.o \
 	lra-coalesce.o \
diff --git a/gcc/common.opt b/gcc/common.opt
index 4021622..05a47a4 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1742,6 +1742,10 @@ flra-remat
 Common Report Var(flag_lra_remat) Optimization
 Do CFG-sensitive rematerialization in LRA.
 
+flsp=
+Common Joined RejectNegative UInteger Var(flag_lsp) Init(-1)
+-flsp=<port-number>  FIXME.
+
 flto
 Common
 Enable link-time optimization.
diff --git a/gcc/http-server.c b/gcc/http-server.c
new file mode 100644
index 0000000..1e1e8c8
--- /dev/null
+++ b/gcc/http-server.c
@@ -0,0 +1,259 @@
+/* HTTP server implementation.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "http-server.h"
+#include "selftest.h"
+
+/* adapted from GNU libc docs.  */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#define MAXMSG  512
+
+static int
+make_socket (uint16_t port)
+{
+  int sock;
+  struct sockaddr_in name;
+
+  /* Create the socket. */
+  sock = socket (PF_INET, SOCK_STREAM, 0);
+  if (sock < 0)
+    {
+      perror ("socket");
+      exit (EXIT_FAILURE);
+    }
+
+  /* Give the socket a name. */
+  name.sin_family = AF_INET;
+  name.sin_port = htons (port);
+  name.sin_addr.s_addr = htonl (INADDR_ANY);
+  if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
+    {
+      perror ("bind");
+      exit (EXIT_FAILURE);
+    }
+
+  return sock;
+}
+
+/* Serve LSP on PORT.  */
+
+void
+server::serve (int port)
+{
+  int sock;
+  fd_set active_fd_set, read_fd_set;
+  int i;
+  struct sockaddr_in clientname;
+  socklen_t size;
+
+  /* Create the socket and set it up to accept connections. */
+  sock = make_socket (port);
+  if (listen (sock, 1) < 0)
+    {
+      perror ("listen");
+      exit (EXIT_FAILURE);
+    }
+
+  /* Initialize the set of active sockets. */
+  FD_ZERO (&active_fd_set);
+  FD_SET (sock, &active_fd_set);
+
+  while (1)
+    {
+      /* Block until input arrives on one or more active sockets. */
+      read_fd_set = active_fd_set;
+      if (select (FD_SETSIZE, &read_fd_set, NULL, NULL, NULL) < 0)
+	{
+	  perror ("select");
+	  exit (EXIT_FAILURE);
+	}
+
+      /* Service all the sockets with input pending. */
+      for (i = 0; i < FD_SETSIZE; ++i)
+	if (FD_ISSET (i, &read_fd_set))
+	  {
+	    if (i == sock)
+	      {
+		/* Connection request on original socket. */
+		int new_;
+		size = sizeof (clientname);
+		new_ = accept (sock,
+			       (struct sockaddr *) &clientname,
+			       &size);
+		if (new_ < 0)
+		  {
+		    perror ("accept");
+		    exit (EXIT_FAILURE);
+		  }
+		fprintf (stderr,
+			 "Server: connect from host %s, port %hd.\n",
+			 inet_ntoa (clientname.sin_addr),
+			 ntohs (clientname.sin_port));
+		FD_SET (new_, &active_fd_set);
+	      }
+	    else
+	      {
+		/* Data arriving on an already-connected socket. */
+		if (read_from_client (i) < 0)
+		  {
+		    close (i);
+		    FD_CLR (i, &active_fd_set);
+		  }
+	      }
+	  }
+    }
+}
+
+int
+server::read_from_client (int filedes)
+{
+  char buffer[MAXMSG];
+  int nbytes;
+
+  nbytes = read (filedes, buffer, MAXMSG);
+  if (nbytes < 0)
+    {
+      /* Read error. */
+      perror ("read");
+      exit (EXIT_FAILURE);
+    }
+  else if (nbytes == 0)
+    /* End-of-file. */
+    return -1;
+  else
+    {
+      /* Data read. */
+      on_read (nbytes, buffer);
+      return 0;
+    }
+}
+
+void
+httpish_server::on_read (size_t sz, const char *buf)
+{
+  fprintf (stderr, "httpish_server::on_read: `%s'\n", buf); // FIXME: respect sz
+  size_t line_start = 0;
+  while (line_start < sz)
+    {
+      for (size_t i = line_start; i + 1 < sz; i++)
+	{
+	  if (buf[i] == '\r' && buf[i + 1] == '\n')
+	    {
+	      if (i == line_start)
+		{
+		  line_start = i + 2;
+		  // FIXME: respect the "Content-Length" header
+		  on_content (sz - line_start, buf + line_start);
+		  //line_start += ;
+		  return;
+		  //break;
+		}
+	      else
+		{
+		  parse_header (i - line_start, buf + line_start);
+		  line_start = i + 2;
+		  break;
+		}
+	    }
+	}
+    }
+}
+
+void
+httpish_server::parse_header (size_t sz, const char *buf)
+{
+  for (size_t colon_idx = 0; colon_idx + 1 < sz; colon_idx++)
+    if (buf[colon_idx] == ':' && buf[colon_idx + 1] == ' ')
+      {
+	on_header (colon_idx, buf, sz - (colon_idx + 2), buf + colon_idx +2);
+	return;
+      }
+  // FIXME: error-handling
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+namespace {
+
+class test_server : public httpish_server
+{
+ public:
+  test_server () : m_user_agent (NULL), m_content (NULL) {}
+  ~test_server () { free (m_user_agent); free (m_content); }
+
+  void on_header (size_t key_sz, const char *key,
+		  size_t value_sz, const char *value) FINAL OVERRIDE
+  {
+    if (key_sz == strlen ("User-Agent")
+	&& 0 == strncmp (key, "User-Agent", key_sz))
+      m_user_agent = xstrndup (value, value_sz);
+  }
+
+  void on_content (size_t sz, const char *buf) FINAL OVERRIDE
+  {
+    m_content = xstrndup (buf, sz);
+  }
+
+  char *m_user_agent;
+  char *m_content;
+};
+
+} // anonymous namespace
+
+/* Selftests.  */
+
+static void
+test_http_server ()
+{
+  test_server s;
+  const char *in = ("POST /jsonrpc HTTP/1.1\r\n"
+		    "Host: localhost:4000\r\n"
+		    "Content-Length: 12\r\n"
+		    "content-type: application/json\r\n"
+		    "Accept-Encoding: gzip, deflate, compress\r\n"
+		    "Accept: */*\r\n"
+		    "User-Agent: test-user-agent\r\n"
+		    "\r\n"
+		    "test-content");
+  s.on_read (strlen (in), in);
+  ASSERT_STREQ ("test-user-agent", s.m_user_agent);
+  ASSERT_STREQ ("test-content", s.m_content);
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+http_server_c_tests ()
+{
+  test_http_server ();
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/http-server.h b/gcc/http-server.h
new file mode 100644
index 0000000..233f194
--- /dev/null
+++ b/gcc/http-server.h
@@ -0,0 +1,55 @@
+/* HTTP server implementation.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_HTTP_SERVER_H
+#define GCC_HTTP_SERVER_H
+
+/* FIXME.  */
+
+class server
+{
+ public:
+  virtual ~server () {}
+  void serve (int port);
+
+  virtual void on_read (size_t sz, const char *buf) = 0;
+
+ private:
+  int read_from_client (int filedes);
+};
+
+/* Subclass of server than expects an HTTP-like protocol,
+   with header lines ended by '\r\n', then a '\r\n' line, then
+   the content.  */
+
+class httpish_server : public server
+{
+ public:
+  void on_read (size_t sz, const char *buf) OVERRIDE FINAL;
+
+  virtual void on_header (size_t key_sz, const char *key,
+			  size_t value_sz, const char *value) = 0;
+  virtual void on_content (size_t sz, const char *buf) = 0;
+
+ private:
+  void parse_header (size_t sz, const char *buf);
+};
+
+#endif  /* GCC_HTTP_SERVER_H  */
+
diff --git a/gcc/json-rpc.c b/gcc/json-rpc.c
index f437814..c92b2bb 100644
--- a/gcc/json-rpc.c
+++ b/gcc/json-rpc.c
@@ -144,6 +144,15 @@ jsonrpc::server::handle_request_1 (json::value *request)
 }
 
 json::value *
+jsonrpc::server::handle_request (size_t sz, const char *buf)
+{
+  json::value *request = json::parse_utf8_string (sz, buf);
+  json::value *response = handle_request (request);
+  delete request;
+  return response;
+}
+
+json::value *
 jsonrpc::server::handle_request_string (const char *utf8)
 {
   json::value *request = json::parse_utf8_string (utf8);
@@ -204,6 +213,8 @@ assert_is_failure (const json::value *response, int code, const char *message,
 
 using namespace jsonrpc;
 
+namespace {
+
 class test_server : public jsonrpc::server
 {
  public:
@@ -240,6 +251,8 @@ class test_server : public jsonrpc::server
   }
 };
 
+} // anonymous namespace
+
 static void
 test_simple ()
 {
diff --git a/gcc/json-rpc.h b/gcc/json-rpc.h
index 2f1b8b8..b1bdb10 100644
--- a/gcc/json-rpc.h
+++ b/gcc/json-rpc.h
@@ -29,6 +29,7 @@ class server
   server (bool verbose) : m_verbose (verbose) {}
   virtual ~server () {}
   json::value *handle_request (json::value *);
+  json::value *handle_request (size_t sz, const char *buf);
   json::value *handle_request_string (const char *utf8);
 
   virtual json::value *dispatch (const char *method,
diff --git a/gcc/json.c b/gcc/json.c
index dff0d7c..400a636 100644
--- a/gcc/json.c
+++ b/gcc/json.c
@@ -218,6 +218,7 @@ class lexer
 {
  public:
   lexer ();
+  void add_utf8 (size_t sz, const char *utf8_buf);
   void add_utf8 (const char *utf8);
 
   const token *peek ();
@@ -244,6 +245,7 @@ class parser
 {
  public:
   parser ();
+  void add_utf8 (size_t sz, const char *utf8_buf);
   void add_utf8 (const char *utf8);
   value *parse_value ();
   object *parse_object ();
@@ -291,7 +293,7 @@ lexer::consume ()
   gcc_assert (m_num_next_tokens > 0);
   gcc_assert (m_num_next_tokens <= MAX_TOKENS);
 
-  if (0)
+  if (1)
     {
       fprintf (stderr, "consuming token: ");
       dump_token (stderr, &m_next_tokens[0]);
@@ -307,6 +309,14 @@ lexer::consume ()
 }
 
 void
+lexer::add_utf8 (size_t sz, const char *utf8_buf)
+{
+  // FIXME: this assumes Latin-1.
+  for (size_t i = 0; i < sz; i++)
+    m_buffer.safe_push (utf8_buf[sz]);
+}
+
+void
 lexer::add_utf8 (const char *utf8)
 {
   // FIXME: this assumes Latin-1.
@@ -562,6 +572,12 @@ parser::parser ()
 }
 
 void
+parser::add_utf8 (size_t sz, const char *utf8_buf)
+{
+  m_lexer.add_utf8 (sz, utf8_buf);
+}
+
+void
 parser::add_utf8 (const char *utf8)
 {
   m_lexer.add_utf8 (utf8);
@@ -693,6 +709,14 @@ parser::error (const char *msg)
 
 
 value *
+json::parse_utf8_string (size_t sz, const char *utf8_buf)
+{
+  parser p;
+  p.add_utf8 (sz, utf8_buf);
+  return p.parse_value ();
+}
+
+value *
 json::parse_utf8_string (const char *utf8)
 {
   parser p;
diff --git a/gcc/json.h b/gcc/json.h
index e094446..0545068 100644
--- a/gcc/json.h
+++ b/gcc/json.h
@@ -148,6 +148,7 @@ class literal : public value
 
 /* Parsing decls.  */
 
+extern value *parse_utf8_string (size_t sz, const char *utf8_buf);
 extern value *parse_utf8_string (const char *utf8);
 
 } // namespace json
diff --git a/gcc/lsp-main.c b/gcc/lsp-main.c
new file mode 100644
index 0000000..4c2ab16
--- /dev/null
+++ b/gcc/lsp-main.c
@@ -0,0 +1,103 @@
+/* Language Server Protocol implementation.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "http-server.h"
+#include "json.h"
+#include "json-rpc.h"
+#include "selftest.h"
+
+class jsonrpc_server : public httpish_server
+{
+ public:
+  jsonrpc_server (jsonrpc::server &handler) : m_handler (handler) {}
+
+  void on_header (size_t key_sz, const char *key,
+		  size_t value_sz, const char *value) OVERRIDE FINAL
+  {
+    fprintf (stderr, "got header: key: '%.*s' value: '%.*s'\n",
+	     (int)key_sz, key, (int)value_sz, value);
+  }
+
+  void on_content (size_t sz, const char *buf) OVERRIDE FINAL
+  {
+    fprintf (stderr, "got content: '%.*s'\n", (int)sz, buf);
+    json::value *response = m_handler.handle_request (sz, buf);
+    // FIXME: send the response back to the client
+  }
+
+ private:
+  jsonrpc::server &m_handler;
+};
+
+// FIXME: copy of test server...
+
+using namespace jsonrpc;
+
+namespace {
+
+class test_server : public jsonrpc::server
+{
+ public:
+  test_server (bool verbose) : server (verbose) {}
+
+  json::value *
+  dispatch (const char *method, const json::value *params,
+	    const json::value *id) FINAL OVERRIDE
+  {
+    if (0 == strcmp (method, "subtract"))
+      return do_subtract (params, id);
+    return make_method_not_found (id, method);
+  }
+
+  json::value *
+  do_subtract (const json::value *params, const json::value *id)
+  {
+    const json::array *positional = params->as_array ();
+    if (!positional)
+      return make_invalid_params (id, "params was not an array");
+    if (positional->get_length () != 2)
+      return make_invalid_params (id, "length of params was not 2");
+
+    const json::number *arg0 = positional->get (0)->as_number ();
+    if (!arg0)
+      return make_invalid_params (id, "param 0 was not a number");
+    const json::number *arg1 = positional->get (1)->as_number ();
+    if (!arg1)
+      return make_invalid_params (id, "param 1 was not a number");
+
+    double result = arg0->get () - arg1->get ();
+
+    return make_success (id, new json::number (result));
+  }
+};
+
+} // anonymous namespace
+
+/* Serve LSP on PORT.  */
+
+void
+serve_lsp (int port)
+{
+  test_server ts (true);
+  jsonrpc_server s (ts);
+  s.serve (port);
+}
diff --git a/gcc/lsp.h b/gcc/lsp.h
new file mode 100644
index 0000000..6b04025
--- /dev/null
+++ b/gcc/lsp.h
@@ -0,0 +1,26 @@
+/* Language Server Protocol implementation.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_LSP_H
+#define GCC_LSP_H
+
+extern void serve_lsp (int port);
+
+#endif  /* GCC_LSP_H  */
+
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index 5a11148..a84c53b 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -66,6 +66,7 @@ selftest::run_tests ()
   sreal_c_tests ();
   fibonacci_heap_c_tests ();
   typed_splay_tree_c_tests ();
+  http_server_c_tests ();
   json_c_tests ();
   json_rpc_c_tests ();
   lsp_c_tests ();
diff --git a/gcc/selftest.h b/gcc/selftest.h
index 8ce0f27..2e266c4 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -182,6 +182,7 @@ extern void gimple_c_tests ();
 extern void ggc_tests_c_tests ();
 extern void hash_map_tests_c_tests ();
 extern void hash_set_tests_c_tests ();
+extern void http_server_c_tests ();
 extern void input_c_tests ();
 extern void json_c_tests ();
 extern void json_rpc_c_tests ();
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 54a4f05..681bda8 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -79,6 +79,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "omp-offload.h"
 #include "hsa-common.h"
 #include "edit-context.h"
+#include "lsp.h"
 
 #if defined(DBX_DEBUGGING_INFO) || defined(XCOFF_DEBUGGING_INFO)
 #include "dbxout.h"
@@ -469,6 +470,9 @@ compile_file (void)
   timevar_pop (TV_PARSE_GLOBAL);
   timevar_stop (TV_PHASE_PARSING);
 
+  if (flag_lsp != -1)
+    serve_lsp (flag_lsp);
+
   if (flag_dump_locations)
     dump_location_info (stderr);
 
-- 
1.8.5.3

