]> git.saurik.com Git - apple/javascriptcore.git/blob - replay/scripts/CodeGeneratorReplayInputs.py
JavaScriptCore-7600.1.4.13.1.tar.gz
[apple/javascriptcore.git] / replay / scripts / CodeGeneratorReplayInputs.py
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
27 import os.path
28 import re
29 import sys
30 import string
31 from string import Template
32 import optparse
33 import logging
34 from CodeGeneratorReplayInputsTemplates import Templates
35
36 try:
37 import json
38 except 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.
43 GLOBAL_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
100 FRAMEWORK_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.
130 QUEUE_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.
147 logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.ERROR)
148 log = logging.getLogger('global')
149
150
151 # Model classes, which transliterate JSON input.
152 class ParseException(Exception):
153 pass
154
155
156 class TypecheckException(Exception):
157 pass
158
159
160 class 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
188 class Frameworks:
189 Global = Framework("Global")
190 WTF = Framework("WTF")
191 JavaScriptCore = Framework("JavaScriptCore")
192 WebCore = Framework("WebCore")
193 Test = Framework("Test")
194
195
196 class 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
217 class 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
223 class 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
239 class 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
249 class 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
270 class 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
283 class 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
364 def 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
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
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
399 class 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.
516 class 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
544 def 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
555 class 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
951 def 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
968 if __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.