diff --git a/JsonGenerator/source/documentation_generator.py b/JsonGenerator/source/documentation_generator.py index 1764c96..e1edadc 100644 --- a/JsonGenerator/source/documentation_generator.py +++ b/JsonGenerator/source/documentation_generator.py @@ -271,6 +271,7 @@ def MethodDump(method, props, classname, section, header, is_notification=False, orig_method = method method = props["alt"] if main_status and not alt_status and "alt" in props else method + orig_method2 = method MdHeader(method, 2, type, section) @@ -323,6 +324,9 @@ def MethodDump(method, props, classname, section, header, is_notification=False, events = [props["events"]] if isinstance(props["events"], str) else props["events"] MdParagraph("Also see: " + (", ".join(map(lambda x: link("event." + x), events)))) + if "@lookup" in props: + method = method.replace("::", "#1::") + if is_property: MdHeader("Value", 3) if "params" in props: @@ -339,21 +343,28 @@ def MethodDump(method, props, classname, section, header, is_notification=False, elif "summary" in props: props["result"]["description"] = props["summary"] + if "@lookup" in props: + MdParagraph("> The *%s* instance ID shell be passed within the designator, e.g. ``%s.1.%s%s``." % (props["@lookup"][2].lower(), classname, orig_method2.replace("::", "<%s>::" % props["@lookup"][2].lower()) , "@" + props["index"]["example"] if "index" in props else "")) + if "index" in props: if "name" not in props["index"] or "example" not in props["index"]: raise DocumentationError("'%s': index field requires 'name' and 'example' properties" % method) - extra_paragraph = "> The *%s* argument shall be passed as the index to the property, e.g. ``%s.1.%s@%s``.%s" % ( - props["index"]["name"].lower(), classname, method, props["index"]["example"], + extra_paragraph = "> The *%s* argument shall be passed as the index to the property, e.g. ``%s.1.%s@<%s>``.%s" % ( + props["index"]["name"].lower(), classname, method, props["index"]["name"].lower(), (" " + props["index"]["description"]) if "description" in props["index"] else "") if not extra_paragraph.endswith('.'): extra_paragraph += '.' MdParagraph(extra_paragraph) + else: MdHeader("Parameters", 3) + if "@lookup" in props: + MdParagraph("> The *%s* argument shell be passed within the designator, e.g. ``%s.1.%s``" % (props["@lookup"][2], classname, method)) + if "params" in props: ParamTable("params", props["params"]) else: diff --git a/JsonGenerator/source/header_loader.py b/JsonGenerator/source/header_loader.py index 3874834..fb6e240 100644 --- a/JsonGenerator/source/header_loader.py +++ b/JsonGenerator/source/header_loader.py @@ -121,7 +121,12 @@ def _EvaluateRpcFormat(obj): if interface.is_event: event_interfaces.add(CppInterface.Interface(interface, 0, file)) - def ResolveTypedef(type): + def ResolveTypedef(type, parent=type): + if isinstance(type, str): + raise CppParseError(parent, "%s: undefined type" % type) + elif isinstance(type, list): + raise CppParseError(parent, "%s: undefined type" % " ".join(type)) + return type.Resolve() def ConvertType(var): @@ -133,9 +138,9 @@ def ConvertType(var): var_type = ResolveTypedef(var.type) if isinstance(var_type, str): - raise CppParseError(var.type, "%s: undefined type" % var_type) + raise CppParseError(var, "%s: undefined type" % var_type) elif isinstance(var_type, list): - raise CppParseError(var.type, "%s: undefined type" % " ".join(var_type)) + raise CppParseError(var, "%s: undefined type" % " ".join(var_type)) cppType = var_type.Type() is_iterator = (isinstance(cppType, CppParser.Class) and cppType.is_iterator) @@ -181,9 +186,11 @@ def ConvertType(var): raise CppParseError(var, "passed iterators must not be constant") return result + # All other pointer types are not supported else: raise CppParseError(var, "unable to convert C++ type to JSON type: %s - input pointer allowed only on interator or C-style buffer" % cppType.type) + # Primitives else: result = None @@ -291,12 +298,14 @@ def GenerateObject(ctype, was_typdef): if cppType.full_name == "::WPEFramework::Core::JSONRPC::Context": result = "@context", {} - else: + elif (cppType.vars and not cppType.methods) or not verify: result = GenerateObject(cppType, isinstance(var.type.Type(), CppParser.Typedef)) + else: + raise CppParseError(var, "unable to convert this C++ class to JSON type: %s" % cppType.type) # All other types are not supported else: - raise CppParseError(var, "unable to convert C++ type to JSON type: %s" % cppType.type) + raise CppParseError(var, "unable to convert this C++ type to JSON type: %s" % cppType.type) if result: if var_type.IsPointer(): @@ -445,12 +454,6 @@ def BuildResult(vars, is_property=False): var_type = ResolveTypedef(var.type) if var.meta.output: - if var_type.IsValue(): - raise CppParseError(var, "parameter marked with @out tag must be either a reference or a pointer") - - if var_type.IsConst(): - raise CppParseError(var, "parameter marked with @out tag must not be const") - var_name = "value" if is_property else (var.meta.text if var.meta.text else var.name.lower()) if var_name.startswith("__unnamed"): @@ -458,6 +461,12 @@ def BuildResult(vars, is_property=False): properties[var_name] = ConvertParameter(var) + if var_type.IsValue(): + raise CppParseError(var, "parameter marked with @out tag must be either a reference or a pointer") + + if var_type.IsConst(): + raise CppParseError(var, "parameter marked with @out tag must not be const") + if not is_property and not var.name.startswith("@_") and not var.name.startswith("__unnamed"): properties[var_name]["@originalname"] = var.name @@ -485,7 +494,28 @@ def BuildResult(vars, is_property=False): else: prefix = "" + faces = [] + faces.append((None, prefix, face.obj.methods)) + for method in face.obj.methods: + if method.retval.meta.lookup: + var_type = ResolveTypedef(method.retval.type, method) + + _prefix = ((method.retval.meta.lookup if (method.retval.meta.lookup and method.retval.meta.lookup != "*") else var_type.Type().name[1:].lower()) + "::") + + if isinstance(var_type.Type(), CppParser.Class): + faces.append(([StripFrameworkNamespace(var_type.type.full_name), method.name, method.vars[0].name], _prefix, var_type.Type().methods)) + + if "@lookups" not in schema: + schema["@lookups"] = [] + + schema["@lookups"].append(faces[-1][0][0]) + else: + raise CppParseError(method, "lookup method for an unknown class") + + + for lookup_method, prefix, _methods in faces: + for method in _methods: event_params = EventParameters(method.vars) @@ -664,7 +694,7 @@ def BuildResult(vars, is_property=False): else: raise CppParseError(method, "property method must have one parameter") - elif method.IsVirtual() and not method.IsDestructor() and not event_params: + elif method.IsVirtual() and not method.IsDestructor() and not event_params and not method.retval.meta.lookup: var_type = ResolveTypedef(method.retval.type) if var_type and ((isinstance(var_type.Type(), CppParser.Integer) and (var_type.Type().size == "long")) or not verify): @@ -698,6 +728,9 @@ def BuildResult(vars, is_property=False): if method.retval.meta.details: obj["description"] = method.retval.meta.details.strip() + if lookup_method: + obj["@lookup"] = lookup_method + if method.retval.meta.retval: errors = [] @@ -845,11 +878,6 @@ def BuildResult(vars, is_property=False): if events: schema["events"] = events - if config.DUMP_JSON: - print("\n// JSON interface for {} -----------".format(face.obj.name)) - print(json.dumps(schema, indent=2)) - print("// ----------------\n") - return schema schemas = [] @@ -860,6 +888,15 @@ def BuildResult(vars, is_property=False): if schema: schemas.append(schema) + for s in schemas: + lookups = s["@lookups"] if "@lookups" in s else [] + for l in lookups: + for s2 in schemas: + if StripFrameworkNamespace(s2["@fullname"]) == l: + del s2["@generated"] + + schemas = [s for s in schemas if "@generated" in s] + return schemas, [] def LoadInterface(file, log, all = False, include_paths = []): @@ -883,6 +920,13 @@ def LoadInterface(file, log, all = False, include_paths = []): if not schemas: log.Info("No interfaces found") + else: + if config.DUMP_JSON: + for s in schemas: + print("\n// JSON interface for %s -----------" % s["@fullname"]) + print(json.dumps(s, indent=2)) + print("// ----------------\n") + return schemas, includes except CppParser.ParserError as ex: diff --git a/JsonGenerator/source/json_loader.py b/JsonGenerator/source/json_loader.py index 08e8244..fa516d4 100644 --- a/JsonGenerator/source/json_loader.py +++ b/JsonGenerator/source/json_loader.py @@ -760,6 +760,9 @@ def __init__(self, name, parent, schema, included=None, property=False): if "@originalname" in schema: method_schema["@originalname"] = schema["@originalname"] + if "@lookup" in schema: + method_schema["@lookup"] = schema["@lookup"] + if "hint" in schema: method_schema["hint"] = schema["hint"] diff --git a/JsonGenerator/source/rpc_emitter.py b/JsonGenerator/source/rpc_emitter.py index 642ab6e..821f5c8 100644 --- a/JsonGenerator/source/rpc_emitter.py +++ b/JsonGenerator/source/rpc_emitter.py @@ -655,6 +655,18 @@ def _Invoke(params, response, parent="", repsonse_parent="", const_cast=False): # Emit call to the implementation if not is_json_source: # Full automatic mode + + impl = names.impl + interface = names.interface + + if lookup: + impl = "_lookup" + impl + interface = lookup[0] + emit.Line("%s%s* const %s = %s->%s(%s);" % ("const " if const_cast else "", lookup[0], impl, names.impl,lookup[1], lookup[2])) + emit.Line() + emit.Line("if (%s != nullptr) {" % impl) + emit.Indent() + conditions = [] for _, [arg, _] in sorted_vars: @@ -671,7 +683,7 @@ def _Invoke(params, response, parent="", repsonse_parent="", const_cast=False): emit.Line("if (%s) {" % " && ".join(conditions)) emit.Indent() - implementation_object = "(static_cast(%s))" % (names.interface, names.impl) if const_cast else names.impl + implementation_object = "(static_cast(%s))" % (interface, impl) if const_cast and not lookup else impl function_params = [] if contexted: @@ -709,6 +721,16 @@ def _Invoke(params, response, parent="", repsonse_parent="", const_cast=False): emit.Unindent() emit.Line("}") + if lookup: + emit.Line("%s->Release();" % impl) + emit.Unindent() + emit.Line("}") + emit.Line("else {") + emit.Indent() + emit.Line("%s = Core::ERROR_UNKNOWN_KEY;" % error_code.temp_name) + emit.Unindent() + emit.Line("}") + # Semi-automatic mode else: parameters = [] @@ -809,6 +831,7 @@ def _Invoke(params, response, parent="", repsonse_parent="", const_cast=False): optional_checked = False index_name = m.index.local_name if indexed else None index_name_converted = None + lookup = m.schema.get("@lookup") if is_property: # Normalize property params/repsonse to match methods @@ -831,12 +854,15 @@ def _Invoke(params, response, parent="", repsonse_parent="", const_cast=False): # Emit method prologue template_params = [ params.cpp_type, response.cpp_type ] - if indexed or contexted: + if indexed or contexted or lookup: function_params = [] if contexted: function_params.append("const %s&" % names.context_alias) + if lookup: + function_params.append("const uint32_t") + if indexed: function_params.append("const string&") @@ -856,6 +882,9 @@ def _Invoke(params, response, parent="", repsonse_parent="", const_cast=False): if contexted: lambda_params.append("const %s& %s" % (names.context_alias, names.context)) + if lookup: + lambda_params.append("const uint32_t %s" % (lookup[2])) + if indexed: lambda_params.append("const string& %s" % (index_name)) diff --git a/ProxyStubGenerator/CppParser.py b/ProxyStubGenerator/CppParser.py index b08a8ab..552ea78 100755 --- a/ProxyStubGenerator/CppParser.py +++ b/ProxyStubGenerator/CppParser.py @@ -95,6 +95,7 @@ def __init__(self): self.length = None self.maxlength = None self.interface = None + self.lookup = None self.alt = None self.alt_is_deprecated = None self.alt_is_obsolete = None @@ -388,6 +389,13 @@ def __init__(self, parent_block, parent, string, valid_specifiers, tags_allowed= self.meta.decorators.append("endmarker") elif tag == "PROPERTY": self.meta.is_property = True + elif tag == "LOOKUP": + self.meta.lookup = string[i + 1] + if self.meta.lookup: + self.meta.lookup = self.meta.lookup[0] + else: + self.meta.lookup = "*" + skip = 1 elif tag == "BRIEF": self.meta.brief = string[i + 1] skip = 1 @@ -1652,6 +1660,8 @@ def _find(word, string): tagtokens.append("@INDEX") if _find("@property", token): tagtokens.append("@PROPERTY") + if _find("@lookup", token): + tagtokens.append(__ParseParameterValue(token, "@lookup", False)) if _find("@deprecated", token): tagtokens.append("@DEPRECATED") if _find("@obsolete", token):