Skip to content

Commit

Permalink
[JsonGen] Add lookup support
Browse files Browse the repository at this point in the history
  • Loading branch information
sebaszm committed May 7, 2024
1 parent 411d906 commit fa83856
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 16 deletions.
15 changes: 13 additions & 2 deletions JsonGenerator/source/documentation_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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:
Expand All @@ -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* object index 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:
Expand Down
52 changes: 40 additions & 12 deletions JsonGenerator/source/header_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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():
Expand Down Expand Up @@ -445,19 +454,19 @@ 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"):
raise CppParseError(var, "unnamed parameter, can't deduce parameter name (*2)")

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

Expand Down Expand Up @@ -485,7 +494,23 @@ 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))
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)

Expand Down Expand Up @@ -664,7 +689,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):
Expand Down Expand Up @@ -698,6 +723,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 = []

Expand Down
3 changes: 3 additions & 0 deletions JsonGenerator/source/json_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

Expand Down
33 changes: 31 additions & 2 deletions JsonGenerator/source/rpc_emitter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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<const %s*>(%s))" % (names.interface, names.impl) if const_cast else names.impl
implementation_object = "(static_cast<const %s*>(%s))" % (interface, impl) if const_cast and not lookup else impl
function_params = []

if contexted:
Expand Down Expand Up @@ -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 = []
Expand Down Expand Up @@ -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
Expand All @@ -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&")

Expand All @@ -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))

Expand Down
10 changes: 10 additions & 0 deletions ProxyStubGenerator/CppParser.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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):
Expand Down

0 comments on commit fa83856

Please sign in to comment.