From b1dc7fae2a95804948ba9eedca08d208cdd5f825 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Wed, 26 May 2010 12:19:58 +0200 Subject: Initial import of spice protocol description and demarshall generator The "spice.proto" file describes in detail the networking prototcol that spice uses and spice_codegen.py can parse this and generate demarshallers for such network messages. --- python_modules/codegen.py | 354 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 354 insertions(+) create mode 100644 python_modules/codegen.py (limited to 'python_modules/codegen.py') diff --git a/python_modules/codegen.py b/python_modules/codegen.py new file mode 100644 index 0000000..5bb659a --- /dev/null +++ b/python_modules/codegen.py @@ -0,0 +1,354 @@ +from cStringIO import StringIO + +def camel_to_underscores(s, upper = False): + res = "" + for i in range(len(s)): + c = s[i] + if i > 0 and c.isupper(): + res = res + "_" + if upper: + res = res + c.upper() + else: + res = res + c.lower() + return res + +def underscores_to_camel(s): + res = "" + do_upper = True + for i in range(len(s)): + c = s[i] + if c == "_": + do_upper = True + else: + if do_upper: + res = res + c.upper() + else: + res = res + c + do_upper = False + return res + +proto_prefix = "Temp" + +def set_prefix(prefix): + global proto_prefix + global proto_prefix_upper + global proto_prefix_lower + proto_prefix = prefix + proto_prefix_upper = prefix.upper() + proto_prefix_lower = prefix.lower() + +def prefix_underscore_upper(*args): + s = proto_prefix_upper + for arg in args: + s = s + "_" + arg + return s + +def prefix_underscore_lower(*args): + s = proto_prefix_lower + for arg in args: + s = s + "_" + arg + return s + +def prefix_camel(*args): + s = proto_prefix + for arg in args: + s = s + underscores_to_camel(arg) + return s + +def increment_identifier(idf): + v = idf[-1:] + if v.isdigit(): + return idf[:-1] + str(int(v) + 1) + return idf + "2" + +def sum_array(array): + if len(array) == 0: + return 0 + return " + ".join(array) + +class CodeWriter: + def __init__(self): + self.out = StringIO() + self.contents = [self.out] + self.indentation = 0 + self.at_line_start = True + self.indexes = ["i", "j", "k", "ii", "jj", "kk"] + self.current_index = 0 + self.generated = {} + self.vars = [] + self.has_error_check = False + self.options = {} + self.function_helper_writer = None + + def set_option(self, opt, value = True): + self.options[opt] = value + + def has_option(self, opt): + return self.options.has_key(opt) + + def set_is_generated(self, kind, name): + if not self.generated.has_key(kind): + v = {} + self.generated[kind] = v + else: + v = self.generated[kind] + v[name] = 1 + + def is_generated(self, kind, name): + if not self.generated.has_key(kind): + return False + v = self.generated[kind] + return v.has_key(name) + + def getvalue(self): + strs = map(lambda writer: writer.getvalue(), self.contents) + return "".join(strs) + + def get_subwriter(self): + writer = CodeWriter() + self.contents.append(writer) + self.out = StringIO() + self.contents.append(self.out) + writer.indentation = self.indentation + writer.at_line_start = self.at_line_start + writer.generated = self.generated + writer.options = self.options + + return writer; + + def write(self, s): + # Ensure its a string + s = str(s) + + if len(s) == 0: + return + + if self.at_line_start: + for i in range(self.indentation): + self.out.write(" ") + self.at_line_start = False + self.out.write(s) + return self + + def newline(self): + self.out.write("\n") + self.at_line_start = True + return self + + def writeln(self, s): + self.write(s) + self.newline() + return self + + def label(self, s): + self.indentation = self.indentation - 1 + self.write(s + ":") + self.indentation = self.indentation + 1 + self.newline() + + def statement(self, s): + self.write(s) + self.write(";") + self.newline() + return self + + def assign(self, var, val): + self.write("%s = %s" % (var, val)) + self.write(";") + self.newline() + return self + + def increment(self, var, val): + self.write("%s += %s" % (var, val)) + self.write(";") + self.newline() + return self + + def comment(self, str): + self.write("/* " + str + " */") + return self + + def todo(self, str): + self.comment("TODO: *** %s ***" % str).newline() + return self + + def error_check(self, check, label = "error"): + self.has_error_check = True + with self.block("if (SPICE_UNLIKELY(%s))" % check): + if self.has_option("print_error"): + self.statement('printf("%%s: Caught error - %s", __PRETTY_FUNCTION__)' % check) + if self.has_option("assert_on_error"): + self.statement("assert(0)") + self.statement("goto %s" % label) + + def indent(self): + self.indentation += 4; + + def unindent(self): + self.indentation -= 4; + if self.indentation < 0: + self.indenttation = 0 + + def begin_block(self, prefix= "", comment = ""): + if len(prefix) > 0: + self.write(prefix) + if self.at_line_start: + self.write("{") + else: + self.write(" {") + if len(comment) > 0: + self.write(" ") + self.comment(comment) + self.newline() + self.indent() + + def end_block(self, semicolon=False, newline=True): + self.unindent() + if self.at_line_start: + self.write("}") + else: + self.write(" }") + if semicolon: + self.write(";") + if newline: + self.newline() + + class Block: + def __init__(self, writer, semicolon, newline): + self.writer = writer + self.semicolon = semicolon + self.newline = newline + + def __enter__(self): + return self.writer.get_subwriter() + + def __exit__(self, exc_type, exc_value, traceback): + self.writer.end_block(self.semicolon, self.newline) + + class PartialBlock: + def __init__(self, writer, scope, semicolon, newline): + self.writer = writer + self.scope = scope + self.semicolon = semicolon + self.newline = newline + + def __enter__(self): + return self.scope + + def __exit__(self, exc_type, exc_value, traceback): + self.writer.end_block(self.semicolon, self.newline) + + class NoBlock: + def __init__(self, scope): + self.scope = scope + + def __enter__(self): + return self.scope + + def __exit__(self, exc_type, exc_value, traceback): + pass + + def block(self, prefix= "", comment = "", semicolon=False, newline=True): + self.begin_block(prefix, comment) + return self.Block(self, semicolon, newline) + + def partial_block(self, scope, semicolon=False, newline=True): + return self.PartialBlock(self, scope, semicolon, newline) + + def no_block(self, scope): + return self.NoBlock(scope) + + def optional_block(self, scope): + if scope != None: + return self.NoBlock(scope) + return self.block() + + def for_loop(self, index, limit): + return self.block("for (%s = 0; %s < %s; %s++)" % (index, index, limit, index)) + + def while_loop(self, expr): + return self.block("while (%s)" % (expr)) + + def if_block(self, check, elseif=False, newline=True): + s = "if (%s)" % (check) + if elseif: + s = " else " + s + self.begin_block(s, "") + return self.Block(self, False, newline) + + def variable_defined(self, name): + for n in self.vars: + if n == name: + return True + return False + + def variable_def(self, ctype, *names): + for n in names: + # Strip away initialization + i = n.find("=") + if i != -1: + n = n[0:i] + self.vars.append(n.strip()) + # only add space for non-pointer types + if ctype[-1] == "*": + ctype = ctype[:-1].rstrip() + self.writeln("%s *%s;"%(ctype, ", *".join(names))) + else: + self.writeln("%s %s;"%(ctype, ", ".join(names))) + return self + + def function_helper(self): + if self.function_helper_writer != None: + writer = self.function_helper_writer.get_subwriter() + self.function_helper_writer.newline() + else: + writer = self.get_subwriter() + return writer + + def function(self, name, return_type, args, static = False): + self.has_error_check = False + self.function_helper_writer = self.get_subwriter() + if static: + self.write("static ") + self.write(return_type) + self.write(" %s(%s)"% (name, args)).newline() + self.begin_block() + self.function_variables_writer = self.get_subwriter() + self.function_variables = {} + return self.function_variables_writer + + def macro(self, name, args, define): + self.write("#define %s(%s) %s" % (name, args, define)).newline() + + def add_function_variable(self, ctype, name): + if self.function_variables.has_key(name): + assert(self.function_variables[name] == ctype) + else: + self.function_variables[name] = ctype + self.function_variables_writer.variable_def(ctype, name) + + def pop_index(self): + index = self.indexes[self.current_index] + self.current_index = self.current_index + 1 + self.add_function_variable("uint32_t", index) + return index + + def push_index(self): + self.current_index = self.current_index - 1 + + class Index: + def __init__(self, writer, val): + self.writer = writer + self.val = val + + def __enter__(self): + return self.val + + def __exit__(self, exc_type, exc_value, traceback): + self.writer.push_index() + + def index(self, no_block = False): + if no_block: + return self.no_block(None) + val = self.pop_index() + return self.Index(self, val) -- cgit From 2523cec8c4890f977ee5569d9280d54108790674 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Tue, 22 Jun 2010 16:01:57 +0200 Subject: Support extra prefix in code generators This is require when we add a new spice.proto for the old (major 1) protocol description. --- python_modules/codegen.py | 1 + 1 file changed, 1 insertion(+) (limited to 'python_modules/codegen.py') diff --git a/python_modules/codegen.py b/python_modules/codegen.py index 5bb659a..af6636b 100644 --- a/python_modules/codegen.py +++ b/python_modules/codegen.py @@ -113,6 +113,7 @@ class CodeWriter: writer.at_line_start = self.at_line_start writer.generated = self.generated writer.options = self.options + writer.public_prefix = self.public_prefix return writer; -- cgit From aa7a086933c8b03d2f7f78874342a53719a24fa5 Mon Sep 17 00:00:00 2001 From: Alon Levy Date: Thu, 29 Jul 2010 09:03:15 -0400 Subject: support python 2.5.4+ for marshaller/demarshallers Patch adds a "from __future__" import that doesn't affect newer python's but allows python 2.5.4 to run the code (tested under scratchbox, n900 build environment) --- python_modules/codegen.py | 1 + 1 file changed, 1 insertion(+) (limited to 'python_modules/codegen.py') diff --git a/python_modules/codegen.py b/python_modules/codegen.py index af6636b..03c67e6 100644 --- a/python_modules/codegen.py +++ b/python_modules/codegen.py @@ -1,3 +1,4 @@ +from __future__ import with_statement from cStringIO import StringIO def camel_to_underscores(s, upper = False): -- cgit From f7986c2b0d0f633f7b8953443dd4de69f37f5243 Mon Sep 17 00:00:00 2001 From: Alon Levy Date: Sat, 11 Dec 2010 15:55:19 +0200 Subject: python_modules/codegen.py: fix indent error in an unused function --- python_modules/codegen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'python_modules/codegen.py') diff --git a/python_modules/codegen.py b/python_modules/codegen.py index 03c67e6..75033dc 100644 --- a/python_modules/codegen.py +++ b/python_modules/codegen.py @@ -11,7 +11,7 @@ def camel_to_underscores(s, upper = False): res = res + c.upper() else: res = res + c.lower() - return res + return res def underscores_to_camel(s): res = "" -- cgit From bbd93cdb6b26e475bc7772a6b0c239082adfc153 Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Tue, 21 Jun 2011 13:20:33 +0200 Subject: python: remove c-ism trailing ; --- python_modules/codegen.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'python_modules/codegen.py') diff --git a/python_modules/codegen.py b/python_modules/codegen.py index 75033dc..116760c 100644 --- a/python_modules/codegen.py +++ b/python_modules/codegen.py @@ -116,7 +116,7 @@ class CodeWriter: writer.options = self.options writer.public_prefix = self.public_prefix - return writer; + return writer def write(self, s): # Ensure its a string @@ -184,10 +184,10 @@ class CodeWriter: self.statement("goto %s" % label) def indent(self): - self.indentation += 4; + self.indentation += 4 def unindent(self): - self.indentation -= 4; + self.indentation -= 4 if self.indentation < 0: self.indenttation = 0 -- cgit From d8975877c6e275961ee7d891c8dd2fc67d30305e Mon Sep 17 00:00:00 2001 From: Christophe Fergeau Date: Wed, 22 Jun 2011 13:22:22 +0200 Subject: add ifdef/endif methods to spice code generator These methods will be needed to be able to make some fields optional in spice.proto --- python_modules/codegen.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'python_modules/codegen.py') diff --git a/python_modules/codegen.py b/python_modules/codegen.py index 116760c..6d53551 100644 --- a/python_modules/codegen.py +++ b/python_modules/codegen.py @@ -322,6 +322,18 @@ class CodeWriter: def macro(self, name, args, define): self.write("#define %s(%s) %s" % (name, args, define)).newline() + def ifdef(self, name): + indentation = self.indentation + self.indentation = 0; + self.write("#ifdef %s" % (name)).newline() + self.indentation = indentation + + def endif(self, name): + indentation = self.indentation + self.indentation = 0; + self.write("#endif /* %s */" % (name)).newline() + self.indentation = indentation + def add_function_variable(self, ctype, name): if self.function_variables.has_key(name): assert(self.function_variables[name] == ctype) -- cgit From ca57afda341a1187a9c367e5be71744979dd7a86 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 30 Sep 2011 11:19:38 +0200 Subject: spice_codegen: Always write a channels entry for an ifdef-ed channel Before this patch, if a channel is defined conditionally in spice.proto (because it depends on external headers like the smartcard channel), spice_codegen would write an entry to the channels array in spice_get_*_channel_parser which would only take up a place in the array if the ifdef condition is true, thus moving up all other intializers one place when it is not true. This was causing issues (crashes) when building spice-gtk with the combination of usbredir support enabled and smartcard support disabled. This patch fixes this by adding #else { NULL, 0 }, to the generated code. Thanks to coolper chen for reporting this! Signed-off-by: Hans de Goede --- python_modules/codegen.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'python_modules/codegen.py') diff --git a/python_modules/codegen.py b/python_modules/codegen.py index 6d53551..009cf95 100644 --- a/python_modules/codegen.py +++ b/python_modules/codegen.py @@ -328,6 +328,12 @@ class CodeWriter: self.write("#ifdef %s" % (name)).newline() self.indentation = indentation + def ifdef_else(self, name): + indentation = self.indentation + self.indentation = 0; + self.write("#else /* %s */" % (name)).newline() + self.indentation = indentation + def endif(self, name): indentation = self.indentation self.indentation = 0; -- cgit From e919337980c45fb4bc907cf40cdd106fb8f32d92 Mon Sep 17 00:00:00 2001 From: Alexander Wauck Date: Tue, 31 Mar 2015 12:44:09 -0500 Subject: Make spice_codegen.py work on both Python 2 and 3 This is a new version of my previous patch that does not include six.py. It's still kind of big, but at least it's all spice-common changes now. There are also a few other fixes that Christophe brought to my attention. Note that six now needs to be installed on the system (python-six on Fedora and Debian, six on PyPI). This *should* be enough to make spice_codegen.py work on both Python 2 and Python 3. The major changes are as follows: * cStringIO.StringIO -> io.StringIO * str vs. unicode updates (io.StringIO doesn't like str) * integer division * foo.has_key(bar) -> bar in foo * import internal_thing -> from . import internal_thing * removed from __future__ import with_statement (might break Python 2.5?) * changed some lambdas to list comprehensions (done by 2to3) * cast some_dict.keys() to list where needed (e.g. for sorting) * use normal type names with isinstance instead of types.WhateverType Signed-off-by: Alexander Wauck --- python_modules/codegen.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) (limited to 'python_modules/codegen.py') diff --git a/python_modules/codegen.py b/python_modules/codegen.py index 009cf95..55f513b 100644 --- a/python_modules/codegen.py +++ b/python_modules/codegen.py @@ -1,5 +1,6 @@ -from __future__ import with_statement -from cStringIO import StringIO + +import six +from io import StringIO def camel_to_underscores(s, upper = False): res = "" @@ -85,10 +86,10 @@ class CodeWriter: self.options[opt] = value def has_option(self, opt): - return self.options.has_key(opt) + return opt in self.options def set_is_generated(self, kind, name): - if not self.generated.has_key(kind): + if kind not in self.generated: v = {} self.generated[kind] = v else: @@ -96,13 +97,13 @@ class CodeWriter: v[name] = 1 def is_generated(self, kind, name): - if not self.generated.has_key(kind): + if kind not in self.generated: return False v = self.generated[kind] - return v.has_key(name) + return name in v def getvalue(self): - strs = map(lambda writer: writer.getvalue(), self.contents) + strs = [writer.getvalue() for writer in self.contents] return "".join(strs) def get_subwriter(self): @@ -119,21 +120,24 @@ class CodeWriter: return writer def write(self, s): - # Ensure its a string - s = str(s) + # Ensure its a unicode string + if six.PY2: + s = unicode(s) + else: + s = str(s) if len(s) == 0: return if self.at_line_start: for i in range(self.indentation): - self.out.write(" ") + self.out.write(u" ") self.at_line_start = False self.out.write(s) return self def newline(self): - self.out.write("\n") + self.out.write(u"\n") self.at_line_start = True return self @@ -341,7 +345,7 @@ class CodeWriter: self.indentation = indentation def add_function_variable(self, ctype, name): - if self.function_variables.has_key(name): + if name in self.function_variables: assert(self.function_variables[name] == ctype) else: self.function_variables[name] = ctype -- cgit From 3cc9566a00cb4deb1ead414c390a29beaccc0bd0 Mon Sep 17 00:00:00 2001 From: Christophe Fergeau Date: Tue, 14 Apr 2015 16:08:43 +0200 Subject: codegen: Use six.PY3 rather than six.PY2 Older versions of python-six (at least 1.3.0) defined six.PY3 but not six.PY2. six.PY2 is only used twice in straightforward tests so it's easy to use six.PY3 instead. --- python_modules/codegen.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'python_modules/codegen.py') diff --git a/python_modules/codegen.py b/python_modules/codegen.py index 55f513b..f324498 100644 --- a/python_modules/codegen.py +++ b/python_modules/codegen.py @@ -121,10 +121,10 @@ class CodeWriter: def write(self, s): # Ensure its a unicode string - if six.PY2: - s = unicode(s) - else: + if six.PY3: s = str(s) + else: + s = unicode(s) if len(s) == 0: return -- cgit From 233c463e3461c5846f125b25c37c6f74b36f1f3c Mon Sep 17 00:00:00 2001 From: Frediano Ziglio Date: Tue, 21 Jul 2015 17:45:33 +0100 Subject: codegen: Fix typo in variable name Signed-off-by: Frediano Ziglio --- python_modules/codegen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'python_modules/codegen.py') diff --git a/python_modules/codegen.py b/python_modules/codegen.py index f324498..55500b7 100644 --- a/python_modules/codegen.py +++ b/python_modules/codegen.py @@ -193,7 +193,7 @@ class CodeWriter: def unindent(self): self.indentation -= 4 if self.indentation < 0: - self.indenttation = 0 + self.indentation = 0 def begin_block(self, prefix= "", comment = ""): if len(prefix) > 0: -- cgit From 553be710677bd450b00abb4a95680b506bfcb574 Mon Sep 17 00:00:00 2001 From: Frediano Ziglio Date: Tue, 21 Jul 2015 17:45:34 +0100 Subject: codegen: Optimize code indentation and avoid a loop Signed-off-by: Frediano Ziglio --- python_modules/codegen.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'python_modules/codegen.py') diff --git a/python_modules/codegen.py b/python_modules/codegen.py index 55500b7..02ffdb9 100644 --- a/python_modules/codegen.py +++ b/python_modules/codegen.py @@ -130,8 +130,7 @@ class CodeWriter: return if self.at_line_start: - for i in range(self.indentation): - self.out.write(u" ") + self.out.write(u" " * self.indentation) self.at_line_start = False self.out.write(s) return self -- cgit From 08384ac7fbdc8303fdf85cc2689602e429607126 Mon Sep 17 00:00:00 2001 From: Frediano Ziglio Date: Tue, 21 Jul 2015 17:45:39 +0100 Subject: codegen: Check we don't pop too many indexes --- python_modules/codegen.py | 1 + 1 file changed, 1 insertion(+) (limited to 'python_modules/codegen.py') diff --git a/python_modules/codegen.py b/python_modules/codegen.py index 02ffdb9..c470988 100644 --- a/python_modules/codegen.py +++ b/python_modules/codegen.py @@ -357,6 +357,7 @@ class CodeWriter: return index def push_index(self): + assert self.current_index > 0 self.current_index = self.current_index - 1 class Index: -- cgit From de1286ad80ce7c559fccde5f6e466a89fc1ce60d Mon Sep 17 00:00:00 2001 From: Frediano Ziglio Date: Tue, 21 Jul 2015 17:45:40 +0100 Subject: codegen: Allow to specify C type for index variable This is to prepare to generate the wireshark dissector which uses glib types instead of the newer C ones (for compatibility with some compilers). Signed-off-by: Frediano Ziglio --- python_modules/codegen.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'python_modules/codegen.py') diff --git a/python_modules/codegen.py b/python_modules/codegen.py index c470988..f7a2048 100644 --- a/python_modules/codegen.py +++ b/python_modules/codegen.py @@ -81,6 +81,7 @@ class CodeWriter: self.has_error_check = False self.options = {} self.function_helper_writer = None + self.index_type = 'uint32_t' def set_option(self, opt, value = True): self.options[opt] = value @@ -113,6 +114,7 @@ class CodeWriter: self.contents.append(self.out) writer.indentation = self.indentation writer.at_line_start = self.at_line_start + writer.index_type = self.index_type writer.generated = self.generated writer.options = self.options writer.public_prefix = self.public_prefix @@ -353,7 +355,7 @@ class CodeWriter: def pop_index(self): index = self.indexes[self.current_index] self.current_index = self.current_index + 1 - self.add_function_variable("uint32_t", index) + self.add_function_variable(self.index_type, index) return index def push_index(self): -- cgit