From fa286444b778842ad1b5e546537dac520fe47691 Mon Sep 17 00:00:00 2001
From: sebaszm <45654185+sebaszm@users.noreply.github.com>
Date: Fri, 27 Dec 2024 15:23:44 +0100
Subject: [PATCH] [JsonGen] Automatic object lookup (#140)
* [JsonGen] Automatic object lookup
* Allow const methods on interfaces
* Update emitter.py
* amend documentation
---
.../source/documentation_generator.py | 18 +-
JsonGenerator/source/emitter.py | 2 +-
JsonGenerator/source/header_loader.py | 49 +-
JsonGenerator/source/json_loader.py | 2 +-
JsonGenerator/source/rpc_emitter.py | 1188 ++++++++++-------
ProxyStubGenerator/StubGenerator.py | 2 +-
6 files changed, 724 insertions(+), 537 deletions(-)
diff --git a/JsonGenerator/source/documentation_generator.py b/JsonGenerator/source/documentation_generator.py
index 211a869..3b3aa44 100644
--- a/JsonGenerator/source/documentation_generator.py
+++ b/JsonGenerator/source/documentation_generator.py
@@ -177,17 +177,25 @@ def _TableObj(name, obj, parentName="", parent=None, prefix="", parentOptional=F
if d["type"] == "string" or is_buffer:
str_text = "Decoded data" if d.get("encode") else "String"
+ val_text = "bytes" if d.get("encode") else "chars"
+
if d["range"][0]:
- row += italics("%s length must be in range [%s..%s] chars." % (str_text, d["range"][0], d["range"][1]))
+ row += italics("%s length must be in range [%s..%s] %s." % (str_text, d["range"][0], d["range"][1], val_text))
else:
- row += italics("%s length must be at most %s chars." % (str_text, d["range"][1]))
+ row += italics("%s length must be at most %s %s." % (str_text, d["range"][1], val_text))
else:
row += italics("Value must be in range [%s..%s]." % (d["range"][0], d["range"][1]))
if obj.get("@extract"):
row += " " + italics("(if only one element is present then the array will be omitted)")
- MdRow([prefix, "opaque object" if obj.get("opaque") else "string (base64)" if obj.get("encode") else obj["type"], "optional" if optional else "mandatory", row])
+ if obj.get("@lookupid"):
+ row += "
"
+ row += italics("This item is an instance ID.")
+
+ obj_type = "opaque object" if obj.get("opaque") else ("string (%s)" % obj.get("encode")) if obj.get("encode") else obj["type"]
+
+ MdRow([prefix, obj_type, "optional" if optional else "mandatory", row])
if obj["type"] == "object":
if "required" not in obj and name and len(obj["properties"]) > 1:
@@ -398,13 +406,13 @@ def MethodDump(method, props, classname, section, header, is_notification=False,
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"][0]["example"] if "index" in props else ""))
+ MdParagraph("> The *%s* instance ID shell be passed within the designator, e.g. ``%s.1.%s%s``." % (props["@lookup"]["prefix"].lower(), classname, orig_method2.replace("::", "<%s>::" % props["@lookup"][2].lower()) , "@" + props["index"][0]["example"] if "index" in props else ""))
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))
+ MdParagraph("> The *%s* instance ID shell be passed within the designator, e.g. ``%s.1.%s``." % (props["@lookup"]["prefix"].lower(), classname, method))
if "params" in props:
ParamTable("params", props["params"])
diff --git a/JsonGenerator/source/emitter.py b/JsonGenerator/source/emitter.py
index 90d45a1..412f54d 100644
--- a/JsonGenerator/source/emitter.py
+++ b/JsonGenerator/source/emitter.py
@@ -16,7 +16,7 @@
# limitations under the License.
class Emitter():
- def __init__(self, file_name, indent_size, max_line_length = 220, autoindent=False):
+ def __init__(self, file_name, indent_size, max_line_length = 600, autoindent=False):
self.file = open(file_name, "w") if file_name else None
self.indent_size = indent_size
self.indent = 0
diff --git a/JsonGenerator/source/header_loader.py b/JsonGenerator/source/header_loader.py
index 62afbb0..e04bea8 100644
--- a/JsonGenerator/source/header_loader.py
+++ b/JsonGenerator/source/header_loader.py
@@ -194,6 +194,7 @@ def _EvaluateRpcFormat(obj):
if face.obj.is_json:
schema["mode"] = "auto"
rpc_format = _EvaluateRpcFormat(face.obj)
+ assert not face.obj.is_event
else:
rpc_format = config.RpcFormat.COLLAPSED
@@ -253,6 +254,8 @@ def compute_name(obj, arg, relay=None):
clash_msg = "JSON-RPC name clash detected"
+ passed_interfaces = []
+
event_interfaces = set()
for interface in face.obj.classes:
@@ -476,15 +479,20 @@ def GenerateObject(ctype, was_typdef):
return "object", { "properties": properties, "required": required }
- if cppType.full_name == "::%s::Core::JSONRPC::Context" % config.FRAMEWORK_NAMESPACE:
- result = "@context", {}
+ if "Core::JSONRPC::Context" in cppType.full_name:
+ result = [ "@context", {} ]
elif (cppType.vars and not cppType.methods) or not verify:
result = GenerateObject(cppType, isinstance(var.type.Type(), CppParser.Typedef))
+ elif cppType.is_json:
+ prefix = (cppType.name[1:] if cppType.name[0] == 'I' else cppType.name)
+ result = [ "integer", { "size": 32, "signed": False, "@lookupid": prefix } ]
+ if not [x for x in passed_interfaces if x["name"] == cppType.full_name]:
+ passed_interfaces.append({ "name": cppType.full_name, "id": "@generate", "type": "uint32_t", "prefix": prefix})
else:
if cppType.is_iterator:
raise CppParseError(var, "iterators must be passed by pointer: %s" % cppType.type)
else:
- raise CppParseError(var, "unable to convert this C++ class to JSON type: %s (passing an interface is not possible)" % cppType.type)
+ raise CppParseError(var, "unable to convert this C++ class to JSON type: %s (passing a non-@json interface is not possible)" % cppType.type)
elif isinstance(cppType, CppParser.Optional):
result = ConvertType(cppType.optional, meta=var.meta)
@@ -737,8 +745,9 @@ def BuildResult(vars, is_property=False, test=False):
prefix = ""
faces = []
- faces.append((None, prefix, face.obj.methods))
+ faces.append((prefix, face.obj.methods))
+ """
for method in face.obj.methods:
if method.retval.meta.lookup:
var_type = ResolveTypedef(method.retval.type, method)
@@ -754,9 +763,9 @@ def BuildResult(vars, is_property=False, test=False):
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 prefix, _methods in faces:
for method in _methods:
if not method.IsVirtual() or method.IsDestructor():
@@ -942,7 +951,7 @@ def BuildResult(vars, is_property=False, test=False):
else:
raise CppParseError(method, "property method must have one parameter")
- elif not event_params and not method.retval.meta.lookup:
+ elif not event_params:
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):
@@ -976,9 +985,6 @@ def BuildResult(vars, is_property=False, test=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 = []
@@ -1115,6 +1121,9 @@ def BuildResult(vars, is_property=False, test=False):
events[prefix + method_name] = obj
+ if passed_interfaces:
+ schema["@interfaces"] = passed_interfaces
+
if methods:
schema["methods"] = methods
@@ -1124,6 +1133,7 @@ def BuildResult(vars, is_property=False, test=False):
if events:
schema["events"] = events
+
return schema
schemas = []
@@ -1137,13 +1147,18 @@ def BuildResult(vars, is_property=False, test=False):
scanned.append(face.obj.full_name)
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]
+ for face in (s["@interfaces"] if "@interfaces" in s else []):
+ for ss in schemas:
+ if ss["@fullname"] == face["name"] and "methods" in ss:
+ methods = ss["methods"]
+ for k,_ in methods.items():
+ new_k = face["prefix"].lower() + "::" + k
+ s["methods"][new_k] = methods.pop(k)
+ s["methods"][new_k]["@lookup"] = face
+ ss["@generated"] = False
+
+
+ schemas = [s for s in schemas if s.get("@generated")]
return schemas, []
diff --git a/JsonGenerator/source/json_loader.py b/JsonGenerator/source/json_loader.py
index e550770..8623afb 100644
--- a/JsonGenerator/source/json_loader.py
+++ b/JsonGenerator/source/json_loader.py
@@ -975,7 +975,7 @@ def _AddMethods(section, schema, ctor):
_AddMethods("events", schema, lambda name, obj, method: JsonNotification(name, obj, method))
if not self.methods:
- raise JsonParseError("no methods, properties or events defined in %s" % self.print_name)
+ raise JsonParseError("no methods, properties or events defined in %s" % self.schema["@fullname"] if "@fullname" in self.schema else self.name)
@property
def root(self):
diff --git a/JsonGenerator/source/rpc_emitter.py b/JsonGenerator/source/rpc_emitter.py
index 2a2a927..ae802af 100644
--- a/JsonGenerator/source/rpc_emitter.py
+++ b/JsonGenerator/source/rpc_emitter.py
@@ -302,6 +302,12 @@ def _EmitVersionCode(emit, version):
def _EmitRpcCode(root, emit, ns, header_file, source_file, data_emitted):
+ def trim(identifier):
+ if identifier.startswith(ns):
+ return str(identifier).replace(ns + "::", "")
+ elif identifier.startswith("::" + config.FRAMEWORK_NAMESPACE):
+ return str(identifier).replace("::" + config.FRAMEWORK_NAMESPACE + "::", "")
+
def _EmitHandlerInterface(listener_events):
assert listener_events
@@ -316,6 +322,69 @@ def _EmitHandlerInterface(listener_events):
emit.Line("};")
emit.Line()
+ def _EmitStorageClass(interfaces):
+ assert interfaces
+
+ emit.Line("class LookupStorage {")
+ emit.Line("friend Register();")
+ emit.Line()
+ emit.Line("public:")
+ emit.Indent()
+ emit.Line("LookupStorage() = default;")
+ emit.Line("~LookupStorage() = default;")
+ emit.Line("LookupStorage(const LookupStorage&) = delete;")
+ emit.Line("LookupStorage(LookupStorage&&) = delete;")
+ emit.Line("LookupStorage& operator=(const LookupStorage&) = delete;")
+ emit.Line("LookupStorage& operator=(LookupStorage&&) = delete;")
+ emit.Line()
+ emit.Unindent()
+
+ emit.Line("public:")
+ emit.Indent()
+
+ for face in interfaces:
+ emit.Line("void Closed(%s*, const uint32_t channelId, const std::function& callback = nullptr) { %s.Closed(channelId, callback); }" % (trim(face["name"]), trim(face["name"]), face["prefix"]))
+
+ emit.Line()
+ emit.Unindent()
+ emit.Line("public:")
+ emit.Indent()
+
+ for face in interfaces:
+ emit.Line("PluginHost::LookupStorageType<%s, uint32_t> %s;" % (trim(face["name"]), face["prefix"]))
+
+ emit.Line()
+
+ emit.Unindent()
+ emit.Line("};")
+ emit.Line()
+
+
+ def _EmitStorageEvents(interfaces):
+ assert interfaces
+
+ emit.Line("namespace Link {")
+ emit.Indent()
+ emit.Line()
+
+ emit.Line("void Closed(LookupStorage* storage, const uint32& channelId, const std::function& callback = nullptr)")
+ emit.Line("{")
+ emit.Indent()
+ emit.Line("ASSERT(storage != nullptr);")
+ emit.Line("ASSERT(channelId != 0);")
+
+ for face in interfaces:
+ emit.Line()
+ emit.Line("storage->Closed((%s*){}, channelId, callback);" % trim(face["name"]))
+
+ emit.Unindent()
+ emit.Line("}")
+
+ emit.Unindent()
+ emit.Line()
+ emit.Line("}")
+ emit.Line()
+
def _EmitNoPushWarnings(prologue = True):
if prologue:
if not config.NO_PUSH_WARNING:
@@ -399,679 +468,714 @@ def _EmitEventStatusListenerRegistration(listener_events, legacy, prologue=True)
for event in listener_events:
emit.Line("%s.UnregisterEventStatusListener(%s);" % (names.module, Tstring(event.json_name)))
- is_json_source = source_file.endswith(".json")
-
- for i, ns_ in enumerate(ns.split("::")):
- if ns_ and i >= 2:
- emit.Indent()
-
- if "info" in root.schema and "namespace" in root.schema["info"]:
- emit.Indent()
-
- emit.Indent()
-
- _EmitVersionCode(emit, rpc_version.GetVersion(root.schema["info"] if "info" in root.schema else dict()))
-
- methods_and_properties = [x for x in root.properties if not isinstance(x, (JsonNotification))]
- methods = [x for x in methods_and_properties if not isinstance(x, (JsonNotification, JsonProperty))]
- events = [x for x in root.properties if isinstance(x, JsonNotification)]
- listener_events = [x for x in events if x.is_status_listener]
- alt_events = [x for x in events if x.alternative]
+ def _EmitIndexing(index, index_name):
+ index_checked = False
+ index_name_converted = None
+ index_name_optional = None
- names = DottedDict()
- names['module'] = "_module"
- names['impl'] = "_implementation__"
- names['handler'] = ("_handler_" if not is_json_source else names.impl)
- names['handler_interface'] = "IHandler"
- names['context'] = "_context_"
- names['namespace'] = ("J" + root.json_name)
- names['interface'] = (root.info["interface"] if "interface" in root.info else ("I" + root.json_name))
- names['jsonrpc_alias'] = ("PluginHost::%s" % ("JSONRPCSupportsEventStatus" if listener_events else "JSONRPC"))
- names['context_alias'] = "Core::JSONRPC::Context"
+ def _IsOptional(v):
+ return ((IsObjectOptional(v) and not IsObjectOptionalOrOpaque(v)))
- impl_required = methods_and_properties or listener_events
- module_required = (impl_required or (alt_events and not config.LEGACY_ALT))
+ def _IsLegacyOptional(v):
+ return (IsObjectOptionalOrOpaque(v))
- if listener_events and not is_json_source:
- _EmitHandlerInterface(listener_events)
+ def _EmitRestrictions(index_name, extra=None):
+ index_restrictions = Restrictions(json=False)
- _EmitNoPushWarnings(prologue=True)
+ if extra:
+ index_restrictions.extend(extra)
- if is_json_source:
- emit.Line("using JSONRPC = %s;" % names.jsonrpc_alias)
- emit.Line()
+ index_restrictions.append(index, override=index_name)
- impl_name = ((" " + names.impl) if impl_required else "")
+ if index_restrictions.count():
+ emit.Line("if (%s) {" % ( index_restrictions.join()))
+ emit.Indent()
+ emit.Line("%s = %s;" % (error_code.temp_name, CoreError("bad_request")))
+ emit.Unindent()
+ emit.Line("}")
- register_params = [ "%s& %s" % (names.jsonrpc_alias, names.module) ]
+ return index_restrictions.count()
- if is_json_source:
- register_params.append("IMPLEMENTATION&%s" % impl_name)
- emit.Line("template")
+ if _IsOptional(index) or _IsLegacyOptional(index):
- else:
- register_params.append("%s*%s" % (names.interface, impl_name))
+ if isinstance(index, JsonString):
+ if not _IsLegacyOptional(index) or index.default_value:
+ index_name_optional = index.TempName("opt_")
+ emit.Line("%s %s{};" %(index.cpp_native_type_opt, index_name_optional))
+ else:
+ index_name_optional = index.TempName("opt_")
+ emit.Line("%s %s{%s};" %(index.cpp_native_type_opt, index_name_optional, index.default_value if index.default_value else ""))
- if listener_events:
- register_params.append("%s* %s" % (names.handler_interface, names.handler))
+ if isinstance(index, JsonString):
+ if _IsOptional(index) or _IsLegacyOptional(index):
+ if _IsOptional(index) or index.default_value:
+ emit.Line("if (%s.empty() == false) {" % index_name)
+ emit.Indent()
- emit.Line("static void Register(%s)" % (", ".join(register_params)))
+ cnt = _EmitRestrictions(index_name)
+ if cnt:
+ emit.Line("else {")
+ emit.Indent()
- emit.Line("{")
- emit.Indent()
+ if _IsOptional(index) or index.default_value:
+ emit.Line("%s = %s;" % (index_name_optional, index_name))
- if not is_json_source:
- if methods_and_properties:
- emit.Line("ASSERT(%s != nullptr);" % names.impl)
+ if cnt:
+ emit.Unindent()
+ emit.Line("}")
- if listener_events:
- emit.Line("ASSERT(%s != nullptr);" % names.handler)
+ if index.default_value:
+ emit.Unindent()
+ emit.Line("}")
+ emit.Line("else {")
+ emit.Indent()
+ emit.Line("%s = %s;" %(index_name_optional, index.default_value))
- if methods_and_properties or listener_events:
- emit.Line()
+ if _IsOptional(index) or index.default_value:
+ emit.Unindent()
+ emit.Line("}")
+ else:
+ _EmitRestrictions(index_name, extra=("%s.empty() == true" % index_name))
+ index_checked = True # still have to close the bracket...
+ elif isinstance(index, (JsonInteger, JsonBoolean, JsonEnum)):
+ if _IsOptional(index) or _IsLegacyOptional(index):
+ emit.Line("if (%s.empty() == false) {" % index_name)
+ emit.Indent()
- emit.Line("%s.RegisterVersion(%s, Version::Major, Version::Minor, Version::Patch);" % (names.module, Tstring(names.namespace)))
+ index_name_converted = index.TempName("conv_")
- if module_required:
- emit.Line()
+ if isinstance(index, JsonEnum):
+ emit.Line("Core::EnumerateType<%s> %s(%s.c_str());" % (index.cpp_native_type, index_name_converted, index_name))
+ _EmitRestrictions(index_name_converted, extra="%s.IsSet() == false" % (index_name_converted))
+ index_name_converted += ".Value()"
+ else:
+ emit.Line("%s %s{};" % (index.cpp_native_type, index_name_converted))
+ _EmitRestrictions(index_name_converted, extra="Core::FromString(%s, %s) == false" % (index_name, index_name_converted))
- if alt_events and not config.LEGACY_ALT:
- _EmitAlternativeEventsRegistration(alt_events, prologue=True)
+ if _IsOptional(index) or _IsLegacyOptional(index):
+ emit.Line("else {")
+ emit.Indent()
+ emit.Line("%s = %s;" % (index_name_optional, index_name_converted))
+ emit.Unindent()
+ emit.Line("}")
+ emit.Unindent()
+ emit.Line("}")
- if methods_and_properties:
- emit.Line("// Register methods and properties...")
emit.Line()
- prototypes = []
-
- for m in methods_and_properties:
-
- def _EmitIndexing(index):
- nonlocal index_name
+ if index_name_optional:
+ index_name = index_name_optional
+ elif index_name_converted:
+ index_name = index_name_converted
- _index_checked = False
- _index_name_converted = None
- _index_name_optional = None
+ index_checked = ((index_name_converted != None) or index_checked)
- def _IsOptional(v):
- return ((IsObjectOptional(v) and not IsObjectOptionalOrOpaque(v)))
-
- def _IsLegacyOptional(v):
- return (IsObjectOptionalOrOpaque(v))
-
- def _EmitRestrictions(index_name, extra=None):
- _index_restrictions = Restrictions(json=False)
-
- if extra:
- _index_restrictions.extend(extra)
+ if index_checked:
+ emit.Line("if (%s == %s) {" % (error_code.temp_name, CoreError("none")))
+ emit.Indent()
- _index_restrictions.append(index, override=index_name)
+ return index_checked, index_name
- if _index_restrictions.count():
- emit.Line("if (%s) {" % ( _index_restrictions.join()))
- emit.Indent()
- emit.Line("%s = %s;" % (error_code.temp_name, CoreError("bad_request")))
- emit.Unindent()
- emit.Line("}")
+ def _BuildVars(params, response):
+ # Build param/response dictionaries (dictionaries will ensure they do not repeat)
+ vars = OrderedDict()
- return _index_restrictions.count()
-
- if _IsOptional(index) or _IsLegacyOptional(index):
+ if params:
+ if isinstance(params, JsonObject) and params.do_create:
+ for param in params.properties:
+ vars[param.local_name] = [param, "r"]
+ else:
+ vars[params.local_name] = [params, "r"]
- if isinstance(index, JsonString):
- if not _IsLegacyOptional(index) or index.default_value:
- _index_name_optional = index.TempName("opt_")
- emit.Line("%s %s{};" %(index.cpp_native_type_opt, _index_name_optional))
+ if response:
+ if isinstance(response, JsonObject) and response.do_create:
+ for resp in response.properties:
+ if resp.local_name not in vars:
+ vars[resp.local_name] = [resp, "w"]
+ else:
+ vars[resp.local_name][1] += "w"
+ else:
+ if response.local_name not in vars:
+ vars[response.local_name] = [response, "w"]
else:
- _index_name_optional = index.TempName("opt_")
- emit.Line("%s %s{%s};" %(index.cpp_native_type_opt, _index_name_optional, index.default_value if index.default_value else ""))
+ vars[response.local_name][1] += "w"
+
+ sorted_vars = sorted(vars.items(), key=lambda x: x[1][0].schema["@position"])
+
+ for _, [param, param_type] in sorted_vars:
+ param.flags = DottedDict()
+ param.flags.prefix = ""
+ param.access = param_type
+
+ if "encode" in param.schema:
+ param.flags.encode = param.schema["encode"]
+
+ if "@lookupid" in param.schema:
+ if param_type == "w":
+ param.flags.store_lookup = param.schema["@lookupid"]
+ elif param_type == "r":
+ param.flags.dispose_lookup = param.schema["@lookupid"]
+ elif param.schema.get("@bypointer"):
+ param.flags.prefix = "&"
+
+ # Tie buffer with length variables
+ for _, [param, _] in sorted_vars:
+ if isinstance(param, (JsonString, JsonArray)):
+ length_value = param.schema.get("@length")
+ array_size_value = param.schema.get("@arraysize")
+
+ if length_value:
+ for name, [var, type] in sorted_vars:
+ if name == length_value:
+ if type == "w":
+ raise RPCEmitterError("'%s': parameter pointed to by @length is output only" % param.name)
+ else:
+ var.flags.is_buffer_length = True
+ param.flags.length = var
+ break
- if isinstance(index, JsonString):
- if _IsOptional(index) or _IsLegacyOptional(index):
- if _IsOptional(index) or index.default_value:
- emit.Line("if (%s.empty() == false) {" % index_name)
- emit.Indent()
+ if not param.flags.length:
+ param.flags.size = length_value
- cnt = _EmitRestrictions(index_name)
- if cnt:
- emit.Line("else {")
- emit.Indent()
+ elif array_size_value:
+ param.flags.size = array_size_value
- if _IsOptional(index) or index.default_value:
- emit.Line("%s = %s;" % (_index_name_optional, index_name))
+ return sorted_vars
- if cnt:
- emit.Unindent()
- emit.Line("}")
+ def _Invoke(method, sorted_vars, params, response, parent="", repsonse_parent="", const_cast=False, param_const_cast=False, test_param=True, index=None, context=False):
- if index.default_value:
- emit.Unindent()
- emit.Line("}")
- emit.Line("else {")
- emit.Indent()
- emit.Line("%s = %s;" %(_index_name_optional, index.default_value))
+ index_name = index
- if _IsOptional(index) or index.default_value:
- emit.Unindent()
- emit.Line("}")
- else:
- _EmitRestrictions(index_name, extra=("%s.empty() == true" % index_name))
- _index_checked = True # still have to close the bracket...
+ restrictions = Restrictions(test_set=True)
+ call_conditions = Restrictions(test_set=False)
- elif isinstance(index, (JsonInteger, JsonBoolean, JsonEnum)):
- if _IsOptional(index) or _IsLegacyOptional(index):
- emit.Line("if (%s.empty() == false) {" % index_name)
- emit.Indent()
+ if params:
+ restrictions.append(params, override=params.local_name, test_set=test_param)
+
+ if restrictions.present():
+ emit.Line()
+ emit.Line("if (%s) {" % restrictions.join())
+ emit.Indent()
+ emit.Line("%s = %s;" % (error_code.temp_name, CoreError("bad_request")))
+ emit.Unindent()
+ emit.Line("}")
+ emit.Line("else {")
+ emit.Indent()
- _index_name_converted = index.TempName("conv_")
+ # Emit temporary variables and deserializing of JSON data
- if isinstance(index, JsonEnum):
- emit.Line("Core::EnumerateType<%s> %s(%s.c_str());" % (index.cpp_native_type, _index_name_converted, index_name))
- _EmitRestrictions(_index_name_converted, extra="%s.IsSet() == false" % (_index_name_converted))
- _index_name_converted += ".Value()"
- else:
- emit.Line("%s %s{};" % (index.cpp_native_type, _index_name_converted))
- _EmitRestrictions(_index_name_converted, extra="Core::FromString(%s, %s) == false" % (index_name, _index_name_converted))
+ for _, [param, param_type] in sorted_vars:
+ if param.flags.is_buffer_length:
+ continue
- if _IsOptional(index) or _IsLegacyOptional(index):
- emit.Line("else {")
- emit.Indent()
- emit.Line("%s = %s;" % (_index_name_optional, _index_name_converted))
- emit.Unindent()
- emit.Line("}")
- emit.Unindent()
- emit.Line("}")
+ is_readable = ("r" in param_type)
+ is_writeable = ("w" in param_type)
+ cv_qualifier = ("const " if not is_writeable else "") # don't consider volatile
- emit.Line()
+ # Take care of POD aggregation
+ cpp_name = ((parent + param.cpp_name) if parent else param.local_name)
- if _index_name_optional:
- index_name = _index_name_optional
- elif _index_name_converted:
- index_name = _index_name_converted
+ # Encoded JSON strings to C-style buffer
+ if isinstance(param, JsonString) and (param.flags.length or param.flags.size) and param.flags.encode:
+ conditions = Restrictions(reverse=True)
+ length_param = param.flags.length
- _index_checked = ((_index_name_converted != None) or _index_checked)
+ assert not param.optional
- if _index_checked:
- emit.Line("if (%s == %s) {" % (error_code.temp_name, CoreError("none")))
- emit.Indent()
+ if param.flags.length:
+ size = length_param.temp_name
+ length_cpp_name = parent + length_param.cpp_name
- return _index_checked
+ if length_param.optional and "r" in length_param.access:
+ emit.Line("%s %s{};" % (length_param.cpp_native_type_opt, size))
+ emit.Line("if (%s.IsSet() == true) { %s = %s.Value(); }" % (length_cpp_name, size, length_cpp_name))
+ else:
+ initializer = (length_cpp_name + ".Value()") if "r" in length_param.access else ""
+ emit.Line("%s %s{%s};" % (length_param.cpp_native_type_opt, size, initializer))
- def _Invoke(params, response, parent="", repsonse_parent="", const_cast=False, index=None, test_param=True, param_const_cast=False):
- vars = OrderedDict()
+ emit.Line("%s* %s{nullptr};" % (param.original_type, param.temp_name))
- _index_checks_emitted = _EmitIndexing(index) if index else False
+ if length_param.optional:
+ size += ".Value()"
- # Build param/response dictionaries (dictionaries will ensure they do not repeat)
- if params:
- if isinstance(params, JsonObject) and params.do_create:
- for param in params.properties:
- vars[param.local_name] = [param, "r"]
- else:
- vars[params.local_name] = [params, "r"]
+ conditions.check_set(length_param)
+ conditions.check_not_null(length_param)
- if response:
- if isinstance(response, JsonObject) and response.do_create:
- for resp in response.properties:
- if resp.local_name not in vars:
- vars[resp.local_name] = [resp, "w"]
- else:
- vars[resp.local_name][1] += "w"
+ if length_param.size > 16:
+ conditions.extend("(%s <= 0x400000) /* sanity! */" % size)
else:
- if response.local_name not in vars:
- vars[response.local_name] = [response, "w"]
- else:
- vars[response.local_name][1] += "w"
+ size = param.flags.size
+ emit.Line("%s %s[%s]{};" % (param.original_type, param.temp_name, size))
- sorted_vars = sorted(vars.items(), key=lambda x: x[1][0].schema["@position"])
+ emit.EnterBlock(conditions)
- for _, [param, param_type] in sorted_vars:
- param.flags = DottedDict()
- param.flags.prefix = ""
- param.access = param_type
+ if length_param:
+ emit.Line("%s = reinterpret_cast<%s*>(ALLOCA(%s));" % (param.temp_name, param.original_type, size))
+ emit.Line("ASSERT(%s != nullptr);" % param.temp_name)
- if param.schema.get("@bypointer"):
- param.flags.prefix = "&"
+ if is_readable:
+ if param.flags.encode == "base64":
+ if param.flags.size:
+ size_var = param.TempName("Size_")
+ emit.Line("uint16_t %s{%s};" % (size_var, size))
+ else:
+ size_var = size
- if "encode" in param.schema:
- param.flags.encode = param.schema["encode"]
+ emit.Line("Core::FromString(%s, %s, %s, nullptr);" % (cpp_name, param.temp_name, size_var))
+ else:
+ assert False, "unimplemented encoding: " + param.flags.encode
+ emit.ExitBlock(conditions)
- # Tie buffer with length variables
- for _, [param, _] in sorted_vars:
- if isinstance(param, (JsonString, JsonArray)):
- length_value = param.schema.get("@length")
- array_size_value = param.schema.get("@arraysize")
+ elif isinstance(param, JsonArray):
+ # Array to iterator
+ if param.iterator:
+ emit.Line("%s %s{};" % (param.cpp_native_type_opt, param.temp_name))
- if length_value:
- for name, [var, type] in sorted_vars:
- if name == length_value:
- if type == "w":
- raise RPCEmitterError("'%s': parameter pointed to by @length is output only" % param.name)
- else:
- var.flags.is_buffer_length = True
- param.flags.length = var
- break
-
- if not param.flags.length:
- param.flags.size = length_value
+ if is_readable:
+ elements_name = param.items.TempName("elements")
+ iterator_name = param.items.TempName("iterator")
+ impl_name = param.items.TempName("iteratorImplType")
- elif array_size_value:
- param.flags.size = array_size_value
+ if param.optional:
+ emit.Line("if (%s.IsSet() == true) {" % (cpp_name))
+ emit.Indent()
- restrictions = Restrictions(test_set=True)
+ emit.Line("std::list<%s> %s{};" % (param.items.cpp_native_type, elements_name))
+ emit.Line("auto %s = %s.Elements();" % (iterator_name, cpp_name))
+ emit.Line("while (%s.Next() == true) { %s.push_back(%s.Current()); }" % (iterator_name, elements_name, iterator_name))
+ impl = (param.iterator[:param.iterator.index('<')].replace("IIterator", "Iterator") + ("<%s>" % param.iterator))
+ emit.Line("using %s = %s;" % (impl_name, impl))
+ initializer = "Core::ServiceType<%s>::Create<%s>(std::move(%s))" % (impl_name, param.iterator, elements_name)
- if params:
- restrictions.append(params, override=params.local_name, test_set=test_param)
+ iterator = param.temp_name
+ if param.optional:
+ iterator += ".Value()"
- if restrictions.present():
- emit.Line()
- emit.Line("if (%s) {" % restrictions.join())
- emit.Indent()
- emit.Line("%s = %s;" % (error_code.temp_name, CoreError("bad_request")))
- emit.Unindent()
- emit.Line("}")
- emit.Line("else {")
- emit.Indent()
+ emit.Line("%s = %s;" % (param.temp_name, initializer))
+ emit.Line("ASSERT(%s != nullptr); " % iterator)
- # Emit temporary variables and deserializing of JSON data
+ if param_const_cast:
+ param.flags.cast = "static_cast<%s const&>(%s)" % (param.cpp_native_type_opt, param.temp_name)
- for _, [param, param_type] in sorted_vars:
- if param.flags.is_buffer_length:
- continue
+ if param.optional:
+ emit.Unindent()
+ emit.Line("}")
- is_readable = ("r" in param_type)
- is_writeable = ("w" in param_type)
- cv_qualifier = ("const " if not is_writeable else "") # don't consider volatile
+ emit.Line()
- # Take care of POD aggregation
- cpp_name = ((parent + param.cpp_name) if parent else param.local_name)
+ # array to bitmask
+ elif param.items.schema.get("@bitmask"):
+ if param.optional and is_readable:
+ emit.Line("%s %s{};" % (param.items.cpp_native_type_opt, param.temp_name))
+ emit.Line("if (%s.IsSet() == true) { %s = %s.Value(); }" % ( param.temp_name, cpp_name))
+ else:
+ initializer = cpp_name if is_readable else ""
+ emit.Line("%s%s %s{%s};" % (cv_qualifier, param.items.cpp_native_type, param.temp_name, initializer))
- # Encoded JSON strings to C-style buffer
- if isinstance(param, JsonString) and (param.flags.length or param.flags.size) and param.flags.encode:
- conditions = Restrictions(reverse=True)
+ # array to fixed array
+ elif (param.flags.size or param.flags.length):
+ items = param.items
length_param = param.flags.length
+ conditions = Restrictions(reverse=True)
assert not param.optional
- if param.flags.length:
+ if length_param:
size = length_param.temp_name
- length_cpp_name = parent + length_param.cpp_name
if length_param.optional and "r" in length_param.access:
+ length_cpp_name = parent + length_param.cpp_name
emit.Line("%s %s{};" % (length_param.cpp_native_type_opt, size))
emit.Line("if (%s.IsSet() == true) { %s = %s.Value(); }" % (length_cpp_name, size, length_cpp_name))
else:
- initializer = (length_cpp_name + ".Value()") if "r" in length_param.access else ""
+ initializer = (parent + length_param.cpp_name + ".Value()") if "r" in length_param.access else ""
emit.Line("%s %s{%s};" % (length_param.cpp_native_type_opt, size, initializer))
- emit.Line("%s* %s{nullptr};" % (param.original_type, param.temp_name))
-
- if length_param.optional:
- size += ".Value()"
+ emit.Line("%s* %s{};" % (items.cpp_native_type, param.temp_name))
conditions.check_set(length_param)
conditions.check_not_null(length_param)
- if length_param.size > 16:
- conditions.extend("(%s <= 0x400000) /* sanity! */" % size)
+ if length_param.optional:
+ size += ".Value()"
else:
size = param.flags.size
- emit.Line("%s %s[%s]{};" % (param.original_type, param.temp_name, size))
+ emit.Line("%s %s[%s]{};" % (param.items.cpp_native_type, param.temp_name, size))
emit.EnterBlock(conditions)
if length_param:
- emit.Line("%s = reinterpret_cast<%s*>(ALLOCA(%s));" % (param.temp_name, param.original_type, size))
+ emit.Line("%s = static_cast<%s*>(ALLOCA(%s));" % (param.temp_name, items.cpp_native_type, size))
emit.Line("ASSERT(%s != nullptr);" % param.temp_name)
if is_readable:
- if param.flags.encode == "base64":
- if param.flags.size:
- size_var = param.TempName("Size_")
- emit.Line("uint16_t %s{%s};" % (size_var, size))
- else:
- size_var = size
-
- emit.Line("Core::FromString(%s, %s, %s, nullptr);" % (cpp_name, param.temp_name, size_var))
- else:
- assert False, "unimplemented encoding: " + param.flags.encode
+ emit.EnterBlock()
+ emit.Line("uint16_t i = 0;")
+ emit.Line("auto it = %s.Elements();" % (parent + param.cpp_name))
+ emit.Line("while ((it.Next() == true) && (i < %s)) { %s[i++] = it.Current(); }" % (size, param.items.temp_name))
+ emit.ExitBlock()
emit.ExitBlock(conditions)
- elif isinstance(param, JsonArray):
- # Array to iterator
- if param.iterator:
- emit.Line("%s %s{};" % (param.cpp_native_type_opt, param.temp_name))
+ elif is_json_source:
+ response_cpp_name = (response_parent + param.cpp_name) if response_parent else param.local_name
+ initializer = ("(%s)" if isinstance(param, JsonObject) else "{%s}") % (response_cpp_name if is_writeable else cpp_name)
- if is_readable:
- elements_name = param.items.TempName("elements")
- iterator_name = param.items.TempName("iterator")
- impl_name = param.items.TempName("iteratorImplType")
+ if is_readable and is_writeable:
+ emit.Line("%s = %s;" % (response_cpp_name, cpp_name))
- if param.optional:
- emit.Line("if (%s.IsSet() == true) {" % (cpp_name))
- emit.Indent()
+ emit.Line("%s%s %s%s;" % (cv_qualifier, (param.cpp_type + "&") if is_json_source else param.cpp_native_type, param.temp_name, initializer))
+ else:
+ raise RPCEmitterError("arrays must be iterators: %s" % param.json_name)
- emit.Line("std::list<%s> %s{};" % (param.items.cpp_native_type, elements_name))
- emit.Line("auto %s = %s.Elements();" % (iterator_name, cpp_name))
- emit.Line("while (%s.Next() == true) { %s.push_back(%s.Current()); }" % (iterator_name, elements_name, iterator_name))
- impl = (param.iterator[:param.iterator.index('<')].replace("IIterator", "Iterator") + ("<%s>" % param.iterator))
- emit.Line("using %s = %s;" % (impl_name, impl))
- initializer = "Core::ServiceType<%s>::Create<%s>(std::move(%s))" % (impl_name, param.iterator, elements_name)
+ # All Other
+ else:
+ if is_json_source:
+ response_cpp_name = (response_parent + param.cpp_name) if response_parent else param.local_name
+ initializer = ("(%s)" if isinstance(param, JsonObject) else "{%s}") % (response_cpp_name if is_writeable else cpp_name)
- iterator = param.temp_name
- if param.optional:
- iterator += ".Value()"
+ if is_readable and is_writeable:
+ emit.Line("%s = %s;" % (response_cpp_name, cpp_name))
+ else:
+ initializer = (("(%s)" if isinstance(param, JsonObject) else "{%s}") % cpp_name) if is_readable and not param.convert else "{}"
+
+ if param.optional and is_readable and (param.default_value == None or not parent):
+ emit.Line("%s %s{};" % (param.cpp_native_type_opt, param.temp_name))
+ emit.Line("if (%s.IsSet() == true) {" % (cpp_name))
+ emit.Indent()
+ emit.Line("%s = %s;" % (param.temp_name, cpp_name))
+ emit.Unindent()
- emit.Line("%s = %s;" % (param.temp_name, initializer))
- emit.Line("ASSERT(%s != nullptr); " % iterator)
+ if param.default_value:
+ emit.Line("}")
+ emit.Line("else {")
+ emit.Indent()
+ emit.Line("%s = %s;" % (param.temp_name, param.default_value))
+ emit.Unindent()
- if param_const_cast:
- param.flags.cast = "static_cast<%s const&>(%s)" % (param.cpp_native_type_opt, param.temp_name)
+ emit.Line("}")
+ emit.Line()
+ else:
+ emit.Line("%s%s %s%s;" % (cv_qualifier, (param.cpp_type + "&") if is_json_source else param.cpp_native_type_opt, param.temp_name, initializer))
- if param.optional:
- emit.Unindent()
- emit.Line("}")
+ if param.convert and is_readable:
+ emit.Line((param.convert + ";") % (param.temp_name, cpp_name))
- emit.Line()
+ if param.flags.store_lookup or param.flags.dispose_lookup:
+ emit.Line("%s* _real%s{};" % (param.original_type, param.temp_name))
+ param.flags.prefix += "_real"
- # array to bitmask
- elif param.items.schema.get("@bitmask"):
- if param.optional and is_readable:
- emit.Line("%s %s{};" % (param.items.cpp_native_type_opt, param.temp_name))
- emit.Line("if (%s.IsSet() == true) { %s = %s.Value(); }" % ( param.temp_name, cpp_name))
- else:
- initializer = cpp_name if is_readable else ""
- emit.Line("%s%s %s{%s};" % (cv_qualifier, param.items.cpp_native_type, param.temp_name, initializer))
+ if param.flags.dispose_lookup:
+ emit.Line("_real%s = %s->%s.Dispose(%s, %s);" % (param.temp_name, names.storage, param.flags.dispose_lookup, names.context, param.temp_name))
+ call_conditions.extend("_real%s != nullptr" % param.temp_name)
- # array to fixed array
- elif (param.flags.size or param.flags.length):
- items = param.items
- length_param = param.flags.length
- conditions = Restrictions(reverse=True)
- assert not param.optional
+ # Emit call to the implementation
+ if not is_json_source: # Full automatic mode
- if length_param:
- size = length_param.temp_name
+ impl = names.impl
+ interface = names.interface
- if length_param.optional and "r" in length_param.access:
- length_cpp_name = parent + length_param.cpp_name
- emit.Line("%s %s{};" % (length_param.cpp_native_type_opt, size))
- emit.Line("if (%s.IsSet() == true) { %s = %s.Value(); }" % (length_cpp_name, size, length_cpp_name))
- else:
- initializer = (parent + length_param.cpp_name + ".Value()") if "r" in length_param.access else ""
- emit.Line("%s %s{%s};" % (length_param.cpp_native_type_opt, size, initializer))
+ if lookup:
+ impl = ("_%s%s" % (lookup["prefix"], impl)).lower()
+ interface = trim(lookup["name"])
+ emit.Line("%s%s* const %s = %s->%s.Lookup(%s, %s);" % ("const " if const_cast else "", interface, impl, names.storage, lookup["prefix"], names.context, names.id))
+ call_conditions.extend("%s != nullptr" % impl)
- emit.Line("%s* %s{};" % (items.cpp_native_type, param.temp_name))
+ implementation_object = "(static_cast(%s))" % (interface, impl) if const_cast and not lookup else impl
+ function_params = []
- conditions.check_set(length_param)
- conditions.check_not_null(length_param)
+ if context:
+ function_params.append(names.context)
- if length_param.optional:
- size += ".Value()"
- else:
- size = param.flags.size
- emit.Line("%s %s[%s]{};" % (param.items.cpp_native_type, param.temp_name, size))
+ if index_name:
+ function_params.append(index_name)
- emit.EnterBlock(conditions)
+ for _, [param, _] in sorted_vars:
+ function_params.append("%s%s" % (param.flags.prefix, (param.flags.cast if param.flags.cast else param.temp_name)))
- if length_param:
- emit.Line("%s = static_cast<%s*>(ALLOCA(%s));" % (param.temp_name, items.cpp_native_type, size))
- emit.Line("ASSERT(%s != nullptr);" % param.temp_name)
+ emit.Line()
- if is_readable:
- emit.EnterBlock()
- emit.Line("uint16_t i = 0;")
- emit.Line("auto it = %s.Elements();" % (parent + param.cpp_name))
- emit.Line("while ((it.Next() == true) && (i < %s)) { %s[i++] = it.Current(); }" % (size, param.items.temp_name))
- emit.ExitBlock()
+ if call_conditions.count():
+ emit.Line("if (%s) {" % call_conditions.join())
+ emit.Indent()
- emit.ExitBlock(conditions)
+ assert error_code.temp_name
+ assert method.function_name
+ emit.Line("%s = %s->%s(%s);" % (error_code.temp_name, implementation_object, method.function_name, ", ".join(function_params)))
- elif is_json_source:
- response_cpp_name = (response_parent + param.cpp_name) if response_parent else param.local_name
- initializer = ("(%s)" if isinstance(param, JsonObject) else "{%s}") % (response_cpp_name if is_writeable else cpp_name)
+ if lookup:
+ emit.Line("%s->Release();" % impl)
- if is_readable and is_writeable:
- emit.Line("%s = %s;" % (response_cpp_name, cpp_name))
+ if call_conditions.count():
+ emit.Unindent()
+ emit.Line("}")
+ emit.Line("else {")
+ emit.Indent()
+ emit.Line("%s = %s;" % (error_code.temp_name, CoreError("unknown_key")))
+ emit.Unindent()
+ emit.Line("}")
- emit.Line("%s%s %s%s;" % (cv_qualifier, (param.cpp_type + "&") if is_json_source else param.cpp_native_type, param.temp_name, initializer))
- else:
- raise RPCEmitterError("arrays must be iterators: %s" % param.json_name)
+ # Semi-automatic mode
+ else:
+ parameters = []
- # All Other
- else:
- if is_json_source:
- response_cpp_name = (response_parent + param.cpp_name) if response_parent else param.local_name
- initializer = ("(%s)" if isinstance(param, JsonObject) else "{%s}") % (response_cpp_name if is_writeable else cpp_name)
+ if index_name:
+ parameters.append(index_name)
- if is_readable and is_writeable:
- emit.Line("%s = %s;" % (response_cpp_name, cpp_name))
- else:
- initializer = (("(%s)" if isinstance(param, JsonObject) else "{%s}") % cpp_name) if is_readable and not param.convert else "{}"
+ for _, [ param, _ ] in sorted_vars:
+ parameters.append("%s" % (param.temp_name))
- if param.optional and is_readable and (param.default_value == None or not parent):
- emit.Line("%s %s{};" % (param.cpp_native_type_opt, param.temp_name))
- emit.Line("if (%s.IsSet() == true) {" % (cpp_name))
- emit.Indent()
- emit.Line("%s = %s;" % (param.temp_name, cpp_name))
- emit.Unindent()
+ if const_cast:
+ emit.Line("%s = (static_cast(%s)).%s(%s);" % (error_code.temp_name, names.impl, m.function_name, ", ".join(parameters)))
+ else:
+ emit.Line("%s = %s.%s(%s);" % (error_code.temp_name, names.impl, method.function_name, ", ".join(parameters)))
- if param.default_value:
- emit.Line("}")
- emit.Line("else {")
- emit.Indent()
- emit.Line("%s = %s;" % (param.temp_name, param.default_value))
- emit.Unindent()
+ parameters = []
- emit.Line("}")
- emit.Line()
- else:
- emit.Line("%s%s %s%s;" % (cv_qualifier, (param.cpp_type + "&") if is_json_source else param.cpp_native_type_opt, param.temp_name, initializer))
+ if index_name:
+ parameters.append("const %s& %s" % (any_index.cpp_native_type, index_name))
- if param.convert and is_readable:
- emit.Line((param.convert + ";") % (param.temp_name, cpp_name))
+ for _, [ param, type ] in sorted_vars:
+ parameters.append("%s%s& %s" % ("const " if type == "r" else "", param.cpp_type, param.local_name))
- # Emit call to the implementation
- if not is_json_source: # Full automatic mode
+ prototypes.append(["uint32_t %s(%s)%s" % (method.function_name, ", ".join(parameters), (" const" if (const_cast or (isinstance(method, JsonProperty) and method.readonly)) else "")), CoreError("none")])
- impl = names.impl
- interface = names.interface
+ emit.Line()
- 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()
+ # Emit result handling and serialization to JSON data
- implementation_object = "(static_cast(%s))" % (interface, impl) if const_cast and not lookup else impl
- function_params = []
+ if response and not is_json_source:
+ emit.Line("if (%s == %s) {" % (error_code.temp_name, CoreError("none")))
+ emit.Indent()
- if contexted:
- function_params.append(names.context)
+ for _, [param, param_type] in sorted_vars:
+ if "w" not in param_type:
+ continue
- if indexed:
- function_params.append(index_name)
+ rhs = param.temp_name
+ cpp_name = (repsonse_parent + param.cpp_name) if repsonse_parent else param.local_name
- for _, [param, _] in sorted_vars:
- function_params.append("%s%s" % (param.flags.prefix, (param.flags.cast if param.flags.cast else param.temp_name)))
+ # Special case for C-style buffers disguised as base64-encoded strings
+ if isinstance(param, JsonString) and (param.flags.length or param.flags.size) and param.flags.encode:
+ length_param = param.flags.length
- emit.Line()
- emit.Line("%s = %s->%s(%s);" % (error_code.temp_name, implementation_object, m.function_name, ", ".join(function_params)))
+ conditions = Restrictions(reverse=True)
- if lookup:
- emit.Line("%s->Release();" % impl)
- emit.Unindent()
- emit.Line("}")
- emit.Line("else {")
- emit.Indent()
- emit.Line("%s = %s;" % (error_code.temp_name, CoreError("bad_request")))
- emit.Unindent()
- emit.Line("}")
+ if length_param:
+ conditions.check_set(length_param)
+ conditions.check_not_null(length_param)
- # Semi-automatic mode
- else:
- parameters = []
+ size = length_param.temp_name
- if indexed:
- parameters.append(index_name)
+ if length_param.optional:
+ # the length variable determines optionality of the buffer
+ size += ".Value()"
+ else:
+ size = param.flags.size
- for _, [ param, _ ] in sorted_vars:
- parameters.append("%s" % (param.temp_name))
+ emit.EnterBlock(conditions)
- if const_cast:
- emit.Line("%s = (static_cast(%s)).%s(%s);" % (error_code.temp_name, names.impl, m.function_name, ", ".join(parameters)))
- else:
- emit.Line("%s = %s.%s(%s);" % (error_code.temp_name, names.impl, m.function_name, ", ".join(parameters)))
+ if param.flags.encode == "base64":
+ encoded_name = param.TempName("encoded_")
+ emit.Line("%s %s;" % (param.cpp_native_type, encoded_name))
+ emit.Line("Core::ToString(%s, %s, true, %s);" % (param.temp_name, size, encoded_name))
+ emit.Line("%s = %s;" % (cpp_name, encoded_name))
+ else:
+ assert False, "unimplemented encoding: " + param.flags.encode
- parameters = []
+ emit.ExitBlock(conditions)
- if indexed:
- parameters.append("const %s& %s" % (any_index.cpp_native_type, index_name))
+ elif isinstance(param, JsonArray):
+ if param.iterator:
+ conditions = Restrictions(reverse=True)
+ conditions.check_set(param)
+ conditions.check_not_null(param)
- for _, [ param, type ] in sorted_vars:
- parameters.append("%s%s& %s" % ("const " if type == "r" else "", param.cpp_type, param.local_name))
+ if param.optional:
+ rhs += ".Value()"
- prototypes.append(["uint32_t %s(%s)%s" % (m.function_name, ", ".join(parameters), (" const" if (const_cast or (isinstance(m, JsonProperty) and m.readonly)) else "")), CoreError("none")])
+ emit.EnterBlock(conditions)
- emit.Line()
+ item_name = param.items.TempName("item_")
+ emit.Line("%s %s{};" % (param.items.cpp_native_type, item_name))
+ emit.Line("while (%s->Next(%s) == true) { %s.Add() = %s; }" % (rhs, item_name, cpp_name, item_name))
- # Emit result handling and serialization to JSON data
+ if param.schema.get("@extract"):
+ emit.Line("%s.SetExtractOnSingle(true);" % (cpp_name))
- if response and not is_json_source:
- emit.Line("if (%s == %s) {" % (error_code.temp_name, CoreError("none")))
- emit.Indent()
+ emit.Line("%s->Release();" % rhs)
- for _, [param, param_type] in sorted_vars:
- if "w" not in param_type:
- continue
+ emit.ExitBlock(conditions)
- rhs = param.temp_name
- cpp_name = (repsonse_parent + param.cpp_name) if repsonse_parent else param.local_name
+ elif param.items.schema.get("@bitmask"):
+ emit.Line("%s = %s;" % (cpp_name, rhs))
- # Special case for C-style buffers disguised as base64-encoded strings
- if isinstance(param, JsonString) and (param.flags.length or param.flags.size) and param.flags.encode:
+ elif (param.flags.length or param.flags.size):
length_param = param.flags.length
-
conditions = Restrictions(reverse=True)
if length_param:
conditions.check_set(length_param)
conditions.check_not_null(length_param)
-
size = length_param.temp_name
if length_param.optional:
- # the length variable determines optionality of the buffer
size += ".Value()"
else:
size = param.flags.size
- emit.EnterBlock(conditions)
+ if conditions.count():
+ emit.Line("if (%s) {" % conditions.join())
+ emit.Indent()
- if param.flags.encode == "base64":
- encoded_name = param.TempName("encoded_")
- emit.Line("%s %s;" % (param.cpp_native_type, encoded_name))
- emit.Line("Core::ToString(%s, %s, true, %s);" % (param.temp_name, size, encoded_name))
- emit.Line("%s = %s;" % (cpp_name, encoded_name))
- else:
- assert False, "unimplemented encoding: " + param.flags.encode
+ emit.Line("%s.Clear();" % cpp_name)
+ emit.Line("for (uint16_t i = 0; i < %s; i++) { %s.Add() = %s[i]; }" % (size, cpp_name, rhs))
- emit.ExitBlock(conditions)
+ if conditions.count():
+ emit.Unindent()
+ emit.Line("}")
+ emit.Line("else {")
+ emit.Indent()
+ emit.Line("%s.Null(true);" % cpp_name)
+ emit.Unindent()
+ emit.Line("}")
- elif isinstance(param, JsonArray):
- if param.iterator:
- conditions = Restrictions(reverse=True)
- conditions.check_set(param)
- conditions.check_not_null(param)
+ emit.Line()
+ else:
+ raise RPCEmitterError("unable to serialize a non-iterator array: %s" % param.json_name)
- if param.optional:
- rhs += ".Value()"
+ # All others...
+ else:
+ if param.flags.store_lookup:
+ emit.Line("%s = %s->%s.Store(_real%s, %s);" % (param.temp_name, names.storage, param.cpp_name, param.temp_name, names.context))
+ emit.Line("_real%s->Release();" % param.temp_name)
- emit.EnterBlock(conditions)
+ # assignment operator takes care of OptionalType
+ emit.Line("%s = %s;" % (cpp_name, rhs + param.convert_rhs))
- item_name = param.items.TempName("item_")
- emit.Line("%s %s{};" % (param.items.cpp_native_type, item_name))
- emit.Line("while (%s->Next(%s) == true) { %s.Add() = %s; }" % (rhs, item_name, cpp_name, item_name))
+ if param.schema.get("opaque") and not repsonse_parent: # if comes from a struct it already has a SetQuoted
+ emit.Line("%s.SetQuoted(false);" % (cpp_name))
- if param.schema.get("@extract"):
- emit.Line("%s.SetExtractOnSingle(true);" % (cpp_name))
+ emit.Unindent()
+ emit.Line("}")
- emit.Line("%s->Release();" % rhs)
+ if restrictions.present():
+ emit.Unindent()
+ emit.Line("}")
- emit.ExitBlock(conditions)
+ is_json_source = source_file.endswith(".json")
- elif param.items.schema.get("@bitmask"):
- emit.Line("%s = %s;" % (cpp_name, rhs))
+ for i, ns_ in enumerate(ns.split("::")):
+ if ns_ and i >= 2:
+ emit.Indent()
- elif (param.flags.length or param.flags.size):
- length_param = param.flags.length
- conditions = Restrictions(reverse=True)
+ if "info" in root.schema and "namespace" in root.schema["info"]:
+ emit.Indent()
- if length_param:
- conditions.check_set(length_param)
- conditions.check_not_null(length_param)
- size = length_param.temp_name
+ emit.Indent()
- if length_param.optional:
- size += ".Value()"
- else:
- size = param.flags.size
+ _EmitVersionCode(emit, rpc_version.GetVersion(root.schema["info"] if "info" in root.schema else dict()))
- if conditions.count():
- emit.Line("if (%s) {" % conditions.join())
- emit.Indent()
+ methods_and_properties = [x for x in root.properties if not isinstance(x, (JsonNotification))]
+ methods = [x for x in methods_and_properties if not isinstance(x, (JsonNotification, JsonProperty))]
+ events = [x for x in root.properties if isinstance(x, JsonNotification)]
+ listener_events = [x for x in events if x.is_status_listener]
+ alt_events = [x for x in events if x.alternative]
+ lookup_methods = [x for x in methods_and_properties if x.schema.get("@lookup)")]
- emit.Line("%s.Clear();" % cpp_name)
- emit.Line("for (uint16_t i = 0; i < %s; i++) { %s.Add() = %s[i]; }" % (size, cpp_name, rhs))
+ names = DottedDict()
- if conditions.count():
- emit.Unindent()
- emit.Line("}")
- emit.Line("else {")
- emit.Indent()
- emit.Line("%s.Null(true);" % cpp_name)
- emit.Unindent()
- emit.Line("}")
+ names['module'] = "_module__"
+ names['impl'] = "_implementation__"
+ names['storage'] = "_storage__"
+ names['handler'] = ("_handler_" if not is_json_source else names.impl)
+ names['handler_interface'] = "IHandler"
+ names['context'] = "context"
+ names['id'] = "id"
- emit.Line()
- else:
- raise RPCEmitterError("unable to serialize a non-iterator array: %s" % param.json_name)
+ names['namespace'] = ("J" + root.json_name)
+ names['interface'] = (root.info["interface"] if "interface" in root.info else ("I" + root.json_name))
+ names['jsonrpc_alias'] = ("PluginHost::%s" % ("JSONRPCSupportsEventStatus" if listener_events else "JSONRPC"))
+ names['context_alias'] = "Core::JSONRPC::Context"
- # All others...
- else:
- # assignment operator takes care of OptionalType
- emit.Line("%s = %s;" % (cpp_name, rhs + param.convert_rhs))
+ impl_required = methods_and_properties or listener_events
+ module_required = (impl_required or (alt_events and not config.LEGACY_ALT))
+ storage_required = (root.schema.get("@interfaces") != None)
- if param.schema.get("opaque") and not repsonse_parent: # if comes from a struct it already has a SetQuoted
- emit.Line("%s.SetQuoted(false);" % (cpp_name))
+ if listener_events and not is_json_source:
+ _EmitHandlerInterface(listener_events)
- emit.Unindent()
- emit.Line("}")
+ if storage_required:
+ _EmitStorageClass(root.schema.get("@interfaces"))
- if restrictions.present():
- emit.Unindent()
- emit.Line("}")
+ _EmitNoPushWarnings(prologue=True)
- if _index_checks_emitted:
- emit.Unindent()
- emit.Line("}")
+ if is_json_source:
+ emit.Line("using JSONRPC = %s;" % names.jsonrpc_alias)
+ emit.Line()
- is_property = isinstance(m, JsonProperty)
+ impl_name = ((" " + names.impl) if impl_required else "")
- contexted = (not is_property and m.context)
+ register_params = [ "%s& %s" % (names.jsonrpc_alias, names.module) ]
- # Prepare for handling indexed properties
- indexed = is_property and m.index
- any_index = (m.index[0] if m.index[0] else m.index[1]) if indexed else None
- indexes_are_different = not m.index[2] if indexed else False
- index_name = any_index.local_name if any_index else None
- lookup = m.schema.get("@lookup")
+ if is_json_source:
+ register_params.append("IMPLEMENTATION&%s" % impl_name)
+ emit.Line("template")
+
+ else:
+ register_params.append("%s*%s" % (names.interface, impl_name))
+
+ if listener_events:
+ register_params.append("%s* %s" % (names.handler_interface, names.handler))
+
+ if storage_required:
+ register_params.append("%s*& %s" % ("LookupStorage", names.storage))
+
+ emit.Line("static void Register(%s)" % (", ".join(register_params)))
+
+ emit.Line("{")
+ emit.Indent()
+
+ if not is_json_source:
+ if methods_and_properties:
+ emit.Line("ASSERT(%s != nullptr);" % names.impl)
+
+ if listener_events:
+ emit.Line("ASSERT(%s != nullptr);" % names.handler)
+
+ if storage_required:
+ emit.Line("ASSERT(%s == nullptr);" % names.storage)
+
+ if methods_and_properties or listener_events or storage_required:
+ emit.Line()
+
+ if storage_required:
+ emit.Line("%s = new LookupStorage;" % names.storage)
+ emit.Line("ASSERT(%s != nullptr);" % names.storage)
+ emit.Line()
+
+
+ emit.Line("%s.RegisterVersion(%s, Version::Major, Version::Minor, Version::Patch);" % (names.module, Tstring(names.namespace)))
+
+ if module_required:
+ emit.Line()
+
+ if alt_events and not config.LEGACY_ALT:
+ _EmitAlternativeEventsRegistration(alt_events, prologue=True)
+
+ if methods_and_properties:
+ emit.Line("// Register methods and properties...")
+ emit.Line()
+
+ prototypes = []
+
+ for m in methods_and_properties:
+
+ is_property = isinstance(m, JsonProperty)
+ has_index = is_property and m.index
if is_property:
# Normalize property params/repsonse to match methods
@@ -1085,25 +1189,50 @@ def _Invoke(params, response, parent="", repsonse_parent="", const_cast=False, i
params.Rename("Params")
response.Rename("Result")
- emit.Line("// %sProperty: %s%s" % ("Indexed " if indexed else "", m.Headline(), " (r/o)" if m.readonly else (" (w/o)" if m.writeonly else "")))
+ emit.Line("// %sProperty: %s%s" % ("Indexed " if has_index else "", m.Headline(), " (r/o)" if m.readonly else (" (w/o)" if m.writeonly else "")))
else:
params = copy.deepcopy(m.properties[0])
response = copy.deepcopy(m.properties[1])
emit.Line("// Method: %s" % m.Headline())
+ normalized_params = params if (params and not params.is_void) else None
+ normalized_response = response if (response and not response.is_void) else None
+
+ sorted_vars = _BuildVars(normalized_params, normalized_response)
+
+ has_lookup_params = False
+
+ for _, [param, _] in sorted_vars:
+ if param.flags.store_lookup or param.flags.dispose_lookup:
+ has_lookup_params = True
+ break
+
+ any_index = (m.index[0] if m.index[0] else m.index[1]) if has_index else None
+ indexes_are_different = not m.index[2] if has_index else False
+ index_name = any_index.local_name if any_index else None
+
+ has_context = not is_property and m.context
+ lookup = m.schema.get("@lookup")
+
+ needs_context = has_context or has_lookup_params or lookup
+ needs_storage = has_lookup_params or lookup
+ needs_id = (lookup != None)
+ needs_index = has_index
+ needs_handler = needs_context or needs_id or needs_index
+
# Emit method prologue
template_params = [ params.cpp_type, response.cpp_type ]
- if indexed or contexted or lookup:
+ if needs_handler:
function_params = []
- if contexted:
+ if needs_context:
function_params.append("const %s&" % names.context_alias)
- if lookup:
- function_params.append("const uint32_t")
+ if needs_id:
+ function_params.append("const %s" % lookup["type"])
- if indexed:
+ if needs_index:
function_params.append("const string&")
if not params.is_void:
@@ -1119,13 +1248,13 @@ def _Invoke(params, response, parent="", repsonse_parent="", const_cast=False, i
lambda_params = []
- if contexted:
+ if needs_context:
lambda_params.append("const %s& %s" % (names.context_alias, names.context))
- if lookup:
- lambda_params.append("const uint32_t %s" % (lookup[2]))
+ if needs_id:
+ lambda_params.append("const %s %s" % (lookup["type"], names.id))
- if indexed:
+ if needs_index:
lambda_params.append("const string& %s" % (index_name))
if not params.is_void:
@@ -1134,7 +1263,13 @@ def _Invoke(params, response, parent="", repsonse_parent="", const_cast=False, i
if not response.is_void:
lambda_params.append("%s& %s" % (response.cpp_type, response.local_name))
- emit.Line("[%s%s](%s) -> uint32_t {" % ("&" if is_json_source else "", names.impl, ", ".join(lambda_params)))
+ catches = []
+ catches.append("%s%s" % ("&" if is_json_source else "", names.impl))
+
+ if needs_storage:
+ catches.append(names.storage)
+
+ emit.Line("[%s](%s) -> uint32_t {" % (", ".join(catches), ", ".join(lambda_params)))
emit.Indent()
# Emit the function body
@@ -1146,10 +1281,11 @@ def _Invoke(params, response, parent="", repsonse_parent="", const_cast=False, i
emit.Line("%s %s = %s;" % (error_code.cpp_native_type, error_code.temp_name, CoreError("none")))
emit.Line()
- _index_checks_emitted = _EmitIndexing(m.index[0]) if (indexed and not indexes_are_different) else False
+ # If indexes for r/w property are the same, then emit index code before the check for set/not-set, otherwise do it afterwards
+ index_checks_emitted, index_name = _EmitIndexing(any_index, index_name) if (has_index and not indexes_are_different) else (False, index_name)
if not is_property:
- _Invoke((params if not params.is_void else None), (response if not response.is_void else None), params_parent, response_parent)
+ _Invoke(m, sorted_vars, normalized_params, normalized_response, params_parent, response_parent, context=has_context)
else:
is_read_only = m.readonly
is_write_only = m.writeonly
@@ -1164,14 +1300,23 @@ def _Invoke(params, response, parent="", repsonse_parent="", const_cast=False, i
emit.Line("// read-only property get")
if not is_write_only:
- assert not response.is_void
- maybe_index = m.index[0] if indexes_are_different else None
- _Invoke(None, response, params_parent, response_parent, const_cast=is_read_write, index=maybe_index, test_param=not is_read_write)
+ assert normalized_response
+
+ checks_emitted, index_name = _EmitIndexing(any_index, index_name) if (has_index and not index_checks_emitted) else (False, index_name)
+
+ maybe_index = index_name if has_index else None
+ _Invoke(m, _BuildVars(None, normalized_response), None, normalized_response, params_parent, response_parent, const_cast=is_read_write, test_param=not is_read_write, index=maybe_index, context=has_context)
+
+ if checks_emitted:
+ emit.Unindent()
+ emit.Line("}")
if indexes_are_different:
index_name = any_index.local_name
if not is_read_only:
+ assert normalized_params
+
if is_read_write:
emit.Unindent()
emit.Line("}")
@@ -1181,9 +1326,14 @@ def _Invoke(params, response, parent="", repsonse_parent="", const_cast=False, i
else:
emit.Line("// write-only property set")
- assert not params.is_void
- maybe_index = m.index[1] if indexes_are_different else None
- _Invoke(params, None, params_parent, response_parent, index=maybe_index, test_param=not is_read_write, param_const_cast=is_read_write)
+ checks_emitted, index_name = _EmitIndexing(any_index, index_name) if (has_index and not index_checks_emitted) else (False, index_name)
+
+ maybe_index = index_name if has_index else None
+ _Invoke(m, _BuildVars(normalized_params, None), normalized_params, None, params_parent, response_parent, param_const_cast=is_read_write, test_param=not is_read_write, index=maybe_index, context=has_context)
+
+ if checks_emitted:
+ emit.Unindent()
+ emit.Line("}")
if is_read_write:
emit.Line()
@@ -1193,7 +1343,7 @@ def _Invoke(params, response, parent="", repsonse_parent="", const_cast=False, i
emit.Unindent()
emit.Line("}")
- if _index_checks_emitted:
+ if index_checks_emitted:
emit.Unindent()
emit.Line("}")
@@ -1221,8 +1371,13 @@ def _Invoke(params, response, parent="", repsonse_parent="", const_cast=False, i
emit.Line()
# Emit method deregistrations
- module_name = ((" " + names.module) if module_required else "")
- emit.Line("static void Unregister(%s&%s)" % (names.jsonrpc_alias, module_name))
+ unregister_params = [ names.jsonrpc_alias + "&" + ((" " + names.module) if module_required else "") ]
+
+ if storage_required:
+ unregister_params.append("LookupStorage*& %s" % names.storage)
+
+
+ emit.Line("static void Unregister(%s)" % ", ".join(unregister_params))
emit.Line("{")
emit.Indent()
@@ -1244,6 +1399,12 @@ def _Invoke(params, response, parent="", repsonse_parent="", const_cast=False, i
if listener_events:
_EmitEventStatusListenerRegistration(listener_events, is_json_source, prologue=False)
+ if storage_required:
+ emit.Line()
+ emit.Line("ASSERT(%s != nullptr);" % names.storage)
+ emit.Line("delete %s;" % names.storage)
+ emit.Line("%s = nullptr;" % names.storage)
+
emit.Unindent()
emit.Line("}")
emit.Line()
@@ -1252,6 +1413,9 @@ def _Invoke(params, response, parent="", repsonse_parent="", const_cast=False, i
if events:
_EmitEvents(events)
+ if storage_required:
+ _EmitStorageEvents(root.schema.get("@interfaces"))
+
# Restore warnings level
_EmitNoPushWarnings(prologue=False)
diff --git a/ProxyStubGenerator/StubGenerator.py b/ProxyStubGenerator/StubGenerator.py
index 356ec0f..92aafe3 100755
--- a/ProxyStubGenerator/StubGenerator.py
+++ b/ProxyStubGenerator/StubGenerator.py
@@ -1773,7 +1773,7 @@ def EmitStub(interface_name, methods, stub_methods_name, interface, prepared_par
#
def EmitCompleteMethod():
- emit.Line("uint32_t _Complete(RPC::Data::Frame::Reader& %s)" % vars["reader"])
+ emit.Line("uint32_t _Complete(RPC::Data::Frame::Reader& %s) const" % vars["reader"])
emit.Line("{")
emit.IndentInc()
emit.Line("uint32_t result = Core::ERROR_NONE;")