X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/81345200c95645a1b0d2635520f96ad55dfde63f..refs/heads/master:/replay/scripts/CodeGeneratorReplayInputs.py diff --git a/replay/scripts/CodeGeneratorReplayInputs.py b/replay/scripts/CodeGeneratorReplayInputs.py index d12e9eb..d838ae5 100644 --- a/replay/scripts/CodeGeneratorReplayInputs.py +++ b/replay/scripts/CodeGeneratorReplayInputs.py @@ -57,6 +57,9 @@ GLOBAL_CONFIG = { (["JavaScriptCore"], ("JavaScriptCore", "replay/NondeterministicInput.h") ), + (["JavaScriptCore", "WebCore"], + ("WTF", "wtf/TypeCasts.h") + ), (["WebCore"], ("WTF", "wtf/text/WTFString.h") ), @@ -71,21 +74,15 @@ GLOBAL_CONFIG = { ], "implIncludes": [ - (["WebCore"], - ("WebCore", "replay/ReplayInputTypes.h") - ), (["WebCore"], ("WebCore", "replay/SerializationMethods.h") ), (["WebCore", "JavaScriptCore"], ("JavaScriptCore", "inspector/InspectorValues.h") ), - (["JavaScriptCore"], + (["WebCore", "JavaScriptCore"], ("WTF", "wtf/NeverDestroyed.h") ), - (["JavaScriptCore"], - ("WTF", "wtf/text/AtomicString.h") - ), # Testing fixtures. (["Test"], @@ -111,18 +108,17 @@ FRAMEWORK_CONFIG_MAP = { "prefix": "JS", "namespace": "JSC", "exportMacro": "JS_EXPORT_PRIVATE", - "inputTypeTemplate": Templates.InputTypeFromStaticLocal, }, "WebCore": { "prefix": "Web", "namespace": "WebCore", - "inputTypeTemplate": Templates.InputTypeFromThreadLocal, + "exportMacro": "WEBCORE_EXPORT" }, # Used for bindings tests. "Test": { "prefix": "Test", "namespace": "Test", - "inputTypeTemplate": Templates.InputTypeFromStaticLocal, + "exportMacro": "TEST_EXPORT_MACRO" } } @@ -221,9 +217,10 @@ class InputQueues: class Input: - def __init__(self, name, description, queueString, flags, guard=None): + def __init__(self, name, description, framework, queueString, flags, guard=None): self.name = name self.description = description + self.framework = framework self.queue = InputQueue.fromString(queueString) self._flags = flags self.guard = guard @@ -350,16 +347,19 @@ class Type: if self.mode == TypeModes.SCALAR: return self.type_name(qualified) elif self.mode == TypeModes.SHARED: - return "PassRefPtr<%s>" % self.type_name(qualified) + return "RefPtr<%s>" % self.type_name(qualified) else: return "const %s&" % self.type_name(qualified) def argument_type(self, qualified=False): if self.mode == TypeModes.SHARED: - return "PassRefPtr<%s>" % self.type_name(qualified) + return "RefPtr<%s>&&" % self.type_name(qualified) else: return self.storage_type() + def encoding_type_argument(self, qualified=False): + return self.type_name(qualified=qualified) + def check_for_required_properties(props, obj, what): for prop in props: @@ -390,6 +390,9 @@ class VectorType(Type): return "" def type_name(self, qualified=False): + return "Vector<%s>" % self._element_type.storage_type(qualified=qualified) + + def encoding_type_argument(self, qualified=False): return "Vector<%s>" % self._element_type.type_name(qualified=qualified) def argument_type(self, qualified=False): @@ -397,7 +400,7 @@ class VectorType(Type): class InputsModel: - def __init__(self, parsed_json): + def __init__(self): self.inputs = [] self.types = [] @@ -406,8 +409,6 @@ class InputsModel: self.types_by_name = {} self.inputs_by_name = {} - self.parse_toplevel(parsed_json) - def enum_types(self): _enums = filter(lambda x: x.is_enum() or x.is_enum_class(), self.types) return sorted(_enums, key=lambda _enum: _enum.type_name()) @@ -418,27 +419,33 @@ class InputsModel: else: return self.types_by_name.get(member.typeName) - def parse_toplevel(self, json): - check_for_required_properties(['types', 'inputs'], json, 'toplevel') - if not isinstance(json['types'], dict): - raise ParseException("Malformed specification: types is not a dict of framework->type list") + def parse_specification(self, json): + if 'types' in json: + if not isinstance(json['types'], dict): + raise ParseException("Malformed specification: types is not a dict of framework->type list") - if not isinstance(json['inputs'], list): - raise ParseException("Malformed specification: inputs is not an array") + for framework_name, type_list in json['types'].iteritems(): + if not isinstance(type_list, list): + raise ParseException("Malformed specification: type list for framework %s is not a list" % framework_name) - for type_framework_name, type_list in json['types'].iteritems(): - if not isinstance(type_list, list): - raise ParseException("Malformed specification: type list for framework %s is not a list" % type_framework_name) + framework = Framework.fromString(framework_name) + for _type in type_list: + self.parse_type_with_framework(_type, framework) - for _type in type_list: - self.parse_type_with_framework_name(_type, type_framework_name) + if 'inputs' in json: + if not isinstance(json['inputs'], dict): + raise ParseException("Malformed specification: inputs is not a dict of framework->input list") - for val in json['inputs']: - self.parse_input(val) + for framework_name, input_list in json['inputs'].iteritems(): + if not isinstance(input_list, list): + raise ParseException("Malformed specification: input list for framework %s is not a list" % framework_name) - def parse_type_with_framework_name(self, json, framework_name): + framework = Framework.fromString(framework_name) + for _input in input_list: + self.parse_input_with_framework(_input, framework) + + def parse_type_with_framework(self, json, framework): check_for_required_properties(['name', 'mode'], json, 'type') - framework = Framework.fromString(framework_name) if framework is not Frameworks.Global: check_for_required_properties(['header'], json, 'non-global type') @@ -457,14 +464,14 @@ class InputsModel: if not isinstance(json['values'], list) or len(_type.values) == 0: raise ParseException("Malformed specification: enum %s does not supply a list of values" % type_name) - if _type.is_enum() and "storage" not in json: - raise ParseException("Could not parse enum %s: C-style enums must also specify their storage type so they can be forward declared." % type_name) + if _type.is_enum() and enclosing_class is None and type_storage is None: + raise ParseException("Could not parse enum %s: C-style enums not enclosed by a class must specify their storage type so they can be forward declared." % type_name) self.types.append(_type) - def parse_input(self, json): + def parse_input_with_framework(self, json, framework): check_for_required_properties(['name', 'description', 'queue', 'members'], json, 'input') - _input = Input(json['name'], json['description'], json['queue'], json.get('flags', []), json.get('guard')) + _input = Input(json['name'], json['description'], framework, json['queue'], json.get('flags', []), json.get('guard')) if isinstance(json['members'], list): for member in json['members']: check_for_required_properties(['name', 'type'], member, 'member') @@ -563,6 +570,9 @@ class Generator: def setting(self, key, default=''): return self.target_framework.setting(key, GLOBAL_CONFIG.get(key, default)) + def should_generate_item(self, item): + return item.framework is self.target_framework + # This does not account for any filename mangling performed on behalf of the test harness. def output_filename(self, extension=None): components = [] @@ -587,6 +597,9 @@ class Generator: implementation_file.close() def generate_header(self): + enums_to_generate = filter(self.should_generate_item, self._model.enum_types()) + inputs_to_generate = filter(self.should_generate_item, self._model.inputs) + template_arguments = { 'licenseBlock': self.generate_license(), 'headerGuard': re.sub('[-./]', '_', self.output_filename() + ".h"), @@ -596,16 +609,20 @@ class Generator: 'inputsNamespace': self.target_framework.setting('namespace'), 'includes': self.generate_includes(defaults=self.setting('headerIncludes')), 'typeForwardDeclarations': self.generate_type_forward_declarations(), - 'inputForwardDeclarations': "\n".join([wrap_with_guard("class %s;", _input.guard) % _input.name for _input in self._model.inputs]), - 'inputClassDeclarations': "\n\n".join([self.generate_class_declaration(_input) for _input in self._model.inputs]), - 'inputTraitDeclarations': "\n\n".join([self.generate_input_trait_declaration(_input) for _input in self._model.inputs]), - 'enumTraitDeclarations': "\n\n".join([wrap_with_guard(self.generate_enum_trait_declaration(_type), _type.guard) for _type in self._model.enum_types()]), + 'inputForwardDeclarations': "\n".join([wrap_with_guard("class %s;", _input.guard) % _input.name for _input in inputs_to_generate]), + 'inputClassDeclarations': "\n\n".join([self.generate_class_declaration(_input) for _input in inputs_to_generate]), + 'inputTraitDeclarations': "\n\n".join([self.generate_input_trait_declaration(_input) for _input in inputs_to_generate]), + 'inputTypeTraitDeclarations': "\n\n".join([self.generate_input_type_trait_declaration(_input) for _input in inputs_to_generate]), + 'enumTraitDeclarations': "\n\n".join([wrap_with_guard(self.generate_enum_trait_declaration(_type), _type.guard) for _type in enums_to_generate]), 'forEachMacro': self.generate_for_each_macro(), } return Template(Templates.HeaderSkeleton).substitute(template_arguments) def generate_implementation(self): + enums_to_generate = filter(self.should_generate_item, self._model.enum_types()) + inputs_to_generate = filter(self.should_generate_item, self._model.inputs) + template_arguments = { 'licenseBlock': self.generate_license(), 'filename': self.output_filename(), @@ -613,9 +630,9 @@ class Generator: 'traitsNamespace': self.traits_framework.setting('namespace'), 'inputsNamespace': self.target_framework.setting('namespace'), 'includes': self.generate_includes(defaults=self.setting('implIncludes'), includes_for_types=True), - 'inputClassImplementations': "\n\n".join([self.generate_class_implementation(_input) for _input in self._model.inputs]), - 'inputTraitImplementations': "\n\n".join([self.generate_input_trait_implementation(_input) for _input in self._model.inputs]), - 'enumTraitImplementations': "\n\n".join([wrap_with_guard(self.generate_enum_trait_implementation(_type), _type.guard) for _type in self._model.enum_types()]), + 'inputClassImplementations': "\n\n".join([self.generate_class_implementation(_input) for _input in inputs_to_generate]), + 'inputTraitImplementations': "\n\n".join([self.generate_input_trait_implementation(_input) for _input in inputs_to_generate]), + 'enumTraitImplementations': "\n\n".join([wrap_with_guard(self.generate_enum_trait_implementation(_type), _type.guard) for _type in enums_to_generate]), } return Template(Templates.ImplementationSkeleton).substitute(template_arguments) @@ -635,7 +652,7 @@ class Generator: include_for_destructor = _type.mode is TypeModes.SHARED # Enums within classes cannot be forward declared, so we include # headers with the relevant class declaration. - include_for_enclosing_class = _type.is_enum() and _type.enclosing_class is not None + include_for_enclosing_class = _type.enclosing_class is not None # Include headers for types like URL and String which are copied, not owned or shared. include_for_copyable_member = _type.mode is TypeModes.HEAVY_SCALAR if (not includes_for_types) ^ (include_for_destructor or include_for_enclosing_class or include_for_copyable_member): @@ -750,11 +767,11 @@ class Generator: def generate_input_member_tuples(self, _input): return [(_member, self._model.get_type_for_member(_member)) for _member in _input.members] - def qualified_input_name(self, _input): - if self.target_framework == self.traits_framework: - return _input.name - else: + def qualified_input_name(self, _input, forceQualified=False): + if forceQualified or self.target_framework != self.traits_framework: return "%s::%s" % (self.target_framework.setting('namespace'), _input.name) + else: + return _input.name def generate_input_trait_declaration(self, _input): decl_type = ['struct'] @@ -769,19 +786,34 @@ class Generator: return wrap_with_guard(Template(Templates.InputTraitsDeclaration).substitute(template_arguments), _input.guard) + def generate_input_type_trait_declaration(self, _input): + template_arguments = { + 'qualifiedInputName': self.qualified_input_name(_input, forceQualified=True), + } + + return wrap_with_guard(Template(Templates.InputTypeTraitsDeclaration).substitute(template_arguments), _input.guard) + def generate_enum_trait_declaration(self, _type): + decl_type = ['struct'] + if len(self.setting('exportMacro')) > 0: + decl_type.append(self.setting('exportMacro')) + should_qualify_type = _type.framework != self.traits_framework template = Templates.EnumTraitDeclaration if _type.is_enum() else Templates.EnumClassTraitDeclaration template_arguments = { - 'enumName': _type.type_name(qualified=should_qualify_type), + 'encodingTypeArgument': _type.encoding_type_argument(qualified=should_qualify_type), + 'enumType': _type.type_name(qualified=should_qualify_type), + 'structOrClass': " ".join(decl_type) } return Template(template).substitute(template_arguments) def generate_for_each_macro(self): + inputs_to_generate = filter(self.should_generate_item, self._model.inputs) + macro_name = "%s_REPLAY_INPUT_NAMES_FOR_EACH" % self.setting('prefix').upper() lines = [] lines.append("#define %s(macro) \\" % macro_name) - lines.extend([" macro(%s) \\" % _input.name for _input in self._model.inputs]) + lines.extend([" macro(%s) \\" % _input.name for _input in inputs_to_generate]) lines.append(" \\") lines.append("// end of %s" % macro_name) return "\n".join(lines) @@ -801,10 +833,10 @@ class Generator: prefix_components = [] if should_qualify_type: prefix_components.append(_type.framework.setting('namespace')) - if _type.is_enum_class(): - prefix_components.append(_type.type_name()) - if _type.enclosing_class is not None: + if _type.is_enum() and _type.enclosing_class is not None: prefix_components.append(_type.enclosing_class) + elif _type.is_enum_class(): + prefix_components.append(_type.type_name(qualified=False)) prefix_components.append("") enum_prefix = "::".join(prefix_components) encodeLines = [] @@ -838,8 +870,10 @@ class Generator: # Generate body for decode. decodeLines = [] - for _value in _type.values: + for i, _value in enumerate(_type.values): + template_arguments = { + 'branchKeyword': "else if" if i > 0 else "if", 'enumStringValue': _value, 'qualifiedEnumValue': "%s%s" % (enum_prefix, _value), 'qualifiedEnumName': _type.type_name(qualified=should_qualify_type) @@ -848,8 +882,9 @@ class Generator: for guard, guard_values in _type.guard_values_map.iteritems(): guardedLines = [] - for guard_value in guard_values: + for i, guard_value in enumerate(guard_values): template_arguments = { + 'branchKeyword': "else if" if i > 0 else "if", 'enumStringValue': guard_value, 'qualifiedEnumValue': "%s%s" % (enum_prefix, guard_value), 'qualifiedEnumName': _type.type_name(qualified=should_qualify_type) @@ -858,7 +893,8 @@ class Generator: decodeLines.append(wrap_with_guard("\n".join(guardedLines), guard)) template_arguments = { - 'enumName': _type.type_name(qualified=should_qualify_type), + 'encodingTypeArgument': _type.encoding_type_argument(qualified=should_qualify_type), + 'enumType': _type.type_name(qualified=should_qualify_type), 'encodeCases': "\n".join(encodeLines), 'decodeCases': "\n".join(decodeLines) } @@ -868,7 +904,7 @@ class Generator: def generate_input_trait_implementation(self, _input): template_arguments = { 'inputsNamespace': self.target_framework.setting('namespace'), - 'inputTypeImplementation': Template(self.setting('inputTypeTemplate')).substitute(None, inputName=_input.name), + 'inputNameStringLiteral': '"%s"' % _input.name, 'qualifiedInputName': self.qualified_input_name(_input), 'constructorArguments': self.generate_constructor_arguments_list(_input), 'constructorFormalsList': self.generate_constructor_formals_list(_input), @@ -881,7 +917,7 @@ class Generator: steps = [] for (_member, _type) in self.generate_input_member_tuples(_input): should_qualify_type = _type.framework != self.traits_framework - put_method = "put<%s>" % _type.type_name(qualified=should_qualify_type) + put_method = "put<%s>" % _type.encoding_type_argument(qualified=should_qualify_type) steps.extend([ " encodedValue.%s(ASCIILiteral(\"%s\"), input.%s());" % (put_method, _member.memberName, _member.memberName) @@ -899,7 +935,7 @@ class Generator: steps = [] for (_member, _type) in self.generate_input_member_tuples(_input): should_qualify_type = _type.framework != self.traits_framework - get_method = "get<%s>" % _type.type_name(qualified=should_qualify_type) + get_method = "get<%s>" % _type.encoding_type_argument(qualified=should_qualify_type) lines = [ " %s %s;" % (_type.storage_type(qualified=should_qualify_type), _member.memberName), @@ -939,7 +975,7 @@ class Generator: def generate_member_move_expression(self, _member): _type = self._model.get_type_for_member(_member) - if _type.mode == TypeModes.OWNED: + if _type.mode in [TypeModes.OWNED, TypeModes.SHARED]: return "WTF::move(%s)" % _member.memberName else: return _member.memberName @@ -948,19 +984,29 @@ class Generator: return ", ".join([self.generate_member_move_expression(_member) for _member in _input.members]) -def generate_from_specification(input_filepath=None, output_prefix="", output_dirpath=None, framework_name=None, force_output=False): - try: - with open(input_filepath, "r") as input_file: - parsed_json = json.load(input_file) - except ValueError as e: - raise Exception("Error parsing valid JSON in file: " + input_filepath) +def generate_from_specifications(input_filepaths=[], output_prefix="", output_dirpath=None, framework_name=None, force_output=False): if not framework_name in FRAMEWORK_CONFIG_MAP: raise ParseException("Unknown or unsupported framework name supplied: " + framework_name) - model = InputsModel(parsed_json) + if len(input_filepaths) == 0: + raise ParseException("Must provide at least one specification file, none were provided.") + + def parse_json_from_file(input_filepath): + try: + with open(input_filepath, "r") as input_file: + return json.load(input_file) + except ValueError as e: + raise Exception("Error parsing valid JSON in file: " + input_filepath) + + specifications = map(parse_json_from_file, input_filepaths) + + model = InputsModel() + for spec in specifications: + model.parse_specification(spec) + model.resolve_types() - generator = Generator(model, framework_name, input_filepath, output_prefix) + generator = Generator(model, framework_name, input_filepaths[0], output_prefix) generator.write_output_files(output_dirpath, force_output) @@ -968,7 +1014,7 @@ def generate_from_specification(input_filepath=None, output_prefix="", output_di if __name__ == '__main__': allowed_framework_names = FRAMEWORK_CONFIG_MAP.keys() - cli_parser = optparse.OptionParser(usage="usage: %prog [options] ") + cli_parser = optparse.OptionParser(usage="usage: %prog [options] [, ]") cli_parser.add_option("-o", "--outputDir", help="Directory where generated files should be written.") cli_parser.add_option("--framework", type="choice", choices=allowed_framework_names, help="The framework these inputs belong to.") # JavaScriptCore, WebCore cli_parser.add_option("--force", action="store_true", help="Force output of generated scripts, even if nothing changed.") @@ -978,8 +1024,6 @@ if __name__ == '__main__': options = None arg_options, arg_values = cli_parser.parse_args() - if (len(arg_values) < 1): - raise ParseException("At least one plain argument expected") if not arg_options.outputDir: raise ParseException("Missing output directory") @@ -988,7 +1032,7 @@ if __name__ == '__main__': log.setLevel(logging.DEBUG) options = { - 'input_filepath': arg_values[0], + 'input_filepaths': arg_values, 'output_dirpath': arg_options.outputDir, 'output_prefix': os.path.basename(arg_values[0]) if arg_options.test else "", 'framework_name': arg_options.framework, @@ -996,7 +1040,7 @@ if __name__ == '__main__': } try: - generate_from_specification(**options) + generate_from_specifications(**options) except (ParseException, TypecheckException) as e: if arg_options.test: log.error(e.message)