2 # Copyright (c) 2014 Apple Inc. All rights reserved.
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
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
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.
31 from string
import Template
34 from CodeGeneratorReplayInputsTemplates
import Templates
39 import simplejson
as json
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.
44 "baseFilename": "ReplayInputs",
45 "guardCondition": "ENABLE(WEB_REPLAY)",
46 "traitsFrameworkName": "JavaScriptCore",
48 # These are formatted as ([allowed_frameworks], (framework, header_path)).
49 # The generator can figure out how to format the includes.
52 ("WebCore", "replay/EventLoopInput.h")
54 (["JavaScriptCore", "WebCore"],
55 ("JavaScriptCore", "replay/EncodedValue.h")
58 ("JavaScriptCore", "replay/NondeterministicInput.h")
60 (["JavaScriptCore", "WebCore"],
61 ("WTF", "wtf/TypeCasts.h")
64 ("WTF", "wtf/text/WTFString.h")
69 ("WebCore", "platform/ExternalNamespaceHeaderIncludeDummy.h")
72 ("Test", "platform/InternalNamespaceHeaderIncludeDummy.h")
78 ("WebCore", "replay/SerializationMethods.h")
80 (["WebCore", "JavaScriptCore"],
81 ("JavaScriptCore", "inspector/InspectorValues.h")
83 (["WebCore", "JavaScriptCore"],
84 ("WTF", "wtf/NeverDestroyed.h")
89 ("WebCore", "platform/ExternalNamespaceImplIncludeDummy.h")
92 ("Test", "platform/InternalNamespaceImplIncludeDummy.h")
97 FRAMEWORK_CONFIG_MAP
= {
110 "exportMacro": "JS_EXPORT_PRIVATE",
114 "namespace": "WebCore",
115 "exportMacro": "WEBCORE_EXPORT"
117 # Used for bindings tests.
121 "exportMacro": "TEST_EXPORT_MACRO"
125 # These settings are specific to an input queue.
128 "enumValue": "ScriptMemoizedData",
129 "baseClass": "NondeterministicInput<%s>",
132 "enumValue": "LoaderMemoizedData",
133 "baseClass": "NondeterministicInput<%s>",
136 "enumValue": "EventLoopInput",
137 "baseClass": "EventLoopInput<%s>",
141 # Use a global logger, which normally only logs errors.
142 # It can be configured to log debug messages from the CLI.
143 logging
.basicConfig(format
='%(levelname)s: %(message)s', level
=logging
.ERROR
)
144 log
= logging
.getLogger('global')
147 # Model classes, which transliterate JSON input.
148 class ParseException(Exception):
152 class TypecheckException(Exception):
157 def __init__(self
, name
):
158 self
._settings
= FRAMEWORK_CONFIG_MAP
[name
]
161 def setting(self
, key
, default
=''):
162 return self
._settings
.get(key
, default
)
165 def fromString(frameworkString
):
166 if frameworkString
== "Global":
167 return Frameworks
.Global
169 if frameworkString
== "WTF":
170 return Frameworks
.WTF
172 if frameworkString
== "JavaScriptCore":
173 return Frameworks
.JavaScriptCore
175 if frameworkString
== "WebCore":
176 return Frameworks
.WebCore
178 if frameworkString
== "Test":
179 return Frameworks
.Test
181 raise ParseException("Unknown framework: " + frameworkString
)
185 Global
= Framework("Global")
186 WTF
= Framework("WTF")
187 JavaScriptCore
= Framework("JavaScriptCore")
188 WebCore
= Framework("WebCore")
189 Test
= Framework("Test")
193 def __init__(self
, settings
):
194 self
._settings
= settings
196 def setting(self
, key
, default
=''):
197 return self
._settings
.get(key
, default
)
200 def fromString(queueString
):
201 if queueString
== "SCRIPT_MEMOIZED":
202 return InputQueues
.SCRIPT_MEMOIZED
204 if queueString
== "LOADER_MEMOIZED":
205 return InputQueues
.LOADER_MEMOIZED
207 if queueString
== "EVENT_LOOP":
208 return InputQueues
.EVENT_LOOP
210 raise ParseException("Unknown input queue: " + queueString
)
214 SCRIPT_MEMOIZED
= InputQueue(QUEUE_CONFIG_MAP
["SCRIPT_MEMOIZED"])
215 LOADER_MEMOIZED
= InputQueue(QUEUE_CONFIG_MAP
["LOADER_MEMOIZED"])
216 EVENT_LOOP
= InputQueue(QUEUE_CONFIG_MAP
["EVENT_LOOP"])
220 def __init__(self
, name
, description
, framework
, queueString
, flags
, guard
=None):
222 self
.description
= description
223 self
.framework
= framework
224 self
.queue
= InputQueue
.fromString(queueString
)
227 self
.members
= [] # names should be unique, but ordered.
229 def setting(self
, key
, default
=''):
230 if key
in self
._flags
:
233 return self
.queue
.setting(key
, default
)
237 def __init__(self
, memberName
, typeName
, flags
=[]):
238 self
.memberName
= memberName
239 self
.typeName
= typeName
242 def has_flag(self
, key
, default
=''):
243 return key
in self
._flags
247 def __init__(self
, name
):
251 def fromString(modeString
):
252 modeString
= modeString
.upper()
253 if modeString
== 'SCALAR':
254 return TypeModes
.SCALAR
255 if modeString
== 'HEAVY_SCALAR':
256 return TypeModes
.HEAVY_SCALAR
257 if modeString
== 'OWNED':
258 return TypeModes
.OWNED
259 if modeString
== 'SHARED':
260 return TypeModes
.SHARED
261 if modeString
== 'VECTOR':
262 return TypeModes
.VECTOR
264 raise ParseException("Unknown type mode: " + modeString
)
268 # Copy for assignment and for getter
269 SCALAR
= TypeMode("SCALAR")
270 # Copy for assignment, pass by reference for getter
271 HEAVY_SCALAR
= TypeMode("HEAVY_SCALAR")
272 # Move for assignment, pass by reference for getter
273 OWNED
= TypeMode("OWNED")
274 # Copy a RefPtr for assignment and getter
275 SHARED
= TypeMode("SHARED")
276 # Move operator for assignment, pass by reference for getter
277 VECTOR
= TypeMode("VECTOR")
281 def __init__(self
, name
, mode
, framework
, header
, enclosing_class
, values
, guard_values_map
, underlying_storage
, flags
, guard
=None):
284 self
.framework
= framework
286 self
.enclosing_class
= enclosing_class
288 self
.guard_values_map
= guard_values_map
289 self
.underlying_storage
= underlying_storage
293 def __eq__(self
, other
):
294 return self
.type_name() == other
.type_name() and self
.mode
== other
.mode
297 return self
._name
.__hash
__()
299 def has_flag(self
, flagString
):
300 return flagString
in self
._flags
303 return self
.has_flag("STRUCT")
306 return self
.has_flag("ENUM")
308 def is_enum_class(self
):
309 return self
.has_flag("ENUM_CLASS")
311 def declaration_kind(self
):
314 elif self
.is_enum_class():
316 elif self
.is_struct():
321 def qualified_prefix(self
):
323 if self
.framework
!= Frameworks
.Global
:
324 components
.append(self
.framework
.setting('namespace'))
325 if self
.enclosing_class
is not None:
326 components
.append(self
.enclosing_class
)
327 components
.append("")
328 return "::".join(components
)
330 def type_name(self
, qualified
=False):
332 return "%s%s" % (self
.qualified_prefix(), self
._name
)
333 elif self
.enclosing_class
is not None:
334 return "%s::%s" % (self
.enclosing_class
, self
._name
)
338 def storage_type(self
, qualified
=False):
339 if self
.mode
== TypeModes
.OWNED
:
340 return "std::unique_ptr<%s>" % self
.type_name(qualified
)
341 elif self
.mode
== TypeModes
.SHARED
:
342 return "RefPtr<%s>" % self
.type_name(qualified
)
344 return self
.type_name(qualified
)
346 def borrow_type(self
, qualified
=False):
347 if self
.mode
== TypeModes
.SCALAR
:
348 return self
.type_name(qualified
)
349 elif self
.mode
== TypeModes
.SHARED
:
350 return "RefPtr<%s>" % self
.type_name(qualified
)
352 return "const %s&" % self
.type_name(qualified
)
354 def argument_type(self
, qualified
=False):
355 if self
.mode
== TypeModes
.SHARED
:
356 return "RefPtr<%s>&&" % self
.type_name(qualified
)
358 return self
.storage_type()
360 def encoding_type_argument(self
, qualified
=False):
361 return self
.type_name(qualified
=qualified
)
364 def check_for_required_properties(props
, obj
, what
):
367 raise ParseException("When parsing %s, required property missing: %s" % (what
, prop
))
370 class 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
386 def is_enum_class(self
):
389 def qualified_prefix(self
):
392 def type_name(self
, qualified
=False):
393 return "Vector<%s>" % self
._element
_type
.storage_type(qualified
=qualified
)
395 def encoding_type_argument(self
, qualified
=False):
396 return "Vector<%s>" % self
._element
_type
.type_name(qualified
=qualified
)
398 def argument_type(self
, qualified
=False):
399 return self
.type_name(qualified
=qualified
) + "&"
407 # Types have associated frameworks and are in their namespace, but within the specification
408 # file types are in a flat namespace. Types with the same name are not allowed.
409 self
.types_by_name
= {}
410 self
.inputs_by_name
= {}
412 def enum_types(self
):
413 _enums
= filter(lambda x
: x
.is_enum() or x
.is_enum_class(), self
.types
)
414 return sorted(_enums
, key
=lambda _enum
: _enum
.type_name())
416 def get_type_for_member(self
, member
):
417 if member
.has_flag("VECTOR"):
418 return VectorType(self
.types_by_name
.get(member
.typeName
))
420 return self
.types_by_name
.get(member
.typeName
)
422 def parse_specification(self
, json
):
424 if not isinstance(json
['types'], dict):
425 raise ParseException("Malformed specification: types is not a dict of framework->type list")
427 for framework_name
, type_list
in json
['types'].iteritems():
428 if not isinstance(type_list
, list):
429 raise ParseException("Malformed specification: type list for framework %s is not a list" % framework_name
)
431 framework
= Framework
.fromString(framework_name
)
432 for _type
in type_list
:
433 self
.parse_type_with_framework(_type
, framework
)
436 if not isinstance(json
['inputs'], dict):
437 raise ParseException("Malformed specification: inputs is not a dict of framework->input list")
439 for framework_name
, input_list
in json
['inputs'].iteritems():
440 if not isinstance(input_list
, list):
441 raise ParseException("Malformed specification: input list for framework %s is not a list" % framework_name
)
443 framework
= Framework
.fromString(framework_name
)
444 for _input
in input_list
:
445 self
.parse_input_with_framework(_input
, framework
)
447 def parse_type_with_framework(self
, json
, framework
):
448 check_for_required_properties(['name', 'mode'], json
, 'type')
449 if framework
is not Frameworks
.Global
:
450 check_for_required_properties(['header'], json
, 'non-global type')
452 type_name
= json
['name']
453 type_mode
= TypeMode
.fromString(json
['mode'])
454 header
= json
.get('header')
455 enclosing_class
= json
.get('enclosing_class')
456 enum_values
= json
.get('values')
457 guarded_enum_values
= json
.get('guarded_values', {})
458 type_storage
= json
.get('storage')
459 type_flags
= json
.get('flags', [])
460 guard
= json
.get('guard', None)
461 _type
= Type(type_name
, type_mode
, framework
, header
, enclosing_class
, enum_values
, guarded_enum_values
, type_storage
, type_flags
, guard
)
462 if _type
.is_enum() or _type
.is_enum_class():
463 check_for_required_properties(['values'], json
, 'enum')
464 if not isinstance(json
['values'], list) or len(_type
.values
) == 0:
465 raise ParseException("Malformed specification: enum %s does not supply a list of values" % type_name
)
467 if _type
.is_enum() and enclosing_class
is None and type_storage
is None:
468 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
)
470 self
.types
.append(_type
)
472 def parse_input_with_framework(self
, json
, framework
):
473 check_for_required_properties(['name', 'description', 'queue', 'members'], json
, 'input')
474 _input
= Input(json
['name'], json
['description'], framework
, json
['queue'], json
.get('flags', []), json
.get('guard'))
475 if isinstance(json
['members'], list):
476 for member
in json
['members']:
477 check_for_required_properties(['name', 'type'], member
, 'member')
478 _input
.members
.append(InputMember(member
['name'], member
['type'], member
.get('flags', [])))
480 self
.inputs
.append(_input
)
482 # Types cannot (yet) reference other types, so we can check references in one pass.
483 def resolve_types(self
):
484 for _type
in self
.types
:
485 self
.typecheck_type(_type
)
487 for _input
in self
.inputs
:
488 self
.typecheck_input(_input
)
490 def typecheck_type(self
, _type
):
491 log
.debug("typecheck type " + _type
.type_name())
493 if _type
.type_name() in self
.types_by_name
:
494 raise TypecheckException("Duplicate type with name: " + _type
.type_name())
496 self
.types_by_name
[_type
.type_name()] = _type
498 def typecheck_input(self
, _input
):
499 log
.debug("typecheck input " + _input
.name
)
501 if _input
.name
in self
.inputs_by_name
:
502 raise TypecheckException("Duplicate input with name: " + _input
.name
)
506 for member
in _input
.members
:
507 if member
.memberName
in seen_members
:
508 raise TypecheckException("Duplicate input member with name: " + member
.memberName
)
510 self
.typecheck_input_member(member
, _input
)
511 seen_members
[member
.memberName
] = member
513 self
.inputs_by_name
[_input
.name
] = _input
515 def typecheck_input_member(self
, input_member
, _input
):
516 log
.debug("typecheck member '%s' of '%s'" % (input_member
.memberName
, _input
.name
))
518 if not input_member
.typeName
in self
.types_by_name
:
519 raise TypecheckException("Unknown type '%s' referenced by member '%s' of input '%s'" % (input_member
.typeName
, input_member
.memberName
, _input
.name
))
522 # A writer that only updates file if it actually changed.
523 class IncrementalFileWriter
:
524 def __init__(self
, filepath
, force_output
):
525 self
._filepath
= filepath
527 self
.force_output
= force_output
529 def write(self
, text
):
534 self
._output
= self
._output
.rstrip() + "\n"
537 read_file
= open(self
._filepath
, "r")
538 old_text
= read_file
.read()
540 text_changed
= old_text
!= self
._output
542 # Ignore, just overwrite by default
545 if text_changed
or self
.force_output
:
546 out_file
= open(self
._filepath
, "w")
547 out_file
.write(self
._output
)
551 def wrap_with_guard(contents
, condition
=None):
552 if condition
is None:
556 "#if %s" % condition
,
558 "#endif // %s" % condition
563 def __init__(self
, model
, target_framework_name
, input_filepath
, output_prefix
):
565 self
.target_framework
= Framework
.fromString(target_framework_name
)
566 self
.traits_framework
= Framework
.fromString(self
.setting('traitsFrameworkName'))
567 self
._input
_filepath
= input_filepath
568 self
._output
_prefix
= output_prefix
570 def setting(self
, key
, default
=''):
571 return self
.target_framework
.setting(key
, GLOBAL_CONFIG
.get(key
, default
))
573 def should_generate_item(self
, item
):
574 return item
.framework
is self
.target_framework
576 # This does not account for any filename mangling performed on behalf of the test harness.
577 def output_filename(self
, extension
=None):
579 if len(self
._output
_prefix
) > 0:
580 components
.extend([self
._output
_prefix
, '-'])
582 components
.extend([self
.setting('prefix'), self
.setting('baseFilename')])
584 if extension
is not None:
585 components
.extend(['.', extension
])
587 return "".join(components
)
589 def write_output_files(self
, _dir
, force
=False):
590 header_file
= IncrementalFileWriter(os
.path
.join(_dir
, self
.output_filename('h')), force
)
591 implementation_file
= IncrementalFileWriter(os
.path
.join(_dir
, self
.output_filename('cpp')), force
)
593 header_file
.write(self
.generate_header())
594 implementation_file
.write(self
.generate_implementation())
597 implementation_file
.close()
599 def generate_header(self
):
600 enums_to_generate
= filter(self
.should_generate_item
, self
._model
.enum_types())
601 inputs_to_generate
= filter(self
.should_generate_item
, self
._model
.inputs
)
603 template_arguments
= {
604 'licenseBlock': self
.generate_license(),
605 'headerGuard': re
.sub('[-./]', '_', self
.output_filename() + ".h"),
606 'filename': self
.output_filename(),
607 'guardCondition': self
.setting('guardCondition'),
608 'traitsNamespace': self
.traits_framework
.setting('namespace'),
609 'inputsNamespace': self
.target_framework
.setting('namespace'),
610 'includes': self
.generate_includes(defaults
=self
.setting('headerIncludes')),
611 'typeForwardDeclarations': self
.generate_type_forward_declarations(),
612 'inputForwardDeclarations': "\n".join([wrap_with_guard("class %s;", _input
.guard
) % _input
.name
for _input
in inputs_to_generate
]),
613 'inputClassDeclarations': "\n\n".join([self
.generate_class_declaration(_input
) for _input
in inputs_to_generate
]),
614 'inputTraitDeclarations': "\n\n".join([self
.generate_input_trait_declaration(_input
) for _input
in inputs_to_generate
]),
615 'inputTypeTraitDeclarations': "\n\n".join([self
.generate_input_type_trait_declaration(_input
) for _input
in inputs_to_generate
]),
616 'enumTraitDeclarations': "\n\n".join([wrap_with_guard(self
.generate_enum_trait_declaration(_type
), _type
.guard
) for _type
in enums_to_generate
]),
617 'forEachMacro': self
.generate_for_each_macro(),
620 return Template(Templates
.HeaderSkeleton
).substitute(template_arguments
)
622 def generate_implementation(self
):
623 enums_to_generate
= filter(self
.should_generate_item
, self
._model
.enum_types())
624 inputs_to_generate
= filter(self
.should_generate_item
, self
._model
.inputs
)
626 template_arguments
= {
627 'licenseBlock': self
.generate_license(),
628 'filename': self
.output_filename(),
629 'guardCondition': self
.setting('guardCondition'),
630 'traitsNamespace': self
.traits_framework
.setting('namespace'),
631 'inputsNamespace': self
.target_framework
.setting('namespace'),
632 'includes': self
.generate_includes(defaults
=self
.setting('implIncludes'), includes_for_types
=True),
633 'inputClassImplementations': "\n\n".join([self
.generate_class_implementation(_input
) for _input
in inputs_to_generate
]),
634 'inputTraitImplementations': "\n\n".join([self
.generate_input_trait_implementation(_input
) for _input
in inputs_to_generate
]),
635 'enumTraitImplementations': "\n\n".join([wrap_with_guard(self
.generate_enum_trait_implementation(_type
), _type
.guard
) for _type
in enums_to_generate
]),
638 return Template(Templates
.ImplementationSkeleton
).substitute(template_arguments
)
640 def generate_license(self
):
641 return Template(Templates
.CopyrightBlock
).substitute(None, inputFilename
=os
.path
.basename(self
._input
_filepath
))
643 def generate_includes(self
, defaults
=[], includes_for_types
=False):
646 for _type
in self
._model
.types
:
647 # Types in the "global" framework are implicitly declared and available in all namespaces.
648 if _type
.framework
is Frameworks
.Global
:
650 # For RefCounted types, we reverse when to include the header so that the destructor can be
651 # used in the header file.
652 include_for_destructor
= _type
.mode
is TypeModes
.SHARED
653 # Enums within classes cannot be forward declared, so we include
654 # headers with the relevant class declaration.
655 include_for_enclosing_class
= _type
.enclosing_class
is not None
656 # Include headers for types like URL and String which are copied, not owned or shared.
657 include_for_copyable_member
= _type
.mode
is TypeModes
.HEAVY_SCALAR
658 if (not includes_for_types
) ^
(include_for_destructor
or include_for_enclosing_class
or include_for_copyable_member
):
661 if self
.target_framework
!= _type
.framework
:
662 lines
.add("#include <%s>" % _type
.header
)
664 lines
.add("#include \"%s\"" % os
.path
.basename(_type
.header
))
666 for entry
in defaults
:
667 (allowed_framework_names
, data
) = entry
668 (framework_name
, header_path
) = data
670 if self
.target_framework
.name
not in allowed_framework_names
:
672 if self
.target_framework
.name
!= framework_name
:
673 lines
.add("#include <%s>" % header_path
)
675 lines
.add("#include \"%s\"" % os
.path
.basename(header_path
))
677 return "\n".join(sorted(list(lines
)))
679 def generate_type_forward_declarations(self
):
682 decls_by_framework
= {}
683 frameworks
= [Framework
.fromString(s
) for s
in FRAMEWORK_CONFIG_MAP
.keys() if s
!= Frameworks
.Global
.name
]
684 for framework
in frameworks
:
685 decls_by_framework
[framework
] = []
687 for _type
in self
._model
.types
:
688 if _type
.framework
not in frameworks
:
690 if _type
.enclosing_class
is not None:
692 if _type
.mode
== TypeModes
.HEAVY_SCALAR
:
694 if _type
.mode
== TypeModes
.SCALAR
and not (_type
.is_enum() or _type
.is_enum_class()):
697 declaration
= "enum %s : %s;" % (_type
.type_name(), _type
.underlying_storage
)
699 declaration
= "%s %s;" % (_type
.declaration_kind(), _type
.type_name())
700 decls_by_framework
[_type
.framework
].append(declaration
)
702 # Declare all namespaces explicitly, even if it's the main namespace.
703 for framework
in frameworks
:
704 if len(decls_by_framework
[framework
]) == 0:
707 decls_by_framework
[framework
].sort()
708 lines
.append("namespace %s {" % framework
.setting('namespace'))
709 lines
.extend(decls_by_framework
[framework
])
713 return "\n".join(lines
)
715 def generate_class_declaration(self
, _input
):
716 extra_declarations
= []
717 if _input
.queue
== InputQueues
.EVENT_LOOP
:
718 extra_declarations
.extend([
720 " // EventLoopInput API",
721 " virtual void dispatch(ReplayController&) override final;",
724 if _input
.setting('CREATE_FROM_PAGE'):
725 extra_declarations
.extend([
726 " static std::unique_ptr<%s> createFromPage(const Page&);" % _input
.name
729 member_getters
= [self
.generate_input_member_getter(_member
) for _member
in _input
.members
]
731 member_declarations
= [self
.generate_input_member_declaration(_member
) for _member
in _input
.members
]
732 if len(member_declarations
) > 0:
733 member_declarations
.insert(0, "private:")
735 template_arguments
= {
736 'inputConstructor': self
.generate_input_constructor_declaration(_input
),
737 'inputDestructor': self
.generate_input_destructor_declaration(_input
),
738 'inputName': _input
.name
,
739 'inputQueue': _input
.setting('enumValue'),
740 'baseClass': _input
.setting('baseClass') % _input
.name
,
741 'extraDeclarations': "\n".join(extra_declarations
),
742 'memberGetters': "\n".join(member_getters
),
743 'memberDeclarations': "\n".join(member_declarations
),
746 return wrap_with_guard(Template(Templates
.InputClassDeclaration
).substitute(template_arguments
), _input
.guard
)
748 def generate_input_constructor_declaration(self
, _input
):
749 formals_list
= self
.generate_constructor_formals_list(_input
)
751 if self
.setting('exportMacro'):
752 terms
.append(self
.setting('exportMacro'))
753 terms
.append("%s(%s)" % (_input
.name
, formals_list
))
754 return " %s;" % " ".join(terms
)
756 def generate_input_destructor_declaration(self
, _input
):
757 return " virtual ~%s();" % _input
.name
759 def generate_input_member_getter(self
, _member
):
760 member_type
= self
._model
.get_type_for_member(_member
)
761 return " %s %s() const { return %s; }" % (member_type
.borrow_type(), _member
.memberName
, self
.generate_member_borrow_expression(_member
))
763 def generate_input_member_declaration(self
, _member
):
764 member_type
= self
._model
.get_type_for_member(_member
)
765 return " %s m_%s;" % (member_type
.storage_type(), _member
.memberName
)
767 def generate_input_member_tuples(self
, _input
):
768 return [(_member
, self
._model
.get_type_for_member(_member
)) for _member
in _input
.members
]
770 def qualified_input_name(self
, _input
, forceQualified
=False):
771 if forceQualified
or self
.target_framework
!= self
.traits_framework
:
772 return "%s::%s" % (self
.target_framework
.setting('namespace'), _input
.name
)
776 def generate_input_trait_declaration(self
, _input
):
777 decl_type
= ['struct']
778 if len(self
.setting('exportMacro')) > 0:
779 decl_type
.append(self
.setting('exportMacro'))
781 template_arguments
= {
782 'structOrClass': " ".join(decl_type
),
783 'queueType': _input
.queue
.setting('enumValue'),
784 'qualifiedInputName': self
.qualified_input_name(_input
),
787 return wrap_with_guard(Template(Templates
.InputTraitsDeclaration
).substitute(template_arguments
), _input
.guard
)
789 def generate_input_type_trait_declaration(self
, _input
):
790 template_arguments
= {
791 'qualifiedInputName': self
.qualified_input_name(_input
, forceQualified
=True),
794 return wrap_with_guard(Template(Templates
.InputTypeTraitsDeclaration
).substitute(template_arguments
), _input
.guard
)
796 def generate_enum_trait_declaration(self
, _type
):
797 decl_type
= ['struct']
798 if len(self
.setting('exportMacro')) > 0:
799 decl_type
.append(self
.setting('exportMacro'))
801 should_qualify_type
= _type
.framework
!= self
.traits_framework
802 template
= Templates
.EnumTraitDeclaration
if _type
.is_enum() else Templates
.EnumClassTraitDeclaration
803 template_arguments
= {
804 'encodingTypeArgument': _type
.encoding_type_argument(qualified
=should_qualify_type
),
805 'enumType': _type
.type_name(qualified
=should_qualify_type
),
806 'structOrClass': " ".join(decl_type
)
808 return Template(template
).substitute(template_arguments
)
810 def generate_for_each_macro(self
):
811 inputs_to_generate
= filter(self
.should_generate_item
, self
._model
.inputs
)
813 macro_name
= "%s_REPLAY_INPUT_NAMES_FOR_EACH" % self
.setting('prefix').upper()
815 lines
.append("#define %s(macro) \\" % macro_name
)
816 lines
.extend([" macro(%s) \\" % _input
.name
for _input
in inputs_to_generate
])
818 lines
.append("// end of %s" % macro_name
)
819 return "\n".join(lines
)
821 def generate_class_implementation(self
, _input
):
822 template_arguments
= {
823 'inputName': _input
.name
,
824 'inputsNamespace': self
.target_framework
.setting('namespace'),
825 'initializerList': self
.generate_constructor_initializer_list(_input
),
826 'constructorFormalsList': self
.generate_constructor_formals_list(_input
),
829 return wrap_with_guard(Template(Templates
.InputClassImplementation
).substitute(template_arguments
), _input
.guard
)
831 def generate_enum_trait_implementation(self
, _type
):
832 should_qualify_type
= _type
.framework
!= self
.traits_framework
833 prefix_components
= []
834 if should_qualify_type
:
835 prefix_components
.append(_type
.framework
.setting('namespace'))
836 if _type
.is_enum() and _type
.enclosing_class
is not None:
837 prefix_components
.append(_type
.enclosing_class
)
838 elif _type
.is_enum_class():
839 prefix_components
.append(_type
.type_name(qualified
=False))
840 prefix_components
.append("")
841 enum_prefix
= "::".join(prefix_components
)
845 encode_template
= Templates
.EnumEncodeCase
846 decode_template
= Templates
.EnumDecodeCase
847 enum_trait_template
= Templates
.EnumTraitImplementation
849 encode_template
= Templates
.EnumClassEncodeCase
850 decode_template
= Templates
.EnumClassDecodeCase
851 enum_trait_template
= Templates
.EnumClassTraitImplementation
853 # Generate body for encode.
854 for _value
in _type
.values
:
855 template_arguments
= {
856 'enumStringValue': _value
,
857 'qualifiedEnumValue': "%s%s" % (enum_prefix
, _value
),
859 encodeLines
.append(Template(encode_template
).substitute(template_arguments
))
861 for guard
, guard_values
in _type
.guard_values_map
.iteritems():
863 for guard_value
in guard_values
:
864 template_arguments
= {
865 'enumStringValue': guard_value
,
866 'qualifiedEnumValue': "%s%s" % (enum_prefix
, guard_value
),
868 guardedLines
.append(Template(encode_template
).substitute(template_arguments
))
869 encodeLines
.append(wrap_with_guard("\n".join(guardedLines
), guard
))
871 # Generate body for decode.
873 for i
, _value
in enumerate(_type
.values
):
875 template_arguments
= {
876 'branchKeyword': "else if" if i
> 0 else "if",
877 'enumStringValue': _value
,
878 'qualifiedEnumValue': "%s%s" % (enum_prefix
, _value
),
879 'qualifiedEnumName': _type
.type_name(qualified
=should_qualify_type
)
881 decodeLines
.append(Template(decode_template
).substitute(template_arguments
))
883 for guard
, guard_values
in _type
.guard_values_map
.iteritems():
885 for i
, guard_value
in enumerate(guard_values
):
886 template_arguments
= {
887 'branchKeyword': "else if" if i
> 0 else "if",
888 'enumStringValue': guard_value
,
889 'qualifiedEnumValue': "%s%s" % (enum_prefix
, guard_value
),
890 'qualifiedEnumName': _type
.type_name(qualified
=should_qualify_type
)
892 guardedLines
.append(Template(decode_template
).substitute(template_arguments
))
893 decodeLines
.append(wrap_with_guard("\n".join(guardedLines
), guard
))
895 template_arguments
= {
896 'encodingTypeArgument': _type
.encoding_type_argument(qualified
=should_qualify_type
),
897 'enumType': _type
.type_name(qualified
=should_qualify_type
),
898 'encodeCases': "\n".join(encodeLines
),
899 'decodeCases': "\n".join(decodeLines
)
902 return Template(enum_trait_template
).substitute(template_arguments
)
904 def generate_input_trait_implementation(self
, _input
):
905 template_arguments
= {
906 'inputsNamespace': self
.target_framework
.setting('namespace'),
907 'inputNameStringLiteral': '"%s"' % _input
.name
,
908 'qualifiedInputName': self
.qualified_input_name(_input
),
909 'constructorArguments': self
.generate_constructor_arguments_list(_input
),
910 'constructorFormalsList': self
.generate_constructor_formals_list(_input
),
911 'encodeSteps': self
.generate_input_encode_implementation(_input
),
912 'decodeSteps': self
.generate_input_decode_implementation(_input
),
914 return wrap_with_guard(Template(Templates
.InputTraitsImplementation
).substitute(template_arguments
), _input
.guard
)
916 def generate_input_encode_implementation(self
, _input
):
918 for (_member
, _type
) in self
.generate_input_member_tuples(_input
):
919 should_qualify_type
= _type
.framework
!= self
.traits_framework
920 put_method
= "put<%s>" % _type
.encoding_type_argument(qualified
=should_qualify_type
)
923 " encodedValue.%s(ASCIILiteral(\"%s\"), input.%s());" % (put_method
, _member
.memberName
, _member
.memberName
)
928 " UNUSED_PARAM(encodedValue);",
929 " UNUSED_PARAM(input);",
932 return "\n".join(steps
)
934 def generate_input_decode_implementation(self
, _input
):
936 for (_member
, _type
) in self
.generate_input_member_tuples(_input
):
937 should_qualify_type
= _type
.framework
!= self
.traits_framework
938 get_method
= "get<%s>" % _type
.encoding_type_argument(qualified
=should_qualify_type
)
941 " %s %s;" % (_type
.storage_type(qualified
=should_qualify_type
), _member
.memberName
),
942 " if (!encodedValue.%s(ASCIILiteral(\"%s\"), %s))" % (get_method
, _member
.memberName
, _member
.memberName
),
947 steps
.append("\n".join(lines
))
951 " UNUSED_PARAM(encodedValue);",
954 return "\n".join(steps
)
956 def generate_constructor_initializer_list(self
, _input
):
958 initializers
.append(" : %s()" % (_input
.setting('baseClass') % _input
.name
))
959 for _member
in _input
.members
:
960 initializers
.append(" , m_%s(%s)" % (_member
.memberName
, self
.generate_member_move_expression(_member
)))
962 return "\n".join(initializers
)
964 def generate_constructor_formals_list(self
, _input
):
965 member_tuples
= self
.generate_input_member_tuples(_input
)
966 return ", ".join(["%s %s" % (_type
.argument_type(), _member
.memberName
) for (_member
, _type
) in member_tuples
])
968 def generate_member_borrow_expression(self
, _member
):
969 _type
= self
._model
.get_type_for_member(_member
)
970 expression
= "m_%s" % _member
.memberName
971 if _type
.mode
== TypeModes
.OWNED
:
972 expression
= "*" + expression
976 def generate_member_move_expression(self
, _member
):
977 _type
= self
._model
.get_type_for_member(_member
)
978 if _type
.mode
in [TypeModes
.OWNED
, TypeModes
.SHARED
]:
979 return "WTF::move(%s)" % _member
.memberName
981 return _member
.memberName
983 def generate_constructor_arguments_list(self
, _input
):
984 return ", ".join([self
.generate_member_move_expression(_member
) for _member
in _input
.members
])
987 def generate_from_specifications(input_filepaths
=[], output_prefix
="", output_dirpath
=None, framework_name
=None, force_output
=False):
989 if not framework_name
in FRAMEWORK_CONFIG_MAP
:
990 raise ParseException("Unknown or unsupported framework name supplied: " + framework_name
)
992 if len(input_filepaths
) == 0:
993 raise ParseException("Must provide at least one specification file, none were provided.")
995 def parse_json_from_file(input_filepath
):
997 with open(input_filepath
, "r") as input_file
:
998 return json
.load(input_file
)
999 except ValueError as e
:
1000 raise Exception("Error parsing valid JSON in file: " + input_filepath
)
1002 specifications
= map(parse_json_from_file
, input_filepaths
)
1004 model
= InputsModel()
1005 for spec
in specifications
:
1006 model
.parse_specification(spec
)
1008 model
.resolve_types()
1009 generator
= Generator(model
, framework_name
, input_filepaths
[0], output_prefix
)
1011 generator
.write_output_files(output_dirpath
, force_output
)
1014 if __name__
== '__main__':
1015 allowed_framework_names
= FRAMEWORK_CONFIG_MAP
.keys()
1017 cli_parser
= optparse
.OptionParser(usage
="usage: %prog [options] <Inputs.json> [, <MoreInputs.json> ]")
1018 cli_parser
.add_option("-o", "--outputDir", help="Directory where generated files should be written.")
1019 cli_parser
.add_option("--framework", type="choice", choices
=allowed_framework_names
, help="The framework these inputs belong to.") # JavaScriptCore, WebCore
1020 cli_parser
.add_option("--force", action
="store_true", help="Force output of generated scripts, even if nothing changed.")
1021 cli_parser
.add_option("-v", "--debug", action
="store_true", help="Log extra output for debugging the generator itself.")
1022 cli_parser
.add_option("-t", "--test", action
="store_true", help="Enable test mode. Use unique output filenames created by prepending the input filename.")
1026 arg_options
, arg_values
= cli_parser
.parse_args()
1028 if not arg_options
.outputDir
:
1029 raise ParseException("Missing output directory")
1031 if arg_options
.debug
:
1032 log
.setLevel(logging
.DEBUG
)
1035 'input_filepaths': arg_values
,
1036 'output_dirpath': arg_options
.outputDir
,
1037 'output_prefix': os
.path
.basename(arg_values
[0]) if arg_options
.test
else "",
1038 'framework_name': arg_options
.framework
,
1039 'force_output': arg_options
.force
1043 generate_from_specifications(**options
)
1044 except (ParseException
, TypecheckException
) as e
:
1045 if arg_options
.test
:
1046 log
.error(e
.message
)
1048 raise e
# Force the build to fail.