1#!/usr/bin/env python 2# Copyright (c) 2014 Apple Inc. All rights reserved. 3# 4# Redistribution and use in source and binary forms, with or without 5# modification, are permitted provided that the following conditions are 6# met: 7# 8# * Redistributions of source code must retain the above copyright 9# notice, this list of conditions and the following disclaimer. 10# * Redistributions in binary form must reproduce the above 11# copyright notice, this list of conditions and the following disclaimer 12# in the documentation and/or other materials provided with the 13# distribution. 14# 15# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 19# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 21# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 27import os.path 28import re 29import sys 30import string 31from string import Template 32import optparse 33import logging 34from CodeGeneratorReplayInputsTemplates import Templates 35 36try: 37 import json 38except ImportError: 39 import simplejson as json 40 41# Configuration values are first looked up in the framework configuration, 42# and then in the global configuration if there is no framework-specific value. 43GLOBAL_CONFIG = { 44 "baseFilename": "ReplayInputs", 45 "guardCondition": "ENABLE(WEB_REPLAY)", 46 "traitsFrameworkName": "JavaScriptCore", 47 48 # These are formatted as ([allowed_frameworks], (framework, header_path)). 49 # The generator can figure out how to format the includes. 50 "headerIncludes": [ 51 (["WebCore"], 52 ("WebCore", "replay/EventLoopInput.h") 53 ), 54 (["JavaScriptCore", "WebCore"], 55 ("JavaScriptCore", "replay/EncodedValue.h") 56 ), 57 (["JavaScriptCore"], 58 ("JavaScriptCore", "replay/NondeterministicInput.h") 59 ), 60 (["WebCore"], 61 ("WTF", "wtf/text/WTFString.h") 62 ), 63 64 # Testing fixtures. 65 (["Test"], 66 ("WebCore", "platform/ExternalNamespaceHeaderIncludeDummy.h") 67 ), 68 (["Test"], 69 ("Test", "platform/InternalNamespaceHeaderIncludeDummy.h") 70 ) 71 ], 72 73 "implIncludes": [ 74 (["WebCore"], 75 ("WebCore", "replay/ReplayInputTypes.h") 76 ), 77 (["WebCore"], 78 ("WebCore", "replay/SerializationMethods.h") 79 ), 80 (["WebCore", "JavaScriptCore"], 81 ("JavaScriptCore", "inspector/InspectorValues.h") 82 ), 83 (["JavaScriptCore"], 84 ("WTF", "wtf/NeverDestroyed.h") 85 ), 86 (["JavaScriptCore"], 87 ("WTF", "wtf/text/AtomicString.h") 88 ), 89 90 # Testing fixtures. 91 (["Test"], 92 ("WebCore", "platform/ExternalNamespaceImplIncludeDummy.h") 93 ), 94 (["Test"], 95 ("Test", "platform/InternalNamespaceImplIncludeDummy.h") 96 ) 97 ], 98} 99 100FRAMEWORK_CONFIG_MAP = { 101 "Global": { 102 "prefix": "", 103 "namespace": "" 104 }, 105 106 "WTF": { 107 "prefix": "WTF", 108 "namespace": "WTF", 109 }, 110 "JavaScriptCore": { 111 "prefix": "JS", 112 "namespace": "JSC", 113 "exportMacro": "JS_EXPORT_PRIVATE", 114 "inputTypeTemplate": Templates.InputTypeFromStaticLocal, 115 }, 116 "WebCore": { 117 "prefix": "Web", 118 "namespace": "WebCore", 119 "inputTypeTemplate": Templates.InputTypeFromThreadLocal, 120 }, 121 # Used for bindings tests. 122 "Test": { 123 "prefix": "Test", 124 "namespace": "Test", 125 "inputTypeTemplate": Templates.InputTypeFromStaticLocal, 126 } 127} 128 129# These settings are specific to an input queue. 130QUEUE_CONFIG_MAP = { 131 "SCRIPT_MEMOIZED": { 132 "enumValue": "ScriptMemoizedData", 133 "baseClass": "NondeterministicInput<%s>", 134 }, 135 "LOADER_MEMOIZED": { 136 "enumValue": "LoaderMemoizedData", 137 "baseClass": "NondeterministicInput<%s>", 138 }, 139 "EVENT_LOOP": { 140 "enumValue": "EventLoopInput", 141 "baseClass": "EventLoopInput<%s>", 142 }, 143} 144 145# Use a global logger, which normally only logs errors. 146# It can be configured to log debug messages from the CLI. 147logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.ERROR) 148log = logging.getLogger('global') 149 150 151# Model classes, which transliterate JSON input. 152class ParseException(Exception): 153 pass 154 155 156class TypecheckException(Exception): 157 pass 158 159 160class Framework: 161 def __init__(self, name): 162 self._settings = FRAMEWORK_CONFIG_MAP[name] 163 self.name = name 164 165 def setting(self, key, default=''): 166 return self._settings.get(key, default) 167 168 @staticmethod 169 def fromString(frameworkString): 170 if frameworkString == "Global": 171 return Frameworks.Global 172 173 if frameworkString == "WTF": 174 return Frameworks.WTF 175 176 if frameworkString == "JavaScriptCore": 177 return Frameworks.JavaScriptCore 178 179 if frameworkString == "WebCore": 180 return Frameworks.WebCore 181 182 if frameworkString == "Test": 183 return Frameworks.Test 184 185 raise ParseException("Unknown framework: " + frameworkString) 186 187 188class Frameworks: 189 Global = Framework("Global") 190 WTF = Framework("WTF") 191 JavaScriptCore = Framework("JavaScriptCore") 192 WebCore = Framework("WebCore") 193 Test = Framework("Test") 194 195 196class InputQueue: 197 def __init__(self, settings): 198 self._settings = settings 199 200 def setting(self, key, default=''): 201 return self._settings.get(key, default) 202 203 @staticmethod 204 def fromString(queueString): 205 if queueString == "SCRIPT_MEMOIZED": 206 return InputQueues.SCRIPT_MEMOIZED 207 208 if queueString == "LOADER_MEMOIZED": 209 return InputQueues.LOADER_MEMOIZED 210 211 if queueString == "EVENT_LOOP": 212 return InputQueues.EVENT_LOOP 213 214 raise ParseException("Unknown input queue: " + queueString) 215 216 217class InputQueues: 218 SCRIPT_MEMOIZED = InputQueue(QUEUE_CONFIG_MAP["SCRIPT_MEMOIZED"]) 219 LOADER_MEMOIZED = InputQueue(QUEUE_CONFIG_MAP["LOADER_MEMOIZED"]) 220 EVENT_LOOP = InputQueue(QUEUE_CONFIG_MAP["EVENT_LOOP"]) 221 222 223class Input: 224 def __init__(self, name, description, queueString, flags, guard=None): 225 self.name = name 226 self.description = description 227 self.queue = InputQueue.fromString(queueString) 228 self._flags = flags 229 self.guard = guard 230 self.members = [] # names should be unique, but ordered. 231 232 def setting(self, key, default=''): 233 if key in self._flags: 234 return True 235 236 return self.queue.setting(key, default) 237 238 239class InputMember: 240 def __init__(self, memberName, typeName, flags=[]): 241 self.memberName = memberName 242 self.typeName = typeName 243 self._flags = flags 244 245 def has_flag(self, key, default=''): 246 return key in self._flags 247 248 249class TypeMode: 250 def __init__(self, name): 251 self._name = name 252 253 @staticmethod 254 def fromString(modeString): 255 modeString = modeString.upper() 256 if modeString == 'SCALAR': 257 return TypeModes.SCALAR 258 if modeString == 'HEAVY_SCALAR': 259 return TypeModes.HEAVY_SCALAR 260 if modeString == 'OWNED': 261 return TypeModes.OWNED 262 if modeString == 'SHARED': 263 return TypeModes.SHARED 264 if modeString == 'VECTOR': 265 return TypeModes.VECTOR 266 267 raise ParseException("Unknown type mode: " + modeString) 268 269 270class TypeModes: 271 # Copy for assignment and for getter 272 SCALAR = TypeMode("SCALAR") 273 # Copy for assignment, pass by reference for getter 274 HEAVY_SCALAR = TypeMode("HEAVY_SCALAR") 275 # Move for assignment, pass by reference for getter 276 OWNED = TypeMode("OWNED") 277 # Copy a RefPtr for assignment and getter 278 SHARED = TypeMode("SHARED") 279 # Move operator for assignment, pass by reference for getter 280 VECTOR = TypeMode("VECTOR") 281 282 283class Type: 284 def __init__(self, name, mode, framework, header, enclosing_class, values, guard_values_map, underlying_storage, flags, guard=None): 285 self._name = name 286 self.mode = mode 287 self.framework = framework 288 self.header = header 289 self.enclosing_class = enclosing_class 290 self.values = values 291 self.guard_values_map = guard_values_map 292 self.underlying_storage = underlying_storage 293 self._flags = flags 294 self.guard = guard 295 296 def __eq__(self, other): 297 return self.type_name() == other.type_name() and self.mode == other.mode 298 299 def __hash__(self): 300 return self._name.__hash__() 301 302 def has_flag(self, flagString): 303 return flagString in self._flags 304 305 def is_struct(self): 306 return self.has_flag("STRUCT") 307 308 def is_enum(self): 309 return self.has_flag("ENUM") 310 311 def is_enum_class(self): 312 return self.has_flag("ENUM_CLASS") 313 314 def declaration_kind(self): 315 if self.is_enum(): 316 return "enum" 317 elif self.is_enum_class(): 318 return "enum class" 319 elif self.is_struct(): 320 return "struct" 321 else: 322 return "class" 323 324 def qualified_prefix(self): 325 components = [] 326 if self.framework != Frameworks.Global: 327 components.append(self.framework.setting('namespace')) 328 if self.enclosing_class is not None: 329 components.append(self.enclosing_class) 330 components.append("") 331 return "::".join(components) 332 333 def type_name(self, qualified=False): 334 if qualified: 335 return "%s%s" % (self.qualified_prefix(), self._name) 336 elif self.enclosing_class is not None: 337 return "%s::%s" % (self.enclosing_class, self._name) 338 else: 339 return self._name 340 341 def storage_type(self, qualified=False): 342 if self.mode == TypeModes.OWNED: 343 return "std::unique_ptr<%s>" % self.type_name(qualified) 344 elif self.mode == TypeModes.SHARED: 345 return "RefPtr<%s>" % self.type_name(qualified) 346 else: 347 return self.type_name(qualified) 348 349 def borrow_type(self, qualified=False): 350 if self.mode == TypeModes.SCALAR: 351 return self.type_name(qualified) 352 elif self.mode == TypeModes.SHARED: 353 return "PassRefPtr<%s>" % self.type_name(qualified) 354 else: 355 return "const %s&" % self.type_name(qualified) 356 357 def argument_type(self, qualified=False): 358 if self.mode == TypeModes.SHARED: 359 return "PassRefPtr<%s>" % self.type_name(qualified) 360 else: 361 return self.storage_type() 362 363 364def check_for_required_properties(props, obj, what): 365 for prop in props: 366 if prop not in obj: 367 raise ParseException("When parsing %s, required property missing: %s" % (what, prop)) 368 369 370class VectorType(Type): 371 def __init__(self, element_type): 372 self._element_type = element_type 373 self.mode = TypeModes.VECTOR 374 self.framework = element_type.framework 375 self.enclosing_class = None 376 377 def has_flag(self): 378 return False 379 380 def is_struct(self): 381 return False 382 383 def is_enum(self): 384 return False 385 386 def is_enum_class(self): 387 return False 388 389 def qualified_prefix(self): 390 return "" 391 392 def type_name(self, qualified=False): 393 return "Vector<%s>" % self._element_type.type_name(qualified=qualified) 394 395 def argument_type(self, qualified=False): 396 return self.type_name(qualified=qualified) + "&" 397 398 399class InputsModel: 400 def __init__(self, parsed_json): 401 self.inputs = [] 402 self.types = [] 403 404 # Types have associated frameworks and are in their namespace, but within the specification 405 # file types are in a flat namespace. Types with the same name are not allowed. 406 self.types_by_name = {} 407 self.inputs_by_name = {} 408 409 self.parse_toplevel(parsed_json) 410 411 def enum_types(self): 412 _enums = filter(lambda x: x.is_enum() or x.is_enum_class(), self.types) 413 return sorted(_enums, key=lambda _enum: _enum.type_name()) 414 415 def get_type_for_member(self, member): 416 if member.has_flag("VECTOR"): 417 return VectorType(self.types_by_name.get(member.typeName)) 418 else: 419 return self.types_by_name.get(member.typeName) 420 421 def parse_toplevel(self, json): 422 check_for_required_properties(['types', 'inputs'], json, 'toplevel') 423 if not isinstance(json['types'], dict): 424 raise ParseException("Malformed specification: types is not a dict of framework->type list") 425 426 if not isinstance(json['inputs'], list): 427 raise ParseException("Malformed specification: inputs is not an array") 428 429 for type_framework_name, type_list in json['types'].iteritems(): 430 if not isinstance(type_list, list): 431 raise ParseException("Malformed specification: type list for framework %s is not a list" % type_framework_name) 432 433 for _type in type_list: 434 self.parse_type_with_framework_name(_type, type_framework_name) 435 436 for val in json['inputs']: 437 self.parse_input(val) 438 439 def parse_type_with_framework_name(self, json, framework_name): 440 check_for_required_properties(['name', 'mode'], json, 'type') 441 framework = Framework.fromString(framework_name) 442 if framework is not Frameworks.Global: 443 check_for_required_properties(['header'], json, 'non-global type') 444 445 type_name = json['name'] 446 type_mode = TypeMode.fromString(json['mode']) 447 header = json.get('header') 448 enclosing_class = json.get('enclosing_class') 449 enum_values = json.get('values') 450 guarded_enum_values = json.get('guarded_values', {}) 451 type_storage = json.get('storage') 452 type_flags = json.get('flags', []) 453 guard = json.get('guard', None) 454 _type = Type(type_name, type_mode, framework, header, enclosing_class, enum_values, guarded_enum_values, type_storage, type_flags, guard) 455 if _type.is_enum() or _type.is_enum_class(): 456 check_for_required_properties(['values'], json, 'enum') 457 if not isinstance(json['values'], list) or len(_type.values) == 0: 458 raise ParseException("Malformed specification: enum %s does not supply a list of values" % type_name) 459 460 if _type.is_enum() and "storage" not in json: 461 raise ParseException("Could not parse enum %s: C-style enums must also specify their storage type so they can be forward declared." % type_name) 462 463 self.types.append(_type) 464 465 def parse_input(self, json): 466 check_for_required_properties(['name', 'description', 'queue', 'members'], json, 'input') 467 _input = Input(json['name'], json['description'], json['queue'], json.get('flags', []), json.get('guard')) 468 if isinstance(json['members'], list): 469 for member in json['members']: 470 check_for_required_properties(['name', 'type'], member, 'member') 471 _input.members.append(InputMember(member['name'], member['type'], member.get('flags', []))) 472 473 self.inputs.append(_input) 474 475 # Types cannot (yet) reference other types, so we can check references in one pass. 476 def resolve_types(self): 477 for _type in self.types: 478 self.typecheck_type(_type) 479 480 for _input in self.inputs: 481 self.typecheck_input(_input) 482 483 def typecheck_type(self, _type): 484 log.debug("typecheck type " + _type.type_name()) 485 486 if _type.type_name() in self.types_by_name: 487 raise TypecheckException("Duplicate type with name: " + _type.type_name()) 488 489 self.types_by_name[_type.type_name()] = _type 490 491 def typecheck_input(self, _input): 492 log.debug("typecheck input " + _input.name) 493 494 if _input.name in self.inputs_by_name: 495 raise TypecheckException("Duplicate input with name: " + _input.name) 496 497 seen_members = {} 498 499 for member in _input.members: 500 if member.memberName in seen_members: 501 raise TypecheckException("Duplicate input member with name: " + member.memberName) 502 503 self.typecheck_input_member(member, _input) 504 seen_members[member.memberName] = member 505 506 self.inputs_by_name[_input.name] = _input 507 508 def typecheck_input_member(self, input_member, _input): 509 log.debug("typecheck member '%s' of '%s'" % (input_member.memberName, _input.name)) 510 511 if not input_member.typeName in self.types_by_name: 512 raise TypecheckException("Unknown type '%s' referenced by member '%s' of input '%s'" % (input_member.typeName, input_member.memberName, _input.name)) 513 514 515# A writer that only updates file if it actually changed. 516class IncrementalFileWriter: 517 def __init__(self, filepath, force_output): 518 self._filepath = filepath 519 self._output = "" 520 self.force_output = force_output 521 522 def write(self, text): 523 self._output += text 524 525 def close(self): 526 text_changed = True 527 self._output = self._output.rstrip() + "\n" 528 529 try: 530 read_file = open(self._filepath, "r") 531 old_text = read_file.read() 532 read_file.close() 533 text_changed = old_text != self._output 534 except: 535 # Ignore, just overwrite by default 536 pass 537 538 if text_changed or self.force_output: 539 out_file = open(self._filepath, "w") 540 out_file.write(self._output) 541 out_file.close() 542 543 544def wrap_with_guard(contents, condition=None): 545 if condition is None: 546 return contents 547 548 return "\n".join([ 549 "#if %s" % condition, 550 contents, 551 "#endif // %s" % condition 552 ]) 553 554 555class Generator: 556 def __init__(self, model, target_framework_name, input_filepath, output_prefix): 557 self._model = model 558 self.target_framework = Framework.fromString(target_framework_name) 559 self.traits_framework = Framework.fromString(self.setting('traitsFrameworkName')) 560 self._input_filepath = input_filepath 561 self._output_prefix = output_prefix 562 563 def setting(self, key, default=''): 564 return self.target_framework.setting(key, GLOBAL_CONFIG.get(key, default)) 565 566 # This does not account for any filename mangling performed on behalf of the test harness. 567 def output_filename(self, extension=None): 568 components = [] 569 if len(self._output_prefix) > 0: 570 components.extend([self._output_prefix, '-']) 571 572 components.extend([self.setting('prefix'), self.setting('baseFilename')]) 573 574 if extension is not None: 575 components.extend(['.', extension]) 576 577 return "".join(components) 578 579 def write_output_files(self, _dir, force=False): 580 header_file = IncrementalFileWriter(os.path.join(_dir, self.output_filename('h')), force) 581 implementation_file = IncrementalFileWriter(os.path.join(_dir, self.output_filename('cpp')), force) 582 583 header_file.write(self.generate_header()) 584 implementation_file.write(self.generate_implementation()) 585 586 header_file.close() 587 implementation_file.close() 588 589 def generate_header(self): 590 template_arguments = { 591 'licenseBlock': self.generate_license(), 592 'headerGuard': re.sub('[-./]', '_', self.output_filename() + ".h"), 593 'filename': self.output_filename(), 594 'guardCondition': self.setting('guardCondition'), 595 'traitsNamespace': self.traits_framework.setting('namespace'), 596 'inputsNamespace': self.target_framework.setting('namespace'), 597 'includes': self.generate_includes(defaults=self.setting('headerIncludes')), 598 'typeForwardDeclarations': self.generate_type_forward_declarations(), 599 'inputForwardDeclarations': "\n".join([wrap_with_guard("class %s;", _input.guard) % _input.name for _input in self._model.inputs]), 600 'inputClassDeclarations': "\n\n".join([self.generate_class_declaration(_input) for _input in self._model.inputs]), 601 'inputTraitDeclarations': "\n\n".join([self.generate_input_trait_declaration(_input) for _input in self._model.inputs]), 602 'enumTraitDeclarations': "\n\n".join([wrap_with_guard(self.generate_enum_trait_declaration(_type), _type.guard) for _type in self._model.enum_types()]), 603 'forEachMacro': self.generate_for_each_macro(), 604 } 605 606 return Template(Templates.HeaderSkeleton).substitute(template_arguments) 607 608 def generate_implementation(self): 609 template_arguments = { 610 'licenseBlock': self.generate_license(), 611 'filename': self.output_filename(), 612 'guardCondition': self.setting('guardCondition'), 613 'traitsNamespace': self.traits_framework.setting('namespace'), 614 'inputsNamespace': self.target_framework.setting('namespace'), 615 'includes': self.generate_includes(defaults=self.setting('implIncludes'), includes_for_types=True), 616 'inputClassImplementations': "\n\n".join([self.generate_class_implementation(_input) for _input in self._model.inputs]), 617 'inputTraitImplementations': "\n\n".join([self.generate_input_trait_implementation(_input) for _input in self._model.inputs]), 618 'enumTraitImplementations': "\n\n".join([wrap_with_guard(self.generate_enum_trait_implementation(_type), _type.guard) for _type in self._model.enum_types()]), 619 } 620 621 return Template(Templates.ImplementationSkeleton).substitute(template_arguments) 622 623 def generate_license(self): 624 return Template(Templates.CopyrightBlock).substitute(None, inputFilename=os.path.basename(self._input_filepath)) 625 626 def generate_includes(self, defaults=[], includes_for_types=False): 627 lines = set() 628 629 for _type in self._model.types: 630 # Types in the "global" framework are implicitly declared and available in all namespaces. 631 if _type.framework is Frameworks.Global: 632 continue 633 # For RefCounted types, we reverse when to include the header so that the destructor can be 634 # used in the header file. 635 include_for_destructor = _type.mode is TypeModes.SHARED 636 # Enums within classes cannot be forward declared, so we include 637 # headers with the relevant class declaration. 638 include_for_enclosing_class = _type.is_enum() and _type.enclosing_class is not None 639 # Include headers for types like URL and String which are copied, not owned or shared. 640 include_for_copyable_member = _type.mode is TypeModes.HEAVY_SCALAR 641 if (not includes_for_types) ^ (include_for_destructor or include_for_enclosing_class or include_for_copyable_member): 642 continue 643 644 if self.target_framework != _type.framework: 645 lines.add("#include <%s>" % _type.header) 646 else: 647 lines.add("#include \"%s\"" % os.path.basename(_type.header)) 648 649 for entry in defaults: 650 (allowed_framework_names, data) = entry 651 (framework_name, header_path) = data 652 653 if self.target_framework.name not in allowed_framework_names: 654 continue 655 if self.target_framework.name != framework_name: 656 lines.add("#include <%s>" % header_path) 657 else: 658 lines.add("#include \"%s\"" % os.path.basename(header_path)) 659 660 return "\n".join(sorted(list(lines))) 661 662 def generate_type_forward_declarations(self): 663 lines = [] 664 665 decls_by_framework = {} 666 frameworks = [Framework.fromString(s) for s in FRAMEWORK_CONFIG_MAP.keys() if s != Frameworks.Global.name] 667 for framework in frameworks: 668 decls_by_framework[framework] = [] 669 670 for _type in self._model.types: 671 if _type.framework not in frameworks: 672 continue 673 if _type.enclosing_class is not None: 674 continue 675 if _type.mode == TypeModes.HEAVY_SCALAR: 676 continue 677 if _type.mode == TypeModes.SCALAR and not (_type.is_enum() or _type.is_enum_class()): 678 continue 679 if _type.is_enum(): 680 declaration = "enum %s : %s;" % (_type.type_name(), _type.underlying_storage) 681 else: 682 declaration = "%s %s;" % (_type.declaration_kind(), _type.type_name()) 683 decls_by_framework[_type.framework].append(declaration) 684 685 # Declare all namespaces explicitly, even if it's the main namespace. 686 for framework in frameworks: 687 if len(decls_by_framework[framework]) == 0: 688 continue 689 690 decls_by_framework[framework].sort() 691 lines.append("namespace %s {" % framework.setting('namespace')) 692 lines.extend(decls_by_framework[framework]) 693 lines.append("}") 694 lines.append("") 695 696 return "\n".join(lines) 697 698 def generate_class_declaration(self, _input): 699 extra_declarations = [] 700 if _input.queue == InputQueues.EVENT_LOOP: 701 extra_declarations.extend([ 702 "", 703 " // EventLoopInput API", 704 " virtual void dispatch(ReplayController&) override final;", 705 ]) 706 707 if _input.setting('CREATE_FROM_PAGE'): 708 extra_declarations.extend([ 709 " static std::unique_ptr<%s> createFromPage(const Page&);" % _input.name 710 ]) 711 712 member_getters = [self.generate_input_member_getter(_member) for _member in _input.members] 713 714 member_declarations = [self.generate_input_member_declaration(_member) for _member in _input.members] 715 if len(member_declarations) > 0: 716 member_declarations.insert(0, "private:") 717 718 template_arguments = { 719 'inputConstructor': self.generate_input_constructor_declaration(_input), 720 'inputDestructor': self.generate_input_destructor_declaration(_input), 721 'inputName': _input.name, 722 'inputQueue': _input.setting('enumValue'), 723 'baseClass': _input.setting('baseClass') % _input.name, 724 'extraDeclarations': "\n".join(extra_declarations), 725 'memberGetters': "\n".join(member_getters), 726 'memberDeclarations': "\n".join(member_declarations), 727 } 728 729 return wrap_with_guard(Template(Templates.InputClassDeclaration).substitute(template_arguments), _input.guard) 730 731 def generate_input_constructor_declaration(self, _input): 732 formals_list = self.generate_constructor_formals_list(_input) 733 terms = [] 734 if self.setting('exportMacro'): 735 terms.append(self.setting('exportMacro')) 736 terms.append("%s(%s)" % (_input.name, formals_list)) 737 return " %s;" % " ".join(terms) 738 739 def generate_input_destructor_declaration(self, _input): 740 return " virtual ~%s();" % _input.name 741 742 def generate_input_member_getter(self, _member): 743 member_type = self._model.get_type_for_member(_member) 744 return " %s %s() const { return %s; }" % (member_type.borrow_type(), _member.memberName, self.generate_member_borrow_expression(_member)) 745 746 def generate_input_member_declaration(self, _member): 747 member_type = self._model.get_type_for_member(_member) 748 return " %s m_%s;" % (member_type.storage_type(), _member.memberName) 749 750 def generate_input_member_tuples(self, _input): 751 return [(_member, self._model.get_type_for_member(_member)) for _member in _input.members] 752 753 def qualified_input_name(self, _input): 754 if self.target_framework == self.traits_framework: 755 return _input.name 756 else: 757 return "%s::%s" % (self.target_framework.setting('namespace'), _input.name) 758 759 def generate_input_trait_declaration(self, _input): 760 decl_type = ['struct'] 761 if len(self.setting('exportMacro')) > 0: 762 decl_type.append(self.setting('exportMacro')) 763 764 template_arguments = { 765 'structOrClass': " ".join(decl_type), 766 'queueType': _input.queue.setting('enumValue'), 767 'qualifiedInputName': self.qualified_input_name(_input), 768 } 769 770 return wrap_with_guard(Template(Templates.InputTraitsDeclaration).substitute(template_arguments), _input.guard) 771 772 def generate_enum_trait_declaration(self, _type): 773 should_qualify_type = _type.framework != self.traits_framework 774 template = Templates.EnumTraitDeclaration if _type.is_enum() else Templates.EnumClassTraitDeclaration 775 template_arguments = { 776 'enumName': _type.type_name(qualified=should_qualify_type), 777 } 778 return Template(template).substitute(template_arguments) 779 780 def generate_for_each_macro(self): 781 macro_name = "%s_REPLAY_INPUT_NAMES_FOR_EACH" % self.setting('prefix').upper() 782 lines = [] 783 lines.append("#define %s(macro) \\" % macro_name) 784 lines.extend([" macro(%s) \\" % _input.name for _input in self._model.inputs]) 785 lines.append(" \\") 786 lines.append("// end of %s" % macro_name) 787 return "\n".join(lines) 788 789 def generate_class_implementation(self, _input): 790 template_arguments = { 791 'inputName': _input.name, 792 'inputsNamespace': self.target_framework.setting('namespace'), 793 'initializerList': self.generate_constructor_initializer_list(_input), 794 'constructorFormalsList': self.generate_constructor_formals_list(_input), 795 } 796 797 return wrap_with_guard(Template(Templates.InputClassImplementation).substitute(template_arguments), _input.guard) 798 799 def generate_enum_trait_implementation(self, _type): 800 should_qualify_type = _type.framework != self.traits_framework 801 prefix_components = [] 802 if should_qualify_type: 803 prefix_components.append(_type.framework.setting('namespace')) 804 if _type.is_enum_class(): 805 prefix_components.append(_type.type_name()) 806 if _type.enclosing_class is not None: 807 prefix_components.append(_type.enclosing_class) 808 prefix_components.append("") 809 enum_prefix = "::".join(prefix_components) 810 encodeLines = [] 811 812 if _type.is_enum(): 813 encode_template = Templates.EnumEncodeCase 814 decode_template = Templates.EnumDecodeCase 815 enum_trait_template = Templates.EnumTraitImplementation 816 else: 817 encode_template = Templates.EnumClassEncodeCase 818 decode_template = Templates.EnumClassDecodeCase 819 enum_trait_template = Templates.EnumClassTraitImplementation 820 821 # Generate body for encode. 822 for _value in _type.values: 823 template_arguments = { 824 'enumStringValue': _value, 825 'qualifiedEnumValue': "%s%s" % (enum_prefix, _value), 826 } 827 encodeLines.append(Template(encode_template).substitute(template_arguments)) 828 829 for guard, guard_values in _type.guard_values_map.iteritems(): 830 guardedLines = [] 831 for guard_value in guard_values: 832 template_arguments = { 833 'enumStringValue': guard_value, 834 'qualifiedEnumValue': "%s%s" % (enum_prefix, guard_value), 835 } 836 guardedLines.append(Template(encode_template).substitute(template_arguments)) 837 encodeLines.append(wrap_with_guard("\n".join(guardedLines), guard)) 838 839 # Generate body for decode. 840 decodeLines = [] 841 for _value in _type.values: 842 template_arguments = { 843 'enumStringValue': _value, 844 'qualifiedEnumValue': "%s%s" % (enum_prefix, _value), 845 'qualifiedEnumName': _type.type_name(qualified=should_qualify_type) 846 } 847 decodeLines.append(Template(decode_template).substitute(template_arguments)) 848 849 for guard, guard_values in _type.guard_values_map.iteritems(): 850 guardedLines = [] 851 for guard_value in guard_values: 852 template_arguments = { 853 'enumStringValue': guard_value, 854 'qualifiedEnumValue': "%s%s" % (enum_prefix, guard_value), 855 'qualifiedEnumName': _type.type_name(qualified=should_qualify_type) 856 } 857 guardedLines.append(Template(decode_template).substitute(template_arguments)) 858 decodeLines.append(wrap_with_guard("\n".join(guardedLines), guard)) 859 860 template_arguments = { 861 'enumName': _type.type_name(qualified=should_qualify_type), 862 'encodeCases': "\n".join(encodeLines), 863 'decodeCases': "\n".join(decodeLines) 864 } 865 866 return Template(enum_trait_template).substitute(template_arguments) 867 868 def generate_input_trait_implementation(self, _input): 869 template_arguments = { 870 'inputsNamespace': self.target_framework.setting('namespace'), 871 'inputTypeImplementation': Template(self.setting('inputTypeTemplate')).substitute(None, inputName=_input.name), 872 'qualifiedInputName': self.qualified_input_name(_input), 873 'constructorArguments': self.generate_constructor_arguments_list(_input), 874 'constructorFormalsList': self.generate_constructor_formals_list(_input), 875 'encodeSteps': self.generate_input_encode_implementation(_input), 876 'decodeSteps': self.generate_input_decode_implementation(_input), 877 } 878 return wrap_with_guard(Template(Templates.InputTraitsImplementation).substitute(template_arguments), _input.guard) 879 880 def generate_input_encode_implementation(self, _input): 881 steps = [] 882 for (_member, _type) in self.generate_input_member_tuples(_input): 883 should_qualify_type = _type.framework != self.traits_framework 884 put_method = "put<%s>" % _type.type_name(qualified=should_qualify_type) 885 886 steps.extend([ 887 " encodedValue.%s(ASCIILiteral(\"%s\"), input.%s());" % (put_method, _member.memberName, _member.memberName) 888 ]) 889 890 if len(steps) == 0: 891 steps.extend([ 892 " UNUSED_PARAM(encodedValue);", 893 " UNUSED_PARAM(input);", 894 ]) 895 896 return "\n".join(steps) 897 898 def generate_input_decode_implementation(self, _input): 899 steps = [] 900 for (_member, _type) in self.generate_input_member_tuples(_input): 901 should_qualify_type = _type.framework != self.traits_framework 902 get_method = "get<%s>" % _type.type_name(qualified=should_qualify_type) 903 904 lines = [ 905 " %s %s;" % (_type.storage_type(qualified=should_qualify_type), _member.memberName), 906 " if (!encodedValue.%s(ASCIILiteral(\"%s\"), %s))" % (get_method, _member.memberName, _member.memberName), 907 " return false;", 908 "" 909 ] 910 911 steps.append("\n".join(lines)) 912 913 if len(steps) == 0: 914 steps.extend([ 915 " UNUSED_PARAM(encodedValue);", 916 ]) 917 918 return "\n".join(steps) 919 920 def generate_constructor_initializer_list(self, _input): 921 initializers = [] 922 initializers.append(" : %s()" % (_input.setting('baseClass') % _input.name)) 923 for _member in _input.members: 924 initializers.append(" , m_%s(%s)" % (_member.memberName, self.generate_member_move_expression(_member))) 925 926 return "\n".join(initializers) 927 928 def generate_constructor_formals_list(self, _input): 929 member_tuples = self.generate_input_member_tuples(_input) 930 return ", ".join(["%s %s" % (_type.argument_type(), _member.memberName) for (_member, _type) in member_tuples]) 931 932 def generate_member_borrow_expression(self, _member): 933 _type = self._model.get_type_for_member(_member) 934 expression = "m_%s" % _member.memberName 935 if _type.mode == TypeModes.OWNED: 936 expression = "*" + expression 937 938 return expression 939 940 def generate_member_move_expression(self, _member): 941 _type = self._model.get_type_for_member(_member) 942 if _type.mode == TypeModes.OWNED: 943 return "WTF::move(%s)" % _member.memberName 944 else: 945 return _member.memberName 946 947 def generate_constructor_arguments_list(self, _input): 948 return ", ".join([self.generate_member_move_expression(_member) for _member in _input.members]) 949 950 951def generate_from_specification(input_filepath=None, output_prefix="", output_dirpath=None, framework_name=None, force_output=False): 952 try: 953 with open(input_filepath, "r") as input_file: 954 parsed_json = json.load(input_file) 955 except ValueError as e: 956 raise Exception("Error parsing valid JSON in file: " + input_filepath) 957 958 if not framework_name in FRAMEWORK_CONFIG_MAP: 959 raise ParseException("Unknown or unsupported framework name supplied: " + framework_name) 960 961 model = InputsModel(parsed_json) 962 model.resolve_types() 963 generator = Generator(model, framework_name, input_filepath, output_prefix) 964 965 generator.write_output_files(output_dirpath, force_output) 966 967 968if __name__ == '__main__': 969 allowed_framework_names = FRAMEWORK_CONFIG_MAP.keys() 970 971 cli_parser = optparse.OptionParser(usage="usage: %prog [options] <Inputs.json>") 972 cli_parser.add_option("-o", "--outputDir", help="Directory where generated files should be written.") 973 cli_parser.add_option("--framework", type="choice", choices=allowed_framework_names, help="The framework these inputs belong to.") # JavaScriptCore, WebCore 974 cli_parser.add_option("--force", action="store_true", help="Force output of generated scripts, even if nothing changed.") 975 cli_parser.add_option("-v", "--debug", action="store_true", help="Log extra output for debugging the generator itself.") 976 cli_parser.add_option("-t", "--test", action="store_true", help="Enable test mode. Use unique output filenames created by prepending the input filename.") 977 978 options = None 979 980 arg_options, arg_values = cli_parser.parse_args() 981 if (len(arg_values) < 1): 982 raise ParseException("At least one plain argument expected") 983 984 if not arg_options.outputDir: 985 raise ParseException("Missing output directory") 986 987 if arg_options.debug: 988 log.setLevel(logging.DEBUG) 989 990 options = { 991 'input_filepath': arg_values[0], 992 'output_dirpath': arg_options.outputDir, 993 'output_prefix': os.path.basename(arg_values[0]) if arg_options.test else "", 994 'framework_name': arg_options.framework, 995 'force_output': arg_options.force 996 } 997 998 try: 999 generate_from_specification(**options) 1000 except (ParseException, TypecheckException) as e: 1001 if arg_options.test: 1002 log.error(e.message) 1003 else: 1004 raise e # Force the build to fail. 1005