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")
61 ("WTF", "wtf/text/WTFString.h")
66 ("WebCore", "platform/ExternalNamespaceHeaderIncludeDummy.h")
69 ("Test", "platform/InternalNamespaceHeaderIncludeDummy.h")
75 ("WebCore", "replay/ReplayInputTypes.h")
78 ("WebCore", "replay/SerializationMethods.h")
80 (["WebCore", "JavaScriptCore"],
81 ("JavaScriptCore", "inspector/InspectorValues.h")
84 ("WTF", "wtf/NeverDestroyed.h")
87 ("WTF", "wtf/text/AtomicString.h")
92 ("WebCore", "platform/ExternalNamespaceImplIncludeDummy.h")
95 ("Test", "platform/InternalNamespaceImplIncludeDummy.h")
100 FRAMEWORK_CONFIG_MAP
= {
113 "exportMacro": "JS_EXPORT_PRIVATE",
114 "inputTypeTemplate": Templates
.InputTypeFromStaticLocal
,
118 "namespace": "WebCore",
119 "inputTypeTemplate": Templates
.InputTypeFromThreadLocal
,
121 # Used for bindings tests.
125 "inputTypeTemplate": Templates
.InputTypeFromStaticLocal
,
129 # These settings are specific to an input queue.
132 "enumValue": "ScriptMemoizedData",
133 "baseClass": "NondeterministicInput<%s>",
136 "enumValue": "LoaderMemoizedData",
137 "baseClass": "NondeterministicInput<%s>",
140 "enumValue": "EventLoopInput",
141 "baseClass": "EventLoopInput<%s>",
145 # Use a global logger, which normally only logs errors.
146 # It can be configured to log debug messages from the CLI.
147 logging
.basicConfig(format
='%(levelname)s: %(message)s', level
=logging
.ERROR
)
148 log
= logging
.getLogger('global')
151 # Model classes, which transliterate JSON input.
152 class ParseException(Exception):
156 class TypecheckException(Exception):
161 def __init__(self
, name
):
162 self
._settings
= FRAMEWORK_CONFIG_MAP
[name
]
165 def setting(self
, key
, default
=''):
166 return self
._settings
.get(key
, default
)
169 def fromString(frameworkString
):
170 if frameworkString
== "Global":
171 return Frameworks
.Global
173 if frameworkString
== "WTF":
174 return Frameworks
.WTF
176 if frameworkString
== "JavaScriptCore":
177 return Frameworks
.JavaScriptCore
179 if frameworkString
== "WebCore":
180 return Frameworks
.WebCore
182 if frameworkString
== "Test":
183 return Frameworks
.Test
185 raise ParseException("Unknown framework: " + frameworkString
)
189 Global
= Framework("Global")
190 WTF
= Framework("WTF")
191 JavaScriptCore
= Framework("JavaScriptCore")
192 WebCore
= Framework("WebCore")
193 Test
= Framework("Test")
197 def __init__(self
, settings
):
198 self
._settings
= settings
200 def setting(self
, key
, default
=''):
201 return self
._settings
.get(key
, default
)
204 def fromString(queueString
):
205 if queueString
== "SCRIPT_MEMOIZED":
206 return InputQueues
.SCRIPT_MEMOIZED
208 if queueString
== "LOADER_MEMOIZED":
209 return InputQueues
.LOADER_MEMOIZED
211 if queueString
== "EVENT_LOOP":
212 return InputQueues
.EVENT_LOOP
214 raise ParseException("Unknown input queue: " + queueString
)
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"])
224 def __init__(self
, name
, description
, queueString
, flags
, guard
=None):
226 self
.description
= description
227 self
.queue
= InputQueue
.fromString(queueString
)
230 self
.members
= [] # names should be unique, but ordered.
232 def setting(self
, key
, default
=''):
233 if key
in self
._flags
:
236 return self
.queue
.setting(key
, default
)
240 def __init__(self
, memberName
, typeName
, flags
=[]):
241 self
.memberName
= memberName
242 self
.typeName
= typeName
245 def has_flag(self
, key
, default
=''):
246 return key
in self
._flags
250 def __init__(self
, name
):
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
267 raise ParseException("Unknown type mode: " + modeString
)
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")
284 def __init__(self
, name
, mode
, framework
, header
, enclosing_class
, values
, guard_values_map
, underlying_storage
, flags
, guard
=None):
287 self
.framework
= framework
289 self
.enclosing_class
= enclosing_class
291 self
.guard_values_map
= guard_values_map
292 self
.underlying_storage
= underlying_storage
296 def __eq__(self
, other
):
297 return self
.type_name() == other
.type_name() and self
.mode
== other
.mode
300 return self
._name
.__hash
__()
302 def has_flag(self
, flagString
):
303 return flagString
in self
._flags
306 return self
.has_flag("STRUCT")
309 return self
.has_flag("ENUM")
311 def is_enum_class(self
):
312 return self
.has_flag("ENUM_CLASS")
314 def declaration_kind(self
):
317 elif self
.is_enum_class():
319 elif self
.is_struct():
324 def qualified_prefix(self
):
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
)
333 def type_name(self
, qualified
=False):
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
)
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
)
347 return self
.type_name(qualified
)
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
)
355 return "const %s&" % self
.type_name(qualified
)
357 def argument_type(self
, qualified
=False):
358 if self
.mode
== TypeModes
.SHARED
:
359 return "PassRefPtr<%s>" % self
.type_name(qualified
)
361 return self
.storage_type()
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
.type_name(qualified
=qualified
)
395 def argument_type(self
, qualified
=False):
396 return self
.type_name(qualified
=qualified
) + "&"
400 def __init__(self
, parsed_json
):
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
= {}
409 self
.parse_toplevel(parsed_json
)
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())
415 def get_type_for_member(self
, member
):
416 if member
.has_flag("VECTOR"):
417 return VectorType(self
.types_by_name
.get(member
.typeName
))
419 return self
.types_by_name
.get(member
.typeName
)
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")
426 if not isinstance(json
['inputs'], list):
427 raise ParseException("Malformed specification: inputs is not an array")
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
)
433 for _type
in type_list
:
434 self
.parse_type_with_framework_name(_type
, type_framework_name
)
436 for val
in json
['inputs']:
437 self
.parse_input(val
)
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')
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
)
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
)
463 self
.types
.append(_type
)
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', [])))
473 self
.inputs
.append(_input
)
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
)
480 for _input
in self
.inputs
:
481 self
.typecheck_input(_input
)
483 def typecheck_type(self
, _type
):
484 log
.debug("typecheck type " + _type
.type_name())
486 if _type
.type_name() in self
.types_by_name
:
487 raise TypecheckException("Duplicate type with name: " + _type
.type_name())
489 self
.types_by_name
[_type
.type_name()] = _type
491 def typecheck_input(self
, _input
):
492 log
.debug("typecheck input " + _input
.name
)
494 if _input
.name
in self
.inputs_by_name
:
495 raise TypecheckException("Duplicate input with name: " + _input
.name
)
499 for member
in _input
.members
:
500 if member
.memberName
in seen_members
:
501 raise TypecheckException("Duplicate input member with name: " + member
.memberName
)
503 self
.typecheck_input_member(member
, _input
)
504 seen_members
[member
.memberName
] = member
506 self
.inputs_by_name
[_input
.name
] = _input
508 def typecheck_input_member(self
, input_member
, _input
):
509 log
.debug("typecheck member '%s' of '%s'" % (input_member
.memberName
, _input
.name
))
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
))
515 # A writer that only updates file if it actually changed.
516 class IncrementalFileWriter
:
517 def __init__(self
, filepath
, force_output
):
518 self
._filepath
= filepath
520 self
.force_output
= force_output
522 def write(self
, text
):
527 self
._output
= self
._output
.rstrip() + "\n"
530 read_file
= open(self
._filepath
, "r")
531 old_text
= read_file
.read()
533 text_changed
= old_text
!= self
._output
535 # Ignore, just overwrite by default
538 if text_changed
or self
.force_output
:
539 out_file
= open(self
._filepath
, "w")
540 out_file
.write(self
._output
)
544 def wrap_with_guard(contents
, condition
=None):
545 if condition
is None:
549 "#if %s" % condition
,
551 "#endif // %s" % condition
556 def __init__(self
, model
, target_framework_name
, input_filepath
, output_prefix
):
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
563 def setting(self
, key
, default
=''):
564 return self
.target_framework
.setting(key
, GLOBAL_CONFIG
.get(key
, default
))
566 # This does not account for any filename mangling performed on behalf of the test harness.
567 def output_filename(self
, extension
=None):
569 if len(self
._output
_prefix
) > 0:
570 components
.extend([self
._output
_prefix
, '-'])
572 components
.extend([self
.setting('prefix'), self
.setting('baseFilename')])
574 if extension
is not None:
575 components
.extend(['.', extension
])
577 return "".join(components
)
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
)
583 header_file
.write(self
.generate_header())
584 implementation_file
.write(self
.generate_implementation())
587 implementation_file
.close()
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(),
606 return Template(Templates
.HeaderSkeleton
).substitute(template_arguments
)
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()]),
621 return Template(Templates
.ImplementationSkeleton
).substitute(template_arguments
)
623 def generate_license(self
):
624 return Template(Templates
.CopyrightBlock
).substitute(None, inputFilename
=os
.path
.basename(self
._input
_filepath
))
626 def generate_includes(self
, defaults
=[], includes_for_types
=False):
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
:
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
):
644 if self
.target_framework
!= _type
.framework
:
645 lines
.add("#include <%s>" % _type
.header
)
647 lines
.add("#include \"%s\"" % os
.path
.basename(_type
.header
))
649 for entry
in defaults
:
650 (allowed_framework_names
, data
) = entry
651 (framework_name
, header_path
) = data
653 if self
.target_framework
.name
not in allowed_framework_names
:
655 if self
.target_framework
.name
!= framework_name
:
656 lines
.add("#include <%s>" % header_path
)
658 lines
.add("#include \"%s\"" % os
.path
.basename(header_path
))
660 return "\n".join(sorted(list(lines
)))
662 def generate_type_forward_declarations(self
):
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
] = []
670 for _type
in self
._model
.types
:
671 if _type
.framework
not in frameworks
:
673 if _type
.enclosing_class
is not None:
675 if _type
.mode
== TypeModes
.HEAVY_SCALAR
:
677 if _type
.mode
== TypeModes
.SCALAR
and not (_type
.is_enum() or _type
.is_enum_class()):
680 declaration
= "enum %s : %s;" % (_type
.type_name(), _type
.underlying_storage
)
682 declaration
= "%s %s;" % (_type
.declaration_kind(), _type
.type_name())
683 decls_by_framework
[_type
.framework
].append(declaration
)
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:
690 decls_by_framework
[framework
].sort()
691 lines
.append("namespace %s {" % framework
.setting('namespace'))
692 lines
.extend(decls_by_framework
[framework
])
696 return "\n".join(lines
)
698 def generate_class_declaration(self
, _input
):
699 extra_declarations
= []
700 if _input
.queue
== InputQueues
.EVENT_LOOP
:
701 extra_declarations
.extend([
703 " // EventLoopInput API",
704 " virtual void dispatch(ReplayController&) override final;",
707 if _input
.setting('CREATE_FROM_PAGE'):
708 extra_declarations
.extend([
709 " static std::unique_ptr<%s> createFromPage(const Page&);" % _input
.name
712 member_getters
= [self
.generate_input_member_getter(_member
) for _member
in _input
.members
]
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:")
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
),
729 return wrap_with_guard(Template(Templates
.InputClassDeclaration
).substitute(template_arguments
), _input
.guard
)
731 def generate_input_constructor_declaration(self
, _input
):
732 formals_list
= self
.generate_constructor_formals_list(_input
)
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
)
739 def generate_input_destructor_declaration(self
, _input
):
740 return " virtual ~%s();" % _input
.name
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
))
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
)
750 def generate_input_member_tuples(self
, _input
):
751 return [(_member
, self
._model
.get_type_for_member(_member
)) for _member
in _input
.members
]
753 def qualified_input_name(self
, _input
):
754 if self
.target_framework
== self
.traits_framework
:
757 return "%s::%s" % (self
.target_framework
.setting('namespace'), _input
.name
)
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'))
764 template_arguments
= {
765 'structOrClass': " ".join(decl_type
),
766 'queueType': _input
.queue
.setting('enumValue'),
767 'qualifiedInputName': self
.qualified_input_name(_input
),
770 return wrap_with_guard(Template(Templates
.InputTraitsDeclaration
).substitute(template_arguments
), _input
.guard
)
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
),
778 return Template(template
).substitute(template_arguments
)
780 def generate_for_each_macro(self
):
781 macro_name
= "%s_REPLAY_INPUT_NAMES_FOR_EACH" % self
.setting('prefix').upper()
783 lines
.append("#define %s(macro) \\" % macro_name
)
784 lines
.extend([" macro(%s) \\" % _input
.name
for _input
in self
._model
.inputs
])
786 lines
.append("// end of %s" % macro_name
)
787 return "\n".join(lines
)
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
),
797 return wrap_with_guard(Template(Templates
.InputClassImplementation
).substitute(template_arguments
), _input
.guard
)
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
)
813 encode_template
= Templates
.EnumEncodeCase
814 decode_template
= Templates
.EnumDecodeCase
815 enum_trait_template
= Templates
.EnumTraitImplementation
817 encode_template
= Templates
.EnumClassEncodeCase
818 decode_template
= Templates
.EnumClassDecodeCase
819 enum_trait_template
= Templates
.EnumClassTraitImplementation
821 # Generate body for encode.
822 for _value
in _type
.values
:
823 template_arguments
= {
824 'enumStringValue': _value
,
825 'qualifiedEnumValue': "%s%s" % (enum_prefix
, _value
),
827 encodeLines
.append(Template(encode_template
).substitute(template_arguments
))
829 for guard
, guard_values
in _type
.guard_values_map
.iteritems():
831 for guard_value
in guard_values
:
832 template_arguments
= {
833 'enumStringValue': guard_value
,
834 'qualifiedEnumValue': "%s%s" % (enum_prefix
, guard_value
),
836 guardedLines
.append(Template(encode_template
).substitute(template_arguments
))
837 encodeLines
.append(wrap_with_guard("\n".join(guardedLines
), guard
))
839 # Generate body for decode.
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
)
847 decodeLines
.append(Template(decode_template
).substitute(template_arguments
))
849 for guard
, guard_values
in _type
.guard_values_map
.iteritems():
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
)
857 guardedLines
.append(Template(decode_template
).substitute(template_arguments
))
858 decodeLines
.append(wrap_with_guard("\n".join(guardedLines
), guard
))
860 template_arguments
= {
861 'enumName': _type
.type_name(qualified
=should_qualify_type
),
862 'encodeCases': "\n".join(encodeLines
),
863 'decodeCases': "\n".join(decodeLines
)
866 return Template(enum_trait_template
).substitute(template_arguments
)
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
),
878 return wrap_with_guard(Template(Templates
.InputTraitsImplementation
).substitute(template_arguments
), _input
.guard
)
880 def generate_input_encode_implementation(self
, _input
):
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
)
887 " encodedValue.%s(ASCIILiteral(\"%s\"), input.%s());" % (put_method
, _member
.memberName
, _member
.memberName
)
892 " UNUSED_PARAM(encodedValue);",
893 " UNUSED_PARAM(input);",
896 return "\n".join(steps
)
898 def generate_input_decode_implementation(self
, _input
):
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
)
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
),
911 steps
.append("\n".join(lines
))
915 " UNUSED_PARAM(encodedValue);",
918 return "\n".join(steps
)
920 def generate_constructor_initializer_list(self
, _input
):
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
)))
926 return "\n".join(initializers
)
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
])
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
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
945 return _member
.memberName
947 def generate_constructor_arguments_list(self
, _input
):
948 return ", ".join([self
.generate_member_move_expression(_member
) for _member
in _input
.members
])
951 def generate_from_specification(input_filepath
=None, output_prefix
="", output_dirpath
=None, framework_name
=None, force_output
=False):
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
)
958 if not framework_name
in FRAMEWORK_CONFIG_MAP
:
959 raise ParseException("Unknown or unsupported framework name supplied: " + framework_name
)
961 model
= InputsModel(parsed_json
)
962 model
.resolve_types()
963 generator
= Generator(model
, framework_name
, input_filepath
, output_prefix
)
965 generator
.write_output_files(output_dirpath
, force_output
)
968 if __name__
== '__main__':
969 allowed_framework_names
= FRAMEWORK_CONFIG_MAP
.keys()
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.")
980 arg_options
, arg_values
= cli_parser
.parse_args()
981 if (len(arg_values
) < 1):
982 raise ParseException("At least one plain argument expected")
984 if not arg_options
.outputDir
:
985 raise ParseException("Missing output directory")
987 if arg_options
.debug
:
988 log
.setLevel(logging
.DEBUG
)
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
999 generate_from_specification(**options
)
1000 except (ParseException
, TypecheckException
) as e
:
1001 if arg_options
.test
:
1002 log
.error(e
.message
)
1004 raise e
# Force the build to fail.