Skip to content

Commit

Permalink
[JsonGen/StubGen] Support fixed arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
sebaszm committed Aug 2, 2024
1 parent d5f4a29 commit 248b605
Show file tree
Hide file tree
Showing 5 changed files with 266 additions and 90 deletions.
26 changes: 24 additions & 2 deletions JsonGenerator/source/class_emitter.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,15 @@ def __EmitAssignment(json_obj, other, type, optional_type=False):
else:
_optional_or_opaque = False # invalid @optional...

emit.Line("%s = %s.%s;" % (prop.cpp_name, other, _prop_name + prop.convert_rhs))
if isinstance(prop, JsonArray) and type == "conv" and prop.schema.get("@arraysize"):
emit.Line("%s.Clear();" % prop.cpp_name)
emit.Line("for (uint16_t i = 0; i < %s; i++) {" % (prop.schema.get("@arraysize")))
emit.Indent()
emit.Line("%s.Add() = %s.%s[i];" % (prop.cpp_name, other, _prop_name + prop.convert_rhs))
emit.Unindent()
emit.Line("}")
else:
emit.Line("%s = %s.%s;" % (prop.cpp_name, other, _prop_name + prop.convert_rhs))

if (prop.optional and not prop.default_value) or _optional_or_opaque:
emit.Unindent()
Expand Down Expand Up @@ -340,7 +348,21 @@ def _EmitConversionOperator(json_obj):
emit.Indent()

conv = (prop.convert if prop.convert else "%s = %s")
emit.Line((conv + ";") % ( ("_value." + prop.actual_name), prop.cpp_name))

if isinstance(prop, JsonArray) and prop.schema.get("@arraysize"):
emit.Line("{")
emit.Indent()
emit.Line("uint16_t i = 0;")
emit.Line("auto it = %s.Elements();" % prop.cpp_name)
emit.Line("while ((it.Next() != true) && (i < %s)) {" % prop.schema.get("@arraysize"))
emit.Indent()
emit.Line("%s[i++] = it.Current();" % ("_value." + prop.actual_name))
emit.Unindent()
emit.Line("}")
emit.Unindent()
emit.Line("}")
else:
emit.Line((conv + ";") % ( ("_value." + prop.actual_name), prop.cpp_name))

if (prop.optional and not prop.default_value):
emit.Unindent()
Expand Down
21 changes: 14 additions & 7 deletions JsonGenerator/source/header_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def ResolveTypedef(type, parent=type):

return type.Resolve()

def ConvertType(var, quiet=False, meta=None):
def ConvertType(var, quiet=False, meta=None, no_array=False):
if not meta:
meta = var.meta

Expand All @@ -171,11 +171,12 @@ def ConvertType(var, quiet=False, meta=None):

cppType = var_type.Type()
is_iterator = (isinstance(cppType, CppParser.Class) and cppType.is_iterator)
is_bitmask = "bitmask" in meta.decorators

# Pointers
if var_type.IsPointer() and (is_iterator or (meta.length and meta.length != ["void"])):
if var_type.IsPointer() and (is_iterator or (meta.length and meta.length != ["void"]) or var.array) and not no_array and not is_bitmask:
# Special case for serializing C-style buffers, that will be converted to base64 encoded strings
if isinstance(cppType, CppParser.Integer) and cppType.size == "char":
if isinstance(cppType, CppParser.Integer) and (cppType.size == "char") and ("encode:base64" in meta.decorators):
props = dict()

if meta.maxlength:
Expand All @@ -186,13 +187,19 @@ def ConvertType(var, quiet=False, meta=None):
if "length" in props:
props["@originaltype"] = cppType.type

props["encode"] = cppType.type != "char"
props["encode"] = (cppType.type != "char")

if meta.range:
props["range"] = meta.range

return "string", props if props else None

elif isinstance(cppType, CppParser.Integer) and (cppType.size == "char") and not var.array:
return ["array", { "items": ConvertParameter(var, no_array=True), "length": " ".join(meta.length), "@arraysize": "#bylength" }]

elif isinstance(cppType, (CppParser.Class, CppParser.String, CppParser.Bool, CppParser.Integer, CppParser.Float)) and var.array:
return ["array", { "items": ConvertParameter(var, no_array=True), "@arraysize": var.array }]

# Special case for iterators, that will be converted to JSON arrays
elif is_iterator and len(cppType.args) == 2:
# Take element type from return value of the Current() method
Expand Down Expand Up @@ -319,7 +326,7 @@ def GenerateObject(ctype, was_typdef):
raise CppParseError(p, "%s: undefined type" % " ".join(p.type))
if isinstance(p.type, str):
raise CppParseError(p, "%s: undefined type" % p.type)
elif isinstance(ResolveTypedef(p.type).Type(), CppParser.Class):
elif isinstance(ResolveTypedef(p.type).Type(), CppParser.Class) and not p.array:
_, props = GenerateObject(ResolveTypedef(p.type).Type(), isinstance(p.type.Type(), CppParser.Typedef))
properties[name] = props
properties[name]["type"] = "object"
Expand Down Expand Up @@ -396,8 +403,8 @@ def ExtractExample(var):
else:
return None

def ConvertParameter(var, quiet=False):
jsonType, args = ConvertType(var, quiet)
def ConvertParameter(var, quiet=False, no_array=False):
jsonType, args = ConvertType(var, quiet, no_array=no_array)
properties = {"type": jsonType}

if args != None:
Expand Down
49 changes: 48 additions & 1 deletion JsonGenerator/source/rpc_emitter.py
Original file line number Diff line number Diff line change
Expand Up @@ -634,7 +634,7 @@ def _Invoke(params, response, parent="", repsonse_parent="", const_cast=False, i
for _, [arg, _] in sorted_vars:
length_var_name = arg.schema.get("length")

if isinstance(arg, JsonString) and length_var_name:
if isinstance(arg, (JsonString, JsonArray)) and length_var_name:
for name, [var, type] in sorted_vars:
if name == length_var_name:
if type == "w":
Expand Down Expand Up @@ -785,6 +785,39 @@ def _Invoke(params, response, parent="", repsonse_parent="", const_cast=False, i
initializer = cpp_name if is_readable else ""
emit.Line("%s%s %s{%s};" % (cv_qualifier, arg.items.cpp_native_type, arg.temp_name, initializer))

elif arg.schema.get("@arraysize"):
if "#" in arg.schema.get("@arraysize"):
for name, [var, var_type] in sorted_vars:
if name == arg.flags.length.local_name:
initializer = (parent + var.cpp_name) if "r" in var_type else ""
emit.Line("%s %s{%s};" % (var.cpp_native_type, var.temp_name, initializer))
break

emit.Line("%s* %s{};" % (arg.items.cpp_native_type, arg.items.temp_name))
emit.Line("if (%s != 0) {" % arg.flags.length.temp_name)
emit.Indent()
emit.Line("%s = static_cast<%s*>(ALLOCA(%s));" % (arg.items.temp_name, arg.items.cpp_native_type, arg.flags.length.temp_name))
emit.Line("ASSERT(%s != nullptr);" % arg.items.temp_name)
_len = arg.flags.length.temp_name
else:
emit.Line("%s %s[%s]{};" % (arg.items.cpp_native_type, arg.items.temp_name, arg.schema.get("@arraysize")))
_len = arg.schema.get("@arraysize")
emit.Line("{")
emit.Indent()

if is_readable:
emit.Line("uint16_t i = 0;")
emit.Line("auto it = %s.Elements();" % arg.local_name)
emit.Line("while ((it.Next() != true) && (i < %s)) {" % _len)
emit.Indent()
emit.Line("%s[i++] = it.Current();" % (arg.items.temp_name))
emit.Unindent()
emit.Line("}")

emit.Unindent()
emit.Line("}")
emit.Line()

elif is_json_source:
response_cpp_name = (response_parent + arg.cpp_name) if response_parent else arg.local_name
initializer = ("(%s)" if isinstance(arg, JsonObject) else "{%s}") % (response_cpp_name if is_writeable else cpp_name)
Expand Down Expand Up @@ -968,6 +1001,20 @@ def _Invoke(params, response, parent="", repsonse_parent="", const_cast=False, i
elif arg.items.schema.get("@bitmask"):
emit.Line("%s = %s;" % (cpp_name, _rhs))

elif arg.schema.get("@arraysize"):
if "#" in arg.schema.get("@arraysize"):
_len = arg.flags.length.temp_name
else:
_len = arg.schema.get("@arraysize")

emit.Line("%s.Clear();" % cpp_name)
emit.Line("for (uint16_t i = 0; i < %s; i++) {" % _len)
emit.Indent()
emit.Line("%s.Add() = %s[i];" % (cpp_name, _rhs))
emit.Unindent()
emit.Line("}")
pass

else:
raise RPCEmitterError("unable to serialize a non-iterator array: %s" % arg.json_name)

Expand Down
66 changes: 49 additions & 17 deletions ProxyStubGenerator/CppParser.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ def __init__(self):

class Optional(Intrinsic):
def __init__(self, subtype):
Intrinsic.__init__(self, "Core::OptionalType<%s>" % subtype.type)
Intrinsic.__init__(self, "Core::OptionalType<%s>" % subtype.Proto())
self.optional = subtype


Expand Down Expand Up @@ -283,6 +283,8 @@ def __init__(self, parent_block, parent, string, valid_specifiers, tags_allowed=
nest1 = 0
nest2 = 0
array = False
array_size = None
self.array = None
skip = 0
self.value = []

Expand Down Expand Up @@ -391,6 +393,14 @@ def __init__(self, parent_block, parent, string, valid_specifiers, tags_allowed=
skip = 1
elif tag == "OPAQUE":
self.meta.decorators.append("opaque")
elif tag == "ENCODEBASE64":
self.meta.decorators.append("encode:base64")
elif tag == "ENCODEHEX":
self.meta.decorators.append("encode:hex")
elif tag == "ENCODEIP":
self.meta.decorators.append("encode:ip")
elif tag == "ENCODEMAC":
self.meta.decorators.append("encode:mac")
elif tag == "OPTIONAL":
self.meta.decorators.append("optional")
elif tag == "EXTRACT":
Expand Down Expand Up @@ -467,6 +477,8 @@ def __init__(self, parent_block, parent, string, valid_specifiers, tags_allowed=
elif token == "]":
array = False
type.append("*")
self.array = array_size
array_size = None

elif token in ["*", "&"]:
type.append(token)
Expand Down Expand Up @@ -514,6 +526,8 @@ def __init__(self, parent_block, parent, string, valid_specifiers, tags_allowed=
elif type_found:
if not array:
self.name = token
else:
array_size = token

if array:
raise ParserError("unmatched bracket '['")
Expand Down Expand Up @@ -584,6 +598,13 @@ def __Search(tree, found, T):
typeIdx = len(self.type) - 1
cnt = 0
ref = 0

if isinstance(self.type[typeIdx], str):
if self.type[typeIdx].startswith('['):
ref |= Ref.POINTER
typeIdx -=1
cnt += 1

while self.type[typeIdx] in ["*", "&", "&&", "const", "volatile"]:
if self.type[typeIdx] == "*":
if ref & Ref.POINTER:
Expand All @@ -602,9 +623,11 @@ def __Search(tree, found, T):
ref |= Ref.CONST
elif self.type[typeIdx] == "volatile":
ref |= Ref.VOLATILE

typeIdx -= 1
cnt += 1


# Skip template parsing here
if isinstance(self.type[typeIdx], str):
if self.type[typeIdx][0] == "<":
Expand All @@ -613,6 +636,7 @@ def __Search(tree, found, T):
if isinstance(self.type[typeIdx], str):
i = typeIdx
type = self.type[i].split()[-1]

if type in ["float", "double"]:
self.type[i] = Type(Float(self.type[i]))
elif type in ["int", "char", "wchar_t", "char16_t", "char32_t", "short", "long", "signed", "unsigned",
Expand Down Expand Up @@ -682,8 +706,17 @@ def Type(self):
def Proto(self):
return str(self.Type())

def Signature(self):
return (self.Proto() + " " + self.name)
def TypeStrong(self):
if self.array:
return ("%s[%s]" % (self.type.Proto("nocv|noref|noptr"), self.array))
else:
return self.Proto()

def Signature(self, override=None):
if self.array:
return ("%s %s[%s]" % (self.type.Proto("nocv|noref|noptr"), override if override != None else self.name, self.array))
else:
return (self.Proto() + " " + override if override != None else self.name)


def Evaluate(identifiers_):
Expand Down Expand Up @@ -937,8 +970,7 @@ def ReferenceString(self):
return _str

def Proto(self, mask=""):
_str = ""
_str += self.CVString(mask)
_str = self.CVString(mask)
_str += " " if _str else ""
_str += self.TypeName()
if "noptr" not in mask:
Expand Down Expand Up @@ -1064,7 +1096,7 @@ def Populate(array, are_methods=False):

def Merge(self, do_methods=False):
new_class = Class(self.parent, self.name)
result = self._Merge(new_class.vars, new_class.methods, do_methods)
self._Merge(new_class.vars, new_class.methods, do_methods)
return new_class

def __str__(self):
Expand Down Expand Up @@ -1169,9 +1201,6 @@ def __init__(self, parent_block, string, value=[], valid_specifiers=["static", "
Name.__init__(self, parent_block, self.name)
self.value = Evaluate(value) if value else None

def Proto(self):
return TypeStr(self.type)

def __str__(self):
return self.Proto()

Expand All @@ -1188,9 +1217,6 @@ def __init__(self, parent_block, string, value=[], valid_specifiers=["static", "
if self.parent:
self.parent.vars.append(self)

def Proto(self):
return TypeStr(self.type)

def __str__(self):
return self.Proto()

Expand All @@ -1207,9 +1233,6 @@ def __init__(self, parent_block, string, value=[], valid_specifiers=[]):
if self.name in parent_block.retval.meta.param:
self.meta.brief = parent_block.retval.meta.param[self.name]

def Proto(self):
return TypeStr(self.type)

def __str__(self):
return "%s %s" % (self.Proto(), self.name)

Expand Down Expand Up @@ -1304,11 +1327,11 @@ def CVString(self):
return str

def Proto(self):
return "%s %s" % (TypeStr(self.type), str(self.name))
return "%s" % (TypeStr(self.type))

def __str__(self):
value = ValueStr(self.value) if self.value else None
return "%s %s" % (self.Proto(), (" = " + value) if value else "")
return "%s %s %s" % (self.Proto(), self.name, (" = " + value) if value else "")

def __repr__(self):
value = ValueStr(self.value) if self.value else None
Expand Down Expand Up @@ -1746,6 +1769,14 @@ def _find(word, string):
tagtokens.append(__ParseParameterValue(token, "@text", True, True, "@text-global"))
elif _find("@text", token):
tagtokens.append(__ParseParameterValue(token, "@text"))
if _find("@encode:base64", token):
tagtokens.append("@ENCODEBASE64")
elif _find("@encode:hex", token):
tagtokens.append("@ENCODEHEX")
elif _find("@encode:ip", token):
tagtokens.append("@ENCODEIP")
elif _find("@encode:mac", token):
tagtokens.append("@ENCODEMAC")
if _find("@length", token):
tagtokens.append(__ParseParameterValue(token, "@length"))
if _find("@maxlength", token):
Expand Down Expand Up @@ -2443,6 +2474,7 @@ def Parse(contents,log = None):
else:
j = i + 1
i += 1

if in_typedef:
current_block[-2].typedefs[-1].type = Type(enum)
in_typedef = False
Expand Down
Loading

0 comments on commit 248b605

Please sign in to comment.