diff options
Diffstat (limited to 'deps/v8/third_party/inspector_protocol/CodeGenerator.py')
-rw-r--r-- | deps/v8/third_party/inspector_protocol/CodeGenerator.py | 370 |
1 files changed, 232 insertions, 138 deletions
diff --git a/deps/v8/third_party/inspector_protocol/CodeGenerator.py b/deps/v8/third_party/inspector_protocol/CodeGenerator.py index de1029e801..8b28d13609 100644 --- a/deps/v8/third_party/inspector_protocol/CodeGenerator.py +++ b/deps/v8/third_party/inspector_protocol/CodeGenerator.py @@ -7,6 +7,8 @@ import sys import optparse import collections import functools +import re +import copy try: import json except ImportError: @@ -52,6 +54,7 @@ def read_config(): cmdline_parser.add_option("--output_base") cmdline_parser.add_option("--jinja_dir") cmdline_parser.add_option("--config") + cmdline_parser.add_option("--config_value", action="append", type="string") arg_options, _ = cmdline_parser.parse_args() jinja_dir = arg_options.jinja_dir if not jinja_dir: @@ -63,6 +66,9 @@ def read_config(): if not config_file: raise Exception("Config file name must be specified") config_base = os.path.dirname(config_file) + config_values = arg_options.config_value + if not config_values: + config_values = [] except Exception: # Work with python 2 and 3 http://docs.python.org/py3k/howto/pyporting.html exc = sys.exc_info()[1] @@ -75,13 +81,17 @@ def read_config(): config_partial = json_to_object(config_json_string, output_base, config_base) config_json_file.close() defaults = { + ".use_snake_file_names": False, + ".use_title_case_methods": False, ".imported": False, ".imported.export_macro": "", ".imported.export_header": False, ".imported.header": False, ".imported.package": False, + ".imported.options": False, ".protocol.export_macro": "", ".protocol.export_header": False, + ".protocol.options": False, ".exported": False, ".exported.export_macro": "", ".exported.export_header": False, @@ -89,6 +99,10 @@ def read_config(): ".lib.export_macro": "", ".lib.export_header": False, } + for key_value in config_values: + parts = key_value.split("=") + if len(parts) == 2: + defaults["." + parts[0]] = parts[1] return (jinja_dir, config_file, init_defaults(config_partial, "", defaults)) except Exception: # Work with python 2 and 3 http://docs.python.org/py3k/howto/pyporting.html @@ -97,6 +111,9 @@ def read_config(): exit(1) +# ---- Begin of utilities exposed to generator ---- + + def to_title_case(name): return name[:1].upper() + name[1:] @@ -109,7 +126,43 @@ def dash_to_camelcase(word): return prefix + "".join(to_title_case(x) or "-" for x in word.split("-")) -def initialize_jinja_env(jinja_dir, cache_dir): +def to_snake_case(name): + return re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", name, sys.maxint).lower() + + +def to_method_case(config, name): + if config.use_title_case_methods: + return to_title_case(name) + return name + + +def join_arrays(dict, keys): + result = [] + for key in keys: + if key in dict: + result += dict[key] + return result + + +def format_include(config, header, file_name=None): + if file_name is not None: + header = header + "/" + file_name + ".h" + header = "\"" + header + "\"" if header[0] not in "<\"" else header + if config.use_snake_file_names: + header = to_snake_case(header) + return header + + +def to_file_name(config, file_name): + if config.use_snake_file_names: + return to_snake_case(file_name).replace(".cpp", ".cc") + return file_name + + +# ---- End of utilities exposed to generator ---- + + +def initialize_jinja_env(jinja_dir, cache_dir, config): # pylint: disable=F0401 sys.path.insert(1, os.path.abspath(jinja_dir)) import jinja2 @@ -122,52 +175,11 @@ def initialize_jinja_env(jinja_dir, cache_dir): keep_trailing_newline=True, # newline-terminate generated files lstrip_blocks=True, # so can indent control flow tags trim_blocks=True) - jinja_env.filters.update({"to_title_case": to_title_case, "dash_to_camelcase": dash_to_camelcase}) + jinja_env.filters.update({"to_title_case": to_title_case, "dash_to_camelcase": dash_to_camelcase, "to_method_case": functools.partial(to_method_case, config)}) jinja_env.add_extension("jinja2.ext.loopcontrols") return jinja_env -def patch_full_qualified_refs(protocol): - def patch_full_qualified_refs_in_domain(json, domain_name): - if isinstance(json, list): - for item in json: - patch_full_qualified_refs_in_domain(item, domain_name) - - if not isinstance(json, dict): - return - for key in json: - if key == "type" and json[key] == "string": - json[key] = domain_name + ".string" - if key != "$ref": - patch_full_qualified_refs_in_domain(json[key], domain_name) - continue - if json["$ref"].find(".") == -1: - json["$ref"] = domain_name + "." + json["$ref"] - return - - for domain in protocol.json_api["domains"]: - patch_full_qualified_refs_in_domain(domain, domain["domain"]) - - -def calculate_exports(protocol): - def calculate_exports_in_json(json_value): - has_exports = False - if isinstance(json_value, list): - for item in json_value: - has_exports = calculate_exports_in_json(item) or has_exports - if isinstance(json_value, dict): - has_exports = ("exported" in json_value and json_value["exported"]) or has_exports - for key in json_value: - has_exports = calculate_exports_in_json(json_value[key]) or has_exports - return has_exports - - protocol.json_api["has_exports"] = False - for domain_json in protocol.json_api["domains"]: - domain_json["has_exports"] = calculate_exports_in_json(domain_json) - if domain_json["has_exports"] and domain_json["domain"] in protocol.generate_domains: - protocol.json_api["has_exports"] = True - - def create_imported_type_definition(domain_name, type, imported_namespace): # pylint: disable=W0622 return { @@ -286,111 +298,195 @@ def wrap_array_definition(type): "raw_type": "protocol::Array<%s>" % type["raw_type"], "raw_pass_type": "protocol::Array<%s>*" % type["raw_type"], "raw_return_type": "protocol::Array<%s>*" % type["raw_type"], - "create_type": "wrapUnique(new protocol::Array<%s>())" % type["raw_type"], "out_type": "protocol::Array<%s>&" % type["raw_type"], } -def create_type_definitions(protocol, imported_namespace): - protocol.type_definitions = {} - protocol.type_definitions["number"] = create_primitive_type_definition("number") - protocol.type_definitions["integer"] = create_primitive_type_definition("integer") - protocol.type_definitions["boolean"] = create_primitive_type_definition("boolean") - protocol.type_definitions["object"] = create_object_type_definition() - protocol.type_definitions["any"] = create_any_type_definition() - for domain in protocol.json_api["domains"]: - protocol.type_definitions[domain["domain"] + ".string"] = create_string_type_definition() - if not ("types" in domain): - continue - for type in domain["types"]: - type_name = domain["domain"] + "." + type["id"] - if type["type"] == "object" and domain["domain"] in protocol.imported_domains: - protocol.type_definitions[type_name] = create_imported_type_definition(domain["domain"], type, imported_namespace) - elif type["type"] == "object": - protocol.type_definitions[type_name] = create_user_type_definition(domain["domain"], type) - elif type["type"] == "array": - items_type = type["items"]["type"] - protocol.type_definitions[type_name] = wrap_array_definition(protocol.type_definitions[items_type]) - elif type["type"] == domain["domain"] + ".string": - protocol.type_definitions[type_name] = create_string_type_definition() - else: - protocol.type_definitions[type_name] = create_primitive_type_definition(type["type"]) - - -def type_definition(protocol, name): - return protocol.type_definitions[name] - - -def resolve_type(protocol, prop): - if "$ref" in prop: - return protocol.type_definitions[prop["$ref"]] - if prop["type"] == "array": - return wrap_array_definition(resolve_type(protocol, prop["items"])) - return protocol.type_definitions[prop["type"]] +class Protocol(object): + def __init__(self, config): + self.config = config + self.json_api = {"domains": []} + self.imported_domains = [] + self.exported_domains = [] + self.generate_domains = self.read_protocol_file(config.protocol.path) + + if config.protocol.options: + self.generate_domains = [rule.domain for rule in config.protocol.options] + self.exported_domains = [rule.domain for rule in config.protocol.options if hasattr(rule, "exported")] + + if config.imported: + self.imported_domains = self.read_protocol_file(config.imported.path) + if config.imported.options: + self.imported_domains = [rule.domain for rule in config.imported.options] + + self.patch_full_qualified_refs() + self.create_notification_types() + self.create_type_definitions() + + + def read_protocol_file(self, file_name): + input_file = open(file_name, "r") + json_string = input_file.read() + input_file.close() + parsed_json = json.loads(json_string) + version = parsed_json["version"]["major"] + "." + parsed_json["version"]["minor"] + domains = [] + for domain in parsed_json["domains"]: + domains.append(domain["domain"]) + domain["version"] = version + self.json_api["domains"] += parsed_json["domains"] + return domains + + + def patch_full_qualified_refs(self): + def patch_full_qualified_refs_in_domain(json, domain_name): + if isinstance(json, list): + for item in json: + patch_full_qualified_refs_in_domain(item, domain_name) + if not isinstance(json, dict): + return + for key in json: + if key == "type" and json[key] == "string": + json[key] = domain_name + ".string" + if key != "$ref": + patch_full_qualified_refs_in_domain(json[key], domain_name) + continue + if json["$ref"].find(".") == -1: + json["$ref"] = domain_name + "." + json["$ref"] + return + for domain in self.json_api["domains"]: + patch_full_qualified_refs_in_domain(domain, domain["domain"]) + + + def create_notification_types(self): + for domain in self.json_api["domains"]: + if "events" in domain: + for event in domain["events"]: + event_type = dict() + event_type["description"] = "Wrapper for notification params" + event_type["type"] = "object" + event_type["id"] = to_title_case(event["name"]) + "Notification" + if "parameters" in event: + event_type["properties"] = copy.deepcopy(event["parameters"]) + if "types" not in domain: + domain["types"] = list() + domain["types"].append(event_type) + + + def create_type_definitions(self): + imported_namespace = "::".join(self.config.imported.namespace) if self.config.imported else "" + self.type_definitions = {} + self.type_definitions["number"] = create_primitive_type_definition("number") + self.type_definitions["integer"] = create_primitive_type_definition("integer") + self.type_definitions["boolean"] = create_primitive_type_definition("boolean") + self.type_definitions["object"] = create_object_type_definition() + self.type_definitions["any"] = create_any_type_definition() + for domain in self.json_api["domains"]: + self.type_definitions[domain["domain"] + ".string"] = create_string_type_definition() + if not ("types" in domain): + continue + for type in domain["types"]: + type_name = domain["domain"] + "." + type["id"] + if type["type"] == "object" and domain["domain"] in self.imported_domains: + self.type_definitions[type_name] = create_imported_type_definition(domain["domain"], type, imported_namespace) + elif type["type"] == "object": + self.type_definitions[type_name] = create_user_type_definition(domain["domain"], type) + elif type["type"] == "array": + items_type = type["items"]["type"] + self.type_definitions[type_name] = wrap_array_definition(self.type_definitions[items_type]) + elif type["type"] == domain["domain"] + ".string": + self.type_definitions[type_name] = create_string_type_definition() + else: + self.type_definitions[type_name] = create_primitive_type_definition(type["type"]) + + + def check_options(self, options, domain, name, include_attr, exclude_attr, default): + for rule in options: + if rule.domain != domain: + continue + if include_attr and hasattr(rule, include_attr): + return name in getattr(rule, include_attr) + if exclude_attr and hasattr(rule, exclude_attr): + return name not in getattr(rule, exclude_attr) + return default + return False -def join_arrays(dict, keys): - result = [] - for key in keys: - if key in dict: - result += dict[key] - return result + # ---- Begin of methods exposed to generator -def has_disable(commands): - for command in commands: - if command["name"] == "disable" and (not ("handlers" in command) or "renderer" in command["handlers"]): - return True - return False + def type_definition(self, name): + return self.type_definitions[name] -def format_include(header): - return "\"" + header + "\"" if header[0] not in "<\"" else header + def resolve_type(self, prop): + if "$ref" in prop: + return self.type_definitions[prop["$ref"]] + if prop["type"] == "array": + return wrap_array_definition(self.resolve_type(prop["items"])) + return self.type_definitions[prop["type"]] -def read_protocol_file(file_name, json_api): - input_file = open(file_name, "r") - json_string = input_file.read() - input_file.close() - parsed_json = json.loads(json_string) - version = parsed_json["version"]["major"] + "." + parsed_json["version"]["minor"] - domains = [] - for domain in parsed_json["domains"]: - domains.append(domain["domain"]) - domain["version"] = version - json_api["domains"] += parsed_json["domains"] - return domains + def generate_command(self, domain, command): + if not self.config.protocol.options: + return domain in self.generate_domains + return self.check_options(self.config.protocol.options, domain, command, "include", "exclude", True) + + + def generate_event(self, domain, event): + if not self.config.protocol.options: + return domain in self.generate_domains + return self.check_options(self.config.protocol.options, domain, event, "include_events", "exclude_events", True) + + + def is_async_command(self, domain, command): + if not self.config.protocol.options: + return False + return self.check_options(self.config.protocol.options, domain, command, "async", None, False) + + + def is_exported(self, domain, name): + if not self.config.protocol.options: + return False + return self.check_options(self.config.protocol.options, domain, name, "exported", None, False) -class Protocol(object): - def __init__(self): - self.json_api = {} - self.generate_domains = [] - self.imported_domains = [] + + def is_imported(self, domain, name): + if not self.config.imported: + return False + if not self.config.imported.options: + return domain in self.imported_domains + return self.check_options(self.config.imported.options, domain, name, "imported", None, False) + + + def is_exported_domain(self, domain): + return domain in self.exported_domains + + + def generate_disable(self, domain): + if "commands" not in domain: + return True + for command in domain["commands"]: + if command["name"] == "disable" and self.generate_command(domain["domain"], "disable"): + return False + return True def main(): jinja_dir, config_file, config = read_config() - protocol = Protocol() - protocol.json_api = {"domains": []} - protocol.generate_domains = read_protocol_file(config.protocol.path, protocol.json_api) - protocol.imported_domains = read_protocol_file(config.imported.path, protocol.json_api) if config.imported else [] - patch_full_qualified_refs(protocol) - calculate_exports(protocol) - create_type_definitions(protocol, "::".join(config.imported.namespace) if config.imported else "") + protocol = Protocol(config) - if not config.exported: - for domain_json in protocol.json_api["domains"]: - if domain_json["has_exports"] and domain_json["domain"] in protocol.generate_domains: - sys.stderr.write("Domain %s is exported, but config is missing export entry\n\n" % domain_json["domain"]) - exit(1) + if not config.exported and len(protocol.exported_domains): + sys.stderr.write("Domains [%s] are exported, but config is missing export entry\n\n" % ", ".join(protocol.exported_domains)) + exit(1) if not os.path.exists(config.protocol.output): os.mkdir(config.protocol.output) - if protocol.json_api["has_exports"] and not os.path.exists(config.exported.output): + if len(protocol.exported_domains) and not os.path.exists(config.exported.output): os.mkdir(config.exported.output) - jinja_env = initialize_jinja_env(jinja_dir, config.protocol.output) + jinja_env = initialize_jinja_env(jinja_dir, config.protocol.output, config) inputs = [] inputs.append(__file__) @@ -414,27 +510,25 @@ def main(): for domain in protocol.json_api["domains"]: class_name = domain["domain"] template_context = { + "protocol": protocol, "config": config, "domain": domain, "join_arrays": join_arrays, - "resolve_type": functools.partial(resolve_type, protocol), - "type_definition": functools.partial(type_definition, protocol), - "has_disable": has_disable, - "format_include": format_include + "format_include": functools.partial(format_include, config), } if domain["domain"] in protocol.generate_domains: - outputs[os.path.join(config.protocol.output, class_name + ".h")] = h_template.render(template_context) - outputs[os.path.join(config.protocol.output, class_name + ".cpp")] = cpp_template.render(template_context) - if domain["has_exports"]: - outputs[os.path.join(config.exported.output, class_name + ".h")] = exported_template.render(template_context) - if domain["domain"] in protocol.imported_domains and domain["has_exports"]: - outputs[os.path.join(config.protocol.output, class_name + ".h")] = imported_template.render(template_context) + outputs[os.path.join(config.protocol.output, to_file_name(config, class_name + ".h"))] = h_template.render(template_context) + outputs[os.path.join(config.protocol.output, to_file_name(config, class_name + ".cpp"))] = cpp_template.render(template_context) + if domain["domain"] in protocol.exported_domains: + outputs[os.path.join(config.exported.output, to_file_name(config, class_name + ".h"))] = exported_template.render(template_context) + if domain["domain"] in protocol.imported_domains: + outputs[os.path.join(config.protocol.output, to_file_name(config, class_name + ".h"))] = imported_template.render(template_context) if config.lib: template_context = { "config": config, - "format_include": format_include, + "format_include": functools.partial(format_include, config), } lib_templates_dir = os.path.join(module_path, "lib") @@ -475,9 +569,9 @@ def main(): parts.append(template.render(template_context)) outputs[file_name] = "\n\n".join(parts) - generate_lib_file(os.path.join(config.lib.output, "Forward.h"), forward_h_templates) - generate_lib_file(os.path.join(config.lib.output, "Protocol.h"), lib_h_templates) - generate_lib_file(os.path.join(config.lib.output, "Protocol.cpp"), lib_cpp_templates) + generate_lib_file(os.path.join(config.lib.output, to_file_name(config, "Forward.h")), forward_h_templates) + generate_lib_file(os.path.join(config.lib.output, to_file_name(config, "Protocol.h")), lib_h_templates) + generate_lib_file(os.path.join(config.lib.output, to_file_name(config, "Protocol.cpp")), lib_cpp_templates) # Make gyp / make generatos happy, otherwise make rebuilds world. inputs_ts = max(map(os.path.getmtime, inputs)) |