From 248b60556cc574efed1718aa818e72d67c1ded08 Mon Sep 17 00:00:00 2001 From: sebaszm Date: Fri, 2 Aug 2024 02:49:29 +0200 Subject: [PATCH 1/2] [JsonGen/StubGen] Support fixed arrays --- JsonGenerator/source/class_emitter.py | 26 +++- JsonGenerator/source/header_loader.py | 21 ++- JsonGenerator/source/rpc_emitter.py | 49 ++++++- ProxyStubGenerator/CppParser.py | 66 ++++++--- ProxyStubGenerator/StubGenerator.py | 194 +++++++++++++++++--------- 5 files changed, 266 insertions(+), 90 deletions(-) diff --git a/JsonGenerator/source/class_emitter.py b/JsonGenerator/source/class_emitter.py index bfaadb9..89a3a96 100644 --- a/JsonGenerator/source/class_emitter.py +++ b/JsonGenerator/source/class_emitter.py @@ -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() @@ -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() diff --git a/JsonGenerator/source/header_loader.py b/JsonGenerator/source/header_loader.py index dc335da..7bda3d1 100644 --- a/JsonGenerator/source/header_loader.py +++ b/JsonGenerator/source/header_loader.py @@ -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 @@ -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: @@ -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 @@ -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" @@ -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: diff --git a/JsonGenerator/source/rpc_emitter.py b/JsonGenerator/source/rpc_emitter.py index bc678d1..757810c 100644 --- a/JsonGenerator/source/rpc_emitter.py +++ b/JsonGenerator/source/rpc_emitter.py @@ -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": @@ -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) @@ -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) diff --git a/ProxyStubGenerator/CppParser.py b/ProxyStubGenerator/CppParser.py index dc2704b..705d1dc 100755 --- a/ProxyStubGenerator/CppParser.py +++ b/ProxyStubGenerator/CppParser.py @@ -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 @@ -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 = [] @@ -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": @@ -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) @@ -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 '['") @@ -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: @@ -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] == "<": @@ -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", @@ -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_): @@ -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: @@ -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): @@ -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() @@ -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() @@ -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) @@ -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 @@ -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): @@ -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 diff --git a/ProxyStubGenerator/StubGenerator.py b/ProxyStubGenerator/StubGenerator.py index 724e976..6e079b6 100755 --- a/ProxyStubGenerator/StubGenerator.py +++ b/ProxyStubGenerator/StubGenerator.py @@ -96,6 +96,8 @@ def Unreachable(): def CreateName(ns): return ns.replace("::I", "").replace("::", "")[1 if ns[0] == "I" else 0:] +def Normalize(n): + return n.replace('.', '_').replace('[','_').replace(']','_').replace('(','_').replace(')','_') class Emitter: def __init__(self, file, size=2): @@ -576,6 +578,7 @@ def GenerateStubs2(output_file, source_file, tree, ns, scan_only=False): class AuxIdentifier(): def __init__(self, type, qualifiers, name, value=None): + name = Normalize(name) self.type = CppParser.Type(type) self.kind = self.type.Type() self.type.ref = qualifiers @@ -621,6 +624,7 @@ class EmitIdentifier(): def __init__(self, index, interface, identifier, override_name=None, suppress_type=False, suffix=""): def _FindLength(length_name, variable_name): + variable_name = Normalize(variable_name) if length_name: if length_name[0] == "void": return EmitIdentifier(-2, interface, \ @@ -684,6 +688,7 @@ def _FindLength(length_name, variable_name): is_variable_length_of = (index == -1) no_length_warnings = is_variable_length_of is_return_value = (override_name == "result") + in_pod = '.' in override_name if override_name else False self.is_on_wire = True self.suffix = suffix @@ -713,10 +718,16 @@ def _FindLength(length_name, variable_name): self.kind = self.type.Type() is_class = isinstance(self.kind, CppParser.Class) + is_struct = is_class and self.kind.vars + is_interface = is_class and (self.kind.Inherits(CLASS_IUNKNOWN) or not is_struct) # Length variables are actually not put on wire self.length_of = None self.max_length_of = None + self.length = None + self.max_length = None + self.is_buffer = False + self.is_array = False # Is this a length or max-length of a buffer? if self.identifier.parent: @@ -741,9 +752,14 @@ def _FindLength(length_name, variable_name): self.interface_id = _FindLength(self.identifier.meta.interface, (name[1:] + "IntefaceId")) # Is it a buffer? - is_buffer = (self.type.IsPointer() and not is_class and not self.interface_id) - self.length = _FindLength(self.identifier.meta.length, (name[1:] + "Len")) - self.max_length = _FindLength(self.identifier.meta.maxlength, (name[1:] + "Len")) + + is_buffer = (self.type.IsPointer() and not is_interface and not self.interface_id) + self.length = _FindLength(self.identifier.meta.length, (name[1:] + "_Len")) + self.max_length = _FindLength(self.identifier.meta.maxlength, (name[1:] + "_MaxLen")) + + if self.identifier.array and not self.length: + self.length = _FindLength([self.identifier.array], (name[1:] + "ImmLen")) + self.max_length = self.length if (is_buffer and not self.length and self.is_input): raise TypenameError(self.identifier, "'%s': an outbound buffer requires a @length tag" % self.trace_proto) @@ -751,16 +767,15 @@ def _FindLength(length_name, variable_name): if (is_buffer and (not self.length and not self.max_length) and self.is_output): raise TypenameError(self.identifier, "'%s': an inbound-only buffer requires a @maxlength tag" % self.trace_proto) - self.is_buffer = ((self.length or self.max_length) and is_buffer) + self.is_buffer = ((self.length or self.max_length) and is_buffer and isinstance(self.identifier_kind, CppParser.Integer) and (self.identifier_kind.size == "char")) + self.is_array = ((self.length or self.max_length) and is_buffer and not self.is_buffer) # If have a input length, assume it's a max-length parameter even if not stated explicitly if (self.is_buffer and not self.max_length and self.length.is_input): self.max_length = self.length if self.is_input and self.is_output: - log.WarnLine(self.identifier, \ - "'%s': maximum length of this inbound/outbound buffer is assumed to be same as @length, use @maxlength to disambiguate" % \ - (self.trace_proto)) + log.WarnLine(self.identifier, "'%s': maximum length of this inbound/outbound buffer is assumed to be same as @length, use @maxlength to disambiguate" % self.trace_proto) elif self.is_output_only: log.InfoLine(self.identifier, "'%s': @maxlength not specified for this inbound buffer, assuming same as @length" % self.trace_proto) @@ -781,11 +796,10 @@ def _FindLength(length_name, variable_name): self.is_compound = (is_class and not self.kind.IsVirtual() and (len(self.kind.vars) > 0)) # Is it an interface instance? - is_iunknown_descendant = is_class #and self.kind.Inherits(CLASS_IUNKNOWN) is_unspecified_interface_instance = (isinstance(self.kind, CppParser.Void) and self.identifier.meta.interface) - has_proxy = not self.is_compound and (is_class and is_iunknown_descendant and self.type.IsPointer()) and self.is_input + has_proxy = not self.is_compound and (is_interface and self.type.IsPointer()) and self.is_input has_output_proxy = not self.is_compound and \ - (self.is_output and self.type.IsPointer() and (is_iunknown_descendant or is_unspecified_interface_instance)) + (self.is_output and self.type.IsPointer() and (is_interface or is_unspecified_interface_instance)) self.proxy = None self.return_proxy = False @@ -801,8 +815,8 @@ def _FindLength(length_name, variable_name): # Have to use instance_id instead of the class name self.proto_weak = (("const " if self.is_const else "") + INSTANCE_ID).strip() self.proto_weak_no_cv = INSTANCE_ID - self.proxy = AuxIdentifier(CppParser.Class(CppParser.Locate("ProxyStub"), "UnknownProxy"), CppParser.Ref.POINTER, ("%sProxy__" % name).replace('.', '_')) - self.proxy_instance = AuxIdentifier(CppParser.InstanceId(), CppParser.Ref.VALUE, ("%sInstanceId__" % name).replace('.', '_')) + self.proxy = AuxIdentifier(CppParser.Class(CppParser.Locate("ProxyStub"), "UnknownProxy"), CppParser.Ref.POINTER, ("%sProxy__" % name)) + self.proxy_instance = AuxIdentifier(CppParser.InstanceId(), CppParser.Ref.VALUE, ("%sInstanceId__" % name)) else: self.proto_weak = self.proto self.proto_weak_no_cv = self.proto_no_cv @@ -825,21 +839,21 @@ def _FindLength(length_name, variable_name): if not is_return_value and (self.is_output and self.return_proxy and not self.type.IsReference()): raise TypenameError(self.identifier, "'%s': output interface must be a reference to a pointer" % self.trace_proto) - if self.is_compound and self.type.IsPointer(): - raise TypenameError(self.identifier, "'%s': C-style POD array is not supported, use an iterator" % self.trace_proto) + #if self.is_compound and self.type.IsPointer() + # raise TypenameError(self.identifier, "'%s': C-style POD array is not supported, use an iterator" % self.trace_proto) if self.length: - if not self.is_buffer: - raise TypenameError(self.identifier, "'%s': @length tag only allowed for raw buffers" % self.trace_proto) + if not self.is_buffer and not self.is_array: + raise TypenameError(self.identifier, "'%s': @length tag only allowed for raw buffers or arrays" % self.trace_proto) if self.max_length: - if not self.is_buffer: - raise TypenameError(self.identifier, "'%s': @maxlength tag only allowed for raw buffers" % self.trace_proto) + if not self.is_buffer and not self.is_array: + raise TypenameError(self.identifier, "'%s': @maxlength tag only allowed for raw buffers or arrays" % self.trace_proto) if self.length_of and not no_length_warnings: if not self.is_input: raise TypenameError(self.identifier, "'%s': a length parameter must not be write-only" % self.trace_proto) - elif not isinstance(self.kind, CppParser.Integer) or self.is_buffer: + elif not isinstance(self.kind, CppParser.Integer) or (self.is_buffer or self.is_array): raise TypenameError(self.identifier, "'%s': this type cannot be a buffer length carrying parameter" % self.trace_proto) if self.kind.size == "long long": raise TypenameError(self.identifier, "'%s': 64-bit buffer length carrying parameter is not supported" % self.trace_proto) @@ -854,7 +868,7 @@ def _FindLength(length_name, variable_name): if self.max_length_of and (self.max_length != self.length) and not no_length_warnings: if not self.is_input: raise TypenameError(self.identifier, "'%s': a max-length parameter must not be write-only" % self.trace_proto) - elif not isinstance(self.kind, CppParser.Integer) or self.is_buffer: + elif not isinstance(self.kind, CppParser.Integer) or (self.is_buffer or self.is_array): raise TypenameError(self.identifier, "'%s': this type cannot be a buffer max-length carrying parameter" % self.trace_proto) if self.kind.size == "long long": raise TypenameError(self.identifier, "'%s': 64-bit buffer max-length carrying parameter is not supported" % self.trace_proto) @@ -874,7 +888,7 @@ def _FindLength(length_name, variable_name): if not self.identifier_type.IsConst() and self.is_input_only: log.WarnLine(identifier, "'%s': read-only parameter is missing a const qualifier" % self.trace_proto) - if self.is_buffer: + if ((self.is_buffer or self.is_array) and not in_pod): if not self.identifier_type.IsPointerToConst() and not self.identifier.meta.input and not self.identifier.meta.output: raise TypenameError(self.identifier, "'%s': pointer to non-const data requires an @in or @out tag" % self.trace_proto) elif not self.identifier_type.IsPointerToConst() and self.is_input_only: @@ -882,7 +896,7 @@ def _FindLength(length_name, variable_name): elif self.identifier_type.IsPointerToConst() and self.is_output: raise TypenameError(identifier, "'%s': output parameter must not be const" % self.trace_proto) - if not self.is_buffer and isinstance(self.kind, (CppParser.Integer, CppParser.BuiltinInteger)): + if (not self.is_buffer and not self.is_array) and isinstance(self.kind, (CppParser.Integer, CppParser.BuiltinInteger)): if not self.kind.IsFixed(): log.WarnLine(self.identifier, "'%s': integer is not fixed-width, use a stdint type" % self.trace_proto) @@ -922,8 +936,8 @@ def _FindLength(length_name, variable_name): log.WarnLine(identifier, "'%s': parameters up to %s bytes are recommended for COM-RPC, see range (%s..%s)" \ % (self.trace_proto, PARAMETER_SIZE_WARNING_THRESHOLD, self.restrict_range[0], self.restrict_range[1])) - if self.is_string or self.is_buffer: - aux_name = (self.name.replace(".", "_") + "PeekedLen__") + if self.is_string or self.is_buffer or self.is_array: + aux_name = Normalize(self.name + "PeekedLen__") aux_size = "uint16_t" if self.restrict_range: @@ -942,7 +956,7 @@ def _FindLength(length_name, variable_name): self.peek_length = AuxIdentifier(CppParser.Integer(aux_size), (CppParser.Ref.VALUE | CppParser.Ref.CONST), aux_name) - elif self.is_buffer: + elif self.is_buffer or self.is_array: if not self.restrict_range: aux_size = (self.length.type_name if self.length else self.max_length.type_name) @@ -962,17 +976,26 @@ def _FindLength(length_name, variable_name): log.WarnLine(self.identifier, "%s: input-only fundamental type passed by reference" % self.trace_proto) if isinstance(self.kind, CppParser.Optional): - if self.kind.optional.type.IsPointer() and not isinstance(self.kind.optional.type.Resolve().type, CppParser.Class): - raise TypenameError(identifier, "'%s': raw buffer must not be OptionalType" % self.trace_proto) + #if self.kind.optional.type.IsPointer() and not isinstance(self.kind.optional.type.Resolve().type, CppParser.Class) and not: + # raise TypenameError(identifier, "'%s': raw buffer must not be OptionalType" % self.trace_proto) self.kind.optional.meta = identifier.meta - self.kind.optional.type.ref |= identifier.type.ref + + if not self.is_array: + self.kind.optional.type.ref |= identifier.type.ref self.optional = EmitIdentifier(0, self.interface, self.kind.optional, self.name, suffix=".Value()") self.optional.restrict_range = self.restrict_range self.is_compound = self.optional.is_compound + self.is_array = self.optional.is_array + self.is_buffer = self.optional.is_buffer self.is_string = self.optional.is_string - self.peek_length = self.optional.peek_length + if not self.length: + self.length = self.optional.length + if not self.max_length: + self.max_length = self.optional.max_length + if not self.peek_length: + self.peek_length = self.optional.peek_length self.suffix = ".Value()" if self.is_compound: @@ -1279,27 +1302,39 @@ def EmitStubMethodImplementation(index, method, interface_name, interface, retva has_hresult = retval and retval.is_hresult # For stubs - def ReadParameter(p): + def ReadParameter(p, no_array=False): assert p.is_on_wire - if p.optional: + if p.optional and (not p.is_array or no_array): if not p.suppress_type: - emit.Line("%s{};" % p.temporary_no_cv) + if not p.is_array: + emit.Line("%s{};" % p.temporary_no_cv) CheckFrame(p) emit.Line("if (%s.Boolean() == true) {" % vars["reader"]) emit.IndentInc() else: - if not p.suppress_type and p.is_compound: + if not p.suppress_type and p.is_compound and not p.is_array: emit.Line("%s{};" % p.temporary_no_cv) - if p.is_compound: + if p.is_array and not no_array: + if not p.suppress_type: + emit.Line("%s %s[%s]{};" % (p.type_name, p.name, p.length.as_rvalue)) + + _index = chr(ord('i') + p.name.count('[')) + emit.Line("for (uint16_t %s = 0; %s < %s; %s++) {" % (_index, _index, p.length.as_rvalue, _index)) + emit.IndentInc() + ReadParameter(EmitParam(interface, p.identifier, "%s[%s]" % (p.name, _index), suppress_type=True), no_array=True) + emit.IndentDec() + emit.Line("}") + + elif p.is_compound: obj_name = p.name if p.optional: - obj_name = p.name.replace(".", "_") + "Object__"; - emit.Line("%s %s{};" % (p.optional.proto_no_cv, obj_name)) + obj_name = Normalize(p.name + "Object__"); + emit.Line("%s %s{};" % (p.optional.type_name, obj_name)) params = [EmitParam(interface, v, (obj_name + "." + v.name), suppress_type=True) for v in p.compound_merged.vars] @@ -1312,14 +1347,25 @@ def ReadParameter(p): emit.Line("%s = std::move(%s);" % (p.name, obj_name)) elif p.is_buffer: - assert not p.optional CheckFrame(p) CheckSize(p) - emit.Line("%s{};" % p.temporary_no_cv) - buffer_param = "const_cast(%s)" % (p.type_name, p.as_rvalue) if not p.identifier_type.IsPointerToConst() else p.as_rvalue + + if not p.suppress_type: + emit.Line("%s{};" % p.temporary_no_cv) + + _name = p.as_rvalue + if p.identifier.array: + _name = "__Temp" + Normalize(_name) + emit.Line("%s %s{};" % (p.proto, _name)) + + buffer_param = "const_cast(%s)" % (p.type_name, _name) if not p.identifier_type.IsPointerToConst() else _name emit.Line("%s = %s.LockBuffer<%s>(%s);" % (p.length.temporary, vars["reader"], p.length.type_name, buffer_param)) emit.Line("%s.UnlockBuffer(%s);" % (vars["reader"], p.length.name)) + if p.identifier.array: + emit.Line("ASSERT(%s == (%s * sizeof(%s)));" % (p.length.as_rvalue, p.identifier.array, p.type_name)) + emit.Line("::memcpy(%s, %s, %s);" % (p.name, _name, p.length.as_rvalue)) + else: param_ = "%s = %s.%s;" % (p.as_lvalue if p.optional else p.temporary, vars["reader"], p.read_rpc_type) @@ -1336,7 +1382,7 @@ def _EmitAssignment(p): _EmitAssignment(p.optional if p.optional else p) - if p.optional: + if p.optional and (not p.is_array or no_array): emit.IndentDec() emit.Line("}") @@ -1450,7 +1496,7 @@ def ReleaseBuffer(p, large_buffer): if (p.max_length and (p.length.type.Type().size == "long")): emit.Line("RPC::Administrator::Instance().Free(%s);" % large_buffer.as_rvalue) - def WriteParameter(p): + def WriteParameter(p, no_array=False): assert p if p.is_on_wire: @@ -1459,8 +1505,16 @@ def WriteParameter(p): emit.Line("if (%s.IsSet() == true) {" % p.name) emit.IndentInc() - if p.is_compound: - params = [EmitParam(interface, v, (p.as_rvalue + "." + v.name)) for v in p.compound_merged.vars] + if p.is_array and not no_array: + _index = chr(ord('i') + p.name.count('[')) + emit.Line("for (uint16_t %s = 0; %s < %s; %s++) {" % (_index, _index, p.length.as_rvalue, _index)) + emit.IndentInc() + WriteParameter(EmitParam(interface, p.identifier, "%s[%s]" % (p.name, _index), suppress_type=True), no_array=True) + emit.IndentDec() + emit.Line("}") + + elif p.is_compound: + params = [EmitParam(interface, v, (p.as_rvalue + "." + v.name), suppress_type=True) for v in p.compound_merged.vars] for pp in params: WriteParameter(pp) @@ -1479,7 +1533,7 @@ def AcquireInterface(p): assert p.proxy assert p.proxy_instance - _name = p.name.replace('.', '_') + _name = Normalize(p.name) _name_maybe_opt = ((_name + "Object__") if p.optional else p.name) emit.Line("%s %s{};" % (p.proto_no_cv, _name)) @@ -1740,7 +1794,7 @@ def EmitProxyMethodImplementation(index, method, interface_name, interface, retv input_params, output_params, proxy_params, return_proxy_params): # For proxy - def WriteParameter(p): + def WriteParameter(p, no_array=False): assert p.is_on_wire if p.optional: @@ -1748,7 +1802,15 @@ def WriteParameter(p): emit.Line("if (%s.IsSet() == true) {" % p.name) emit.IndentInc() - if p.is_compound: + if p.is_array and not no_array: + _index = chr(ord('i') + p.name.count('[')) + emit.Line("for (uint16_t %s = 0; %s < %s; %s++) {" % (_index, _index, p.length.as_rvalue, _index)) + emit.IndentInc() + WriteParameter(EmitParam(interface, p.identifier, "%s[%s]" % (p.name, _index), suppress_type=True), no_array=True) + emit.IndentDec() + emit.Line("}") + + elif p.is_compound: params = [EmitParam(interface, v, (p.as_rvalue + "." + v.name), suppress_type=True) for v in p.compound_merged.vars] for pp in params: @@ -1761,17 +1823,35 @@ def WriteParameter(p): emit.Line("}") # For proxy - def ReadParameter(p): + def ReadParameter(p, no_array=False): if p.is_on_wire: if p.optional: CheckFrame(p) emit.Line("if (%s.Boolean() == true) {" % vars["reader"]) emit.IndentInc() - if p.is_compound: + if p.is_array and not no_array: + _index = chr(ord('i') + p.name.count('[')) + emit.Line("for (uint16_t %s = 0; %s < %s; %s++) {" % (_index, _index, p.length.as_rvalue, _index)) + emit.IndentInc() + ReadParameter(EmitParam(interface, p.identifier, "%s[%s]" % (p.name, _index), suppress_type=True), no_array=True) + emit.IndentDec() + emit.Line("}") + + elif p.is_buffer: + CheckFrame(p) + CheckSize(p) + + if p.length and p.length.is_output: + emit.Line("%s = %s.%s;" % (p.length.as_lvalue, vars["reader"], p.read_rpc_type)) + else: + # No one's interested in the return length, perhaps it's sent via method's return value + emit.Line("%s.%s;" % (vars["reader"], p.read_rpc_type)) + + elif p.is_compound: if p.optional: - obj_name = p.name.replace(".", "_") + "Object__"; - emit.Line("%s %s{};" % (p.optional.proto_no_cv, obj_name)) + obj_name = Normalize(p.name + "Object__"); + emit.Line("%s %s{};" % (p.optional.type_name, obj_name)) else: obj_name = p.name @@ -1787,18 +1867,6 @@ def ReadParameter(p): emit.Line("%s = reinterpret_cast<%s>(Interface(%s.%s, %s));" % \ (p.name, p.pure_proto, vars["reader"], p.read_rpc_type, p.interface_id.as_rvalue)) - elif p.is_buffer: - assert not p.optional - - CheckFrame(p) - CheckSize(p) - - if p.length and p.length.is_output: - emit.Line("%s = %s.%s;" % (p.length.as_lvalue, vars["reader"], p.read_rpc_type)) - else: - # No one's interested in the return length, perhaps it's sent via method's return value - emit.Line("%s.%s;" % (vars["reader"], p.read_rpc_type)) - else: param_ = "%s = %s.%s;" % (p.as_lvalue, vars["reader"], p.read_rpc_type) @@ -1968,7 +2036,7 @@ def EmitProxyMethod(index, method, interface_name, interface, prepared_params): method_type = ((Flatten(retval.identifier.Proto(), ns) + " ") if retval else "void ") method_qualifiers = ((" const" if "const" in method.qualifiers else "") + " override") - params_list = [(Flatten(p.identifier.Proto(), ns) + ((" " + p.name) if not method.stub else "")) for p in params] + params_list = [(Flatten(p.identifier.Signature((p.name if not method.stub else "")), ns)) for p in params] emit.Line("%s%s(%s)%s" % (method_type, method.name, ", ".join(params_list), method_qualifiers)) emit.Line("{") From 18b0a958f5735c03adf23a6d34ab037407db7bc6 Mon Sep 17 00:00:00 2001 From: sebaszm Date: Mon, 5 Aug 2024 17:33:12 +0200 Subject: [PATCH 2/2] fix unknown types passthrough --- ProxyStubGenerator/CppParser.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/ProxyStubGenerator/CppParser.py b/ProxyStubGenerator/CppParser.py index 705d1dc..baab8fc 100755 --- a/ProxyStubGenerator/CppParser.py +++ b/ProxyStubGenerator/CppParser.py @@ -118,7 +118,7 @@ def __init__(self, type): self.type = type def Proto(self): - return self.type + return TypeStr(self.type) def __str__(self): return self.Proto() @@ -130,16 +130,21 @@ def __init__(self, type, comment=""): self.comment = comment def Proto(self): - proto = self.comment if isinstance(self.type, list): + proto = "" + for e in self.type: - proto += " " + (e if isinstance(e, str) else str(e)) + proto += " " + (e.strip() if isinstance(e, str) else str(e)) + proto = proto.replace(" < ", "<").replace(" :: ", "::").replace(" >", ">") proto = proto.replace(" *", "*").replace(" &", "&").replace(" &&", "&&").replace(" ,",",").strip() else: - proto += str(self.type) + proto = str(self.type) - return proto + return ((self.comment + " " if self.comment else "") + proto) + + def __str__(self): + return self.Proto() def __repr__(self): return "undefined %s" % self.Proto() @@ -704,7 +709,7 @@ def Type(self): return self.type def Proto(self): - return str(self.Type()) + return TypeStr(self.type) def TypeStrong(self): if self.array: @@ -987,7 +992,7 @@ def __repr__(self): def TypeStr(s): - return str(Undefined(s, "/* undefined type */")) if not isinstance(s, Type) else str(s) + return str(Undefined(s, "/* undefined type */")) if isinstance(s, list) else str(s) def ValueStr(s):