Replace WTF::move with WTFMove
[WebKit-https.git] / Source / JavaScriptCore / 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         (["JavaScriptCore", "WebCore"],
61             ("WTF", "wtf/TypeCasts.h")
62         ),
63         (["WebCore"],
64             ("WTF", "wtf/text/WTFString.h")
65         ),
66
67         # Testing fixtures.
68         (["Test"],
69             ("WebCore", "platform/ExternalNamespaceHeaderIncludeDummy.h")
70         ),
71         (["Test"],
72             ("Test", "platform/InternalNamespaceHeaderIncludeDummy.h")
73         )
74     ],
75
76     "implIncludes": [
77         (["WebCore"],
78             ("WebCore", "replay/SerializationMethods.h")
79         ),
80         (["WebCore", "JavaScriptCore"],
81             ("JavaScriptCore", "inspector/InspectorValues.h")
82         ),
83         (["WebCore", "JavaScriptCore"],
84             ("WTF", "wtf/NeverDestroyed.h")
85         ),
86
87         # Testing fixtures.
88         (["Test"],
89             ("WebCore", "platform/ExternalNamespaceImplIncludeDummy.h")
90         ),
91         (["Test"],
92             ("Test", "platform/InternalNamespaceImplIncludeDummy.h")
93         )
94     ],
95 }
96
97 FRAMEWORK_CONFIG_MAP = {
98     "Global": {
99         "prefix": "",
100         "namespace": ""
101     },
102
103     "WTF": {
104         "prefix": "WTF",
105         "namespace": "WTF",
106     },
107     "JavaScriptCore": {
108         "prefix": "JS",
109         "namespace": "JSC",
110         "exportMacro": "JS_EXPORT_PRIVATE",
111     },
112     "WebCore": {
113         "prefix": "Web",
114         "namespace": "WebCore",
115         "exportMacro": "WEBCORE_EXPORT"
116     },
117     # Used for bindings tests.
118     "Test": {
119         "prefix": "Test",
120         "namespace": "Test",
121         "exportMacro": "TEST_EXPORT_MACRO"
122     }
123 }
124
125 # These settings are specific to an input queue.
126 QUEUE_CONFIG_MAP = {
127     "SCRIPT_MEMOIZED": {
128         "enumValue": "ScriptMemoizedData",
129         "baseClass": "NondeterministicInput<%s>",
130     },
131     "LOADER_MEMOIZED": {
132         "enumValue": "LoaderMemoizedData",
133         "baseClass": "NondeterministicInput<%s>",
134     },
135     "EVENT_LOOP": {
136         "enumValue": "EventLoopInput",
137         "baseClass": "EventLoopInput<%s>",
138     },
139 }
140
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')
145
146
147 # Model classes, which transliterate JSON input.
148 class ParseException(Exception):
149     pass
150
151
152 class TypecheckException(Exception):
153     pass
154
155
156 class Framework:
157     def __init__(self, name):
158         self._settings = FRAMEWORK_CONFIG_MAP[name]
159         self.name = name
160
161     def setting(self, key, default=''):
162         return self._settings.get(key, default)
163
164     @staticmethod
165     def fromString(frameworkString):
166         if frameworkString == "Global":
167             return Frameworks.Global
168
169         if frameworkString == "WTF":
170             return Frameworks.WTF
171
172         if frameworkString == "JavaScriptCore":
173             return Frameworks.JavaScriptCore
174
175         if frameworkString == "WebCore":
176             return Frameworks.WebCore
177
178         if frameworkString == "Test":
179             return Frameworks.Test
180
181         raise ParseException("Unknown framework: " + frameworkString)
182
183
184 class Frameworks:
185     Global = Framework("Global")
186     WTF = Framework("WTF")
187     JavaScriptCore = Framework("JavaScriptCore")
188     WebCore = Framework("WebCore")
189     Test = Framework("Test")
190
191
192 class InputQueue:
193     def __init__(self, settings):
194         self._settings = settings
195
196     def setting(self, key, default=''):
197         return self._settings.get(key, default)
198
199     @staticmethod
200     def fromString(queueString):
201         if queueString == "SCRIPT_MEMOIZED":
202             return InputQueues.SCRIPT_MEMOIZED
203
204         if queueString == "LOADER_MEMOIZED":
205             return InputQueues.LOADER_MEMOIZED
206
207         if queueString == "EVENT_LOOP":
208             return InputQueues.EVENT_LOOP
209
210         raise ParseException("Unknown input queue: " + queueString)
211
212
213 class InputQueues:
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"])
217
218
219 class Input:
220     def __init__(self, name, description, framework, queueString, flags, guard=None):
221         self.name = name
222         self.description = description
223         self.framework = framework
224         self.queue = InputQueue.fromString(queueString)
225         self._flags = flags
226         self.guard = guard
227         self.members = []  # names should be unique, but ordered.
228
229     def setting(self, key, default=''):
230         if key in self._flags:
231             return True
232
233         return self.queue.setting(key, default)
234
235
236 class InputMember:
237     def __init__(self, memberName, typeName, flags=[]):
238         self.memberName = memberName
239         self.typeName = typeName
240         self._flags = flags
241
242     def has_flag(self, key, default=''):
243         return key in self._flags
244
245
246 class TypeMode:
247     def __init__(self, name):
248         self._name = name
249
250     @staticmethod
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
263
264         raise ParseException("Unknown type mode: " + modeString)
265
266
267 class TypeModes:
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")
278
279
280 class Type:
281     def __init__(self, name, mode, framework, header, enclosing_class, values, guard_values_map, underlying_storage, flags, guard=None):
282         self._name = name
283         self.mode = mode
284         self.framework = framework
285         self.header = header
286         self.enclosing_class = enclosing_class
287         self.values = values
288         self.guard_values_map = guard_values_map
289         self.underlying_storage = underlying_storage
290         self._flags = flags
291         self.guard = guard
292
293     def __eq__(self, other):
294         return self.type_name() == other.type_name() and self.mode == other.mode
295
296     def __hash__(self):
297         return self._name.__hash__()
298
299     def has_flag(self, flagString):
300         return flagString in self._flags
301
302     def is_struct(self):
303         return self.has_flag("STRUCT")
304
305     def is_enum(self):
306         return self.has_flag("ENUM")
307
308     def is_enum_class(self):
309         return self.has_flag("ENUM_CLASS")
310
311     def declaration_kind(self):
312         if self.is_enum():
313             return "enum"
314         elif self.is_enum_class():
315             return "enum class"
316         elif self.is_struct():
317             return "struct"
318         else:
319             return "class"
320
321     def qualified_prefix(self):
322         components = []
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)
329
330     def type_name(self, qualified=False):
331         if qualified:
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)
335         else:
336             return self._name
337
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)
343         else:
344             return self.type_name(qualified)
345
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)
351         else:
352             return "const %s&" % self.type_name(qualified)
353
354     def argument_type(self, qualified=False):
355         if self.mode == TypeModes.SHARED:
356             return "RefPtr<%s>&&" % self.type_name(qualified)
357         else:
358             return self.storage_type()
359
360     def encoding_type_argument(self, qualified=False):
361         return self.type_name(qualified=qualified)
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.storage_type(qualified=qualified)
394
395     def encoding_type_argument(self, qualified=False):
396         return "Vector<%s>" % self._element_type.type_name(qualified=qualified)
397
398     def argument_type(self, qualified=False):
399         return self.type_name(qualified=qualified) + "&"
400
401
402 class InputsModel:
403     def __init__(self):
404         self.inputs = []
405         self.types = []
406
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 = {}
411
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())
415
416     def get_type_for_member(self, member):
417         if member.has_flag("VECTOR"):
418             return VectorType(self.types_by_name.get(member.typeName))
419         else:
420             return self.types_by_name.get(member.typeName)
421
422     def parse_specification(self, json):
423         if 'types' in json:
424             if not isinstance(json['types'], dict):
425                 raise ParseException("Malformed specification: types is not a dict of framework->type list")
426
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)
430
431                 framework = Framework.fromString(framework_name)
432                 for _type in type_list:
433                     self.parse_type_with_framework(_type, framework)
434
435         if 'inputs' in json:
436             if not isinstance(json['inputs'], dict):
437                 raise ParseException("Malformed specification: inputs is not a dict of framework->input list")
438
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)
442
443                 framework = Framework.fromString(framework_name)
444                 for _input in input_list:
445                     self.parse_input_with_framework(_input, framework)
446
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')
451
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)
466
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)
469
470         self.types.append(_type)
471
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', [])))
479
480         self.inputs.append(_input)
481
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)
486
487         for _input in self.inputs:
488             self.typecheck_input(_input)
489
490     def typecheck_type(self, _type):
491         log.debug("typecheck type " + _type.type_name())
492
493         if _type.type_name() in self.types_by_name:
494             raise TypecheckException("Duplicate type with name: " + _type.type_name())
495
496         self.types_by_name[_type.type_name()] = _type
497
498     def typecheck_input(self, _input):
499         log.debug("typecheck input " + _input.name)
500
501         if _input.name in self.inputs_by_name:
502             raise TypecheckException("Duplicate input with name: " + _input.name)
503
504         seen_members = {}
505
506         for member in _input.members:
507             if member.memberName in seen_members:
508                 raise TypecheckException("Duplicate input member with name: " + member.memberName)
509
510             self.typecheck_input_member(member, _input)
511             seen_members[member.memberName] = member
512
513         self.inputs_by_name[_input.name] = _input
514
515     def typecheck_input_member(self, input_member, _input):
516         log.debug("typecheck member '%s' of '%s'" % (input_member.memberName, _input.name))
517
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))
520
521
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
526         self._output = ""
527         self.force_output = force_output
528
529     def write(self, text):
530         self._output += text
531
532     def close(self):
533         text_changed = True
534         self._output = self._output.rstrip() + "\n"
535
536         try:
537             read_file = open(self._filepath, "r")
538             old_text = read_file.read()
539             read_file.close()
540             text_changed = old_text != self._output
541         except:
542             # Ignore, just overwrite by default
543             pass
544
545         if text_changed or self.force_output:
546             out_file = open(self._filepath, "w")
547             out_file.write(self._output)
548             out_file.close()
549
550
551 def wrap_with_guard(contents, condition=None):
552     if condition is None:
553         return contents
554
555     return "\n".join([
556         "#if %s" % condition,
557         contents,
558         "#endif // %s" % condition
559     ])
560
561
562 class Generator:
563     def __init__(self, model, target_framework_name, input_filepath, output_prefix):
564         self._model = model
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
569
570     def setting(self, key, default=''):
571         return self.target_framework.setting(key, GLOBAL_CONFIG.get(key, default))
572
573     def should_generate_item(self, item):
574         return item.framework is self.target_framework
575
576     # This does not account for any filename mangling performed on behalf of the test harness.
577     def output_filename(self, extension=None):
578         components = []
579         if len(self._output_prefix) > 0:
580             components.extend([self._output_prefix, '-'])
581
582         components.extend([self.setting('prefix'), self.setting('baseFilename')])
583
584         if extension is not None:
585             components.extend(['.', extension])
586
587         return "".join(components)
588
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)
592
593         header_file.write(self.generate_header())
594         implementation_file.write(self.generate_implementation())
595
596         header_file.close()
597         implementation_file.close()
598
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)
602
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(),
618         }
619
620         return Template(Templates.HeaderSkeleton).substitute(template_arguments)
621
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)
625
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]),
636         }
637
638         return Template(Templates.ImplementationSkeleton).substitute(template_arguments)
639
640     def generate_license(self):
641         return Template(Templates.CopyrightBlock).substitute(None, inputFilename=os.path.basename(self._input_filepath))
642
643     def generate_includes(self, defaults=[], includes_for_types=False):
644         lines = set()
645
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:
649                 continue
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):
659                 continue
660
661             if self.target_framework != _type.framework:
662                 lines.add("#include <%s>" % _type.header)
663             else:
664                 lines.add("#include \"%s\"" % os.path.basename(_type.header))
665
666         for entry in defaults:
667             (allowed_framework_names, data) = entry
668             (framework_name, header_path) = data
669
670             if self.target_framework.name not in allowed_framework_names:
671                 continue
672             if self.target_framework.name != framework_name:
673                 lines.add("#include <%s>" % header_path)
674             else:
675                 lines.add("#include \"%s\"" % os.path.basename(header_path))
676
677         return "\n".join(sorted(list(lines)))
678
679     def generate_type_forward_declarations(self):
680         lines = []
681
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] = []
686
687         for _type in self._model.types:
688             if _type.framework not in frameworks:
689                 continue
690             if _type.enclosing_class is not None:
691                 continue
692             if _type.mode == TypeModes.HEAVY_SCALAR:
693                 continue
694             if _type.mode == TypeModes.SCALAR and not (_type.is_enum() or _type.is_enum_class()):
695                 continue
696             if _type.is_enum():
697                 declaration = "enum %s : %s;" % (_type.type_name(), _type.underlying_storage)
698             else:
699                 declaration = "%s %s;" % (_type.declaration_kind(), _type.type_name())
700             decls_by_framework[_type.framework].append(declaration)
701
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:
705                 continue
706
707             decls_by_framework[framework].sort()
708             lines.append("namespace %s {" % framework.setting('namespace'))
709             lines.extend(decls_by_framework[framework])
710             lines.append("}")
711             lines.append("")
712
713         return "\n".join(lines)
714
715     def generate_class_declaration(self, _input):
716         extra_declarations = []
717         if _input.queue == InputQueues.EVENT_LOOP:
718             extra_declarations.extend([
719                 "",
720                 "    // EventLoopInput API",
721                 "    virtual void dispatch(ReplayController&) override final;",
722             ])
723
724             if _input.setting('CREATE_FROM_PAGE'):
725                 extra_declarations.extend([
726                     "    static std::unique_ptr<%s> createFromPage(const Page&);" % _input.name
727                 ])
728
729         member_getters = [self.generate_input_member_getter(_member) for _member in _input.members]
730
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:")
734
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),
744         }
745
746         return wrap_with_guard(Template(Templates.InputClassDeclaration).substitute(template_arguments), _input.guard)
747
748     def generate_input_constructor_declaration(self, _input):
749         formals_list = self.generate_constructor_formals_list(_input)
750         terms = []
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)
755
756     def generate_input_destructor_declaration(self, _input):
757         return "    virtual ~%s();" % _input.name
758
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))
762
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)
766
767     def generate_input_member_tuples(self, _input):
768         return [(_member, self._model.get_type_for_member(_member)) for _member in _input.members]
769
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)
773         else:
774             return _input.name
775
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'))
780
781         template_arguments = {
782             'structOrClass': " ".join(decl_type),
783             'queueType': _input.queue.setting('enumValue'),
784             'qualifiedInputName': self.qualified_input_name(_input),
785         }
786
787         return wrap_with_guard(Template(Templates.InputTraitsDeclaration).substitute(template_arguments), _input.guard)
788
789     def generate_input_type_trait_declaration(self, _input):
790         template_arguments = {
791             'qualifiedInputName': self.qualified_input_name(_input, forceQualified=True),
792         }
793
794         return wrap_with_guard(Template(Templates.InputTypeTraitsDeclaration).substitute(template_arguments), _input.guard)
795
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'))
800
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)
807         }
808         return Template(template).substitute(template_arguments)
809
810     def generate_for_each_macro(self):
811         inputs_to_generate = filter(self.should_generate_item, self._model.inputs)
812
813         macro_name = "%s_REPLAY_INPUT_NAMES_FOR_EACH" % self.setting('prefix').upper()
814         lines = []
815         lines.append("#define %s(macro) \\" % macro_name)
816         lines.extend(["    macro(%s) \\" % _input.name for _input in inputs_to_generate])
817         lines.append("    \\")
818         lines.append("// end of %s" % macro_name)
819         return "\n".join(lines)
820
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),
827         }
828
829         return wrap_with_guard(Template(Templates.InputClassImplementation).substitute(template_arguments), _input.guard)
830
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)
842         encodeLines = []
843
844         if _type.is_enum():
845             encode_template = Templates.EnumEncodeCase
846             decode_template = Templates.EnumDecodeCase
847             enum_trait_template = Templates.EnumTraitImplementation
848         else:
849             encode_template = Templates.EnumClassEncodeCase
850             decode_template = Templates.EnumClassDecodeCase
851             enum_trait_template = Templates.EnumClassTraitImplementation
852
853         # Generate body for encode.
854         for _value in _type.values:
855             template_arguments = {
856                 'enumStringValue': _value,
857                 'qualifiedEnumValue': "%s%s" % (enum_prefix, _value),
858             }
859             encodeLines.append(Template(encode_template).substitute(template_arguments))
860
861         for guard, guard_values in _type.guard_values_map.iteritems():
862             guardedLines = []
863             for guard_value in guard_values:
864                 template_arguments = {
865                     'enumStringValue': guard_value,
866                     'qualifiedEnumValue': "%s%s" % (enum_prefix, guard_value),
867                 }
868                 guardedLines.append(Template(encode_template).substitute(template_arguments))
869             encodeLines.append(wrap_with_guard("\n".join(guardedLines), guard))
870
871         # Generate body for decode.
872         decodeLines = []
873         for i, _value in enumerate(_type.values):
874
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)
880             }
881             decodeLines.append(Template(decode_template).substitute(template_arguments))
882
883         for guard, guard_values in _type.guard_values_map.iteritems():
884             guardedLines = []
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)
891                 }
892                 guardedLines.append(Template(decode_template).substitute(template_arguments))
893             decodeLines.append(wrap_with_guard("\n".join(guardedLines), guard))
894
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)
900         }
901
902         return Template(enum_trait_template).substitute(template_arguments)
903
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),
913         }
914         return wrap_with_guard(Template(Templates.InputTraitsImplementation).substitute(template_arguments), _input.guard)
915
916     def generate_input_encode_implementation(self, _input):
917         steps = []
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)
921
922             steps.extend([
923                 "    encodedValue.%s(ASCIILiteral(\"%s\"), input.%s());" % (put_method, _member.memberName, _member.memberName)
924             ])
925
926         if len(steps) == 0:
927             steps.extend([
928                 "    UNUSED_PARAM(encodedValue);",
929                 "    UNUSED_PARAM(input);",
930             ])
931
932         return "\n".join(steps)
933
934     def generate_input_decode_implementation(self, _input):
935         steps = []
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)
939
940             lines = [
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),
943                 "        return false;",
944                 ""
945             ]
946
947             steps.append("\n".join(lines))
948
949         if len(steps) == 0:
950             steps.extend([
951                 "    UNUSED_PARAM(encodedValue);",
952             ])
953
954         return "\n".join(steps)
955
956     def generate_constructor_initializer_list(self, _input):
957         initializers = []
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)))
961
962         return "\n".join(initializers)
963
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])
967
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
973
974         return expression
975
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 "WTFMove(%s)" % _member.memberName
980         else:
981             return _member.memberName
982
983     def generate_constructor_arguments_list(self, _input):
984         return ", ".join([self.generate_member_move_expression(_member) for _member in _input.members])
985
986
987 def generate_from_specifications(input_filepaths=[], output_prefix="", output_dirpath=None, framework_name=None, force_output=False):
988
989     if not framework_name in FRAMEWORK_CONFIG_MAP:
990         raise ParseException("Unknown or unsupported framework name supplied: " + framework_name)
991
992     if len(input_filepaths) == 0:
993         raise ParseException("Must provide at least one specification file, none were provided.")
994
995     def parse_json_from_file(input_filepath):
996         try:
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)
1001
1002     specifications = map(parse_json_from_file, input_filepaths)
1003
1004     model = InputsModel()
1005     for spec in specifications:
1006         model.parse_specification(spec)
1007
1008     model.resolve_types()
1009     generator = Generator(model, framework_name, input_filepaths[0], output_prefix)
1010
1011     generator.write_output_files(output_dirpath, force_output)
1012
1013
1014 if __name__ == '__main__':
1015     allowed_framework_names = FRAMEWORK_CONFIG_MAP.keys()
1016
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.")
1023
1024     options = None
1025
1026     arg_options, arg_values = cli_parser.parse_args()
1027
1028     if not arg_options.outputDir:
1029         raise ParseException("Missing output directory")
1030
1031     if arg_options.debug:
1032         log.setLevel(logging.DEBUG)
1033
1034     options = {
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
1040     }
1041
1042     try:
1043         generate_from_specifications(**options)
1044     except (ParseException, TypecheckException) as e:
1045         if arg_options.test:
1046             log.error(e.message)
1047         else:
1048             raise e  # Force the build to fail.