]> git.saurik.com Git - apple/javascriptcore.git/blame - replay/scripts/CodeGeneratorReplayInputs.py
JavaScriptCore-7601.1.46.3.tar.gz
[apple/javascriptcore.git] / replay / scripts / CodeGeneratorReplayInputs.py
CommitLineData
81345200
A
1#!/usr/bin/env python
2# Copyright (c) 2014 Apple Inc. All rights reserved.
3#
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions are
6# met:
7#
8# * Redistributions of source code must retain the above copyright
9# notice, this list of conditions and the following disclaimer.
10# * Redistributions in binary form must reproduce the above
11# copyright notice, this list of conditions and the following disclaimer
12# in the documentation and/or other materials provided with the
13# distribution.
14#
15# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27import os.path
28import re
29import sys
30import string
31from string import Template
32import optparse
33import logging
34from CodeGeneratorReplayInputsTemplates import Templates
35
36try:
37 import json
38except ImportError:
39 import simplejson as json
40
41# Configuration values are first looked up in the framework configuration,
42# and then in the global configuration if there is no framework-specific value.
43GLOBAL_CONFIG = {
44 "baseFilename": "ReplayInputs",
45 "guardCondition": "ENABLE(WEB_REPLAY)",
46 "traitsFrameworkName": "JavaScriptCore",
47
48 # These are formatted as ([allowed_frameworks], (framework, header_path)).
49 # The generator can figure out how to format the includes.
50 "headerIncludes": [
51 (["WebCore"],
52 ("WebCore", "replay/EventLoopInput.h")
53 ),
54 (["JavaScriptCore", "WebCore"],
55 ("JavaScriptCore", "replay/EncodedValue.h")
56 ),
57 (["JavaScriptCore"],
58 ("JavaScriptCore", "replay/NondeterministicInput.h")
59 ),
ed1e77d3
A
60 (["JavaScriptCore", "WebCore"],
61 ("WTF", "wtf/TypeCasts.h")
62 ),
81345200
A
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": [
81345200
A
77 (["WebCore"],
78 ("WebCore", "replay/SerializationMethods.h")
79 ),
80 (["WebCore", "JavaScriptCore"],
81 ("JavaScriptCore", "inspector/InspectorValues.h")
82 ),
ed1e77d3 83 (["WebCore", "JavaScriptCore"],
81345200
A
84 ("WTF", "wtf/NeverDestroyed.h")
85 ),
81345200
A
86
87 # Testing fixtures.
88 (["Test"],
89 ("WebCore", "platform/ExternalNamespaceImplIncludeDummy.h")
90 ),
91 (["Test"],
92 ("Test", "platform/InternalNamespaceImplIncludeDummy.h")
93 )
94 ],
95}
96
97FRAMEWORK_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",
81345200
A
111 },
112 "WebCore": {
113 "prefix": "Web",
114 "namespace": "WebCore",
ed1e77d3 115 "exportMacro": "WEBCORE_EXPORT"
81345200
A
116 },
117 # Used for bindings tests.
118 "Test": {
119 "prefix": "Test",
120 "namespace": "Test",
ed1e77d3 121 "exportMacro": "TEST_EXPORT_MACRO"
81345200
A
122 }
123}
124
125# These settings are specific to an input queue.
126QUEUE_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.
143logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.ERROR)
144log = logging.getLogger('global')
145
146
147# Model classes, which transliterate JSON input.
148class ParseException(Exception):
149 pass
150
151
152class TypecheckException(Exception):
153 pass
154
155
156class 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
184class Frameworks:
185 Global = Framework("Global")
186 WTF = Framework("WTF")
187 JavaScriptCore = Framework("JavaScriptCore")
188 WebCore = Framework("WebCore")
189 Test = Framework("Test")
190
191
192class 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
213class 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
219class Input:
ed1e77d3 220 def __init__(self, name, description, framework, queueString, flags, guard=None):
81345200
A
221 self.name = name
222 self.description = description
ed1e77d3 223 self.framework = framework
81345200
A
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
236class 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
246class 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
267class 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
280class 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:
ed1e77d3 350 return "RefPtr<%s>" % self.type_name(qualified)
81345200
A
351 else:
352 return "const %s&" % self.type_name(qualified)
353
354 def argument_type(self, qualified=False):
355 if self.mode == TypeModes.SHARED:
ed1e77d3 356 return "RefPtr<%s>&&" % self.type_name(qualified)
81345200
A
357 else:
358 return self.storage_type()
359
ed1e77d3
A
360 def encoding_type_argument(self, qualified=False):
361 return self.type_name(qualified=qualified)
362
81345200
A
363
364def check_for_required_properties(props, obj, what):
365 for prop in props:
366 if prop not in obj:
367 raise ParseException("When parsing %s, required property missing: %s" % (what, prop))
368
369
370class VectorType(Type):
371 def __init__(self, element_type):
372 self._element_type = element_type
373 self.mode = TypeModes.VECTOR
374 self.framework = element_type.framework
375 self.enclosing_class = None
376
377 def has_flag(self):
378 return False
379
380 def is_struct(self):
381 return False
382
383 def is_enum(self):
384 return False
385
386 def is_enum_class(self):
387 return False
388
389 def qualified_prefix(self):
390 return ""
391
392 def type_name(self, qualified=False):
ed1e77d3
A
393 return "Vector<%s>" % self._element_type.storage_type(qualified=qualified)
394
395 def encoding_type_argument(self, qualified=False):
81345200
A
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
402class InputsModel:
ed1e77d3 403 def __init__(self):
81345200
A
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
81345200
A
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
ed1e77d3
A
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")
81345200 426
ed1e77d3
A
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)
81345200 430
ed1e77d3
A
431 framework = Framework.fromString(framework_name)
432 for _type in type_list:
433 self.parse_type_with_framework(_type, framework)
81345200 434
ed1e77d3
A
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")
81345200 438
ed1e77d3
A
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)
81345200 442
ed1e77d3
A
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):
81345200 448 check_for_required_properties(['name', 'mode'], json, 'type')
81345200
A
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
ed1e77d3
A
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)
81345200
A
469
470 self.types.append(_type)
471
ed1e77d3 472 def parse_input_with_framework(self, json, framework):
81345200 473 check_for_required_properties(['name', 'description', 'queue', 'members'], json, 'input')
ed1e77d3 474 _input = Input(json['name'], json['description'], framework, json['queue'], json.get('flags', []), json.get('guard'))
81345200
A
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.
523class 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
551def 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
562class 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
ed1e77d3
A
573 def should_generate_item(self, item):
574 return item.framework is self.target_framework
575
81345200
A
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):
ed1e77d3
A
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
81345200
A
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(),
ed1e77d3
A
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]),
81345200
A
617 'forEachMacro': self.generate_for_each_macro(),
618 }
619
620 return Template(Templates.HeaderSkeleton).substitute(template_arguments)
621
622 def generate_implementation(self):
ed1e77d3
A
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
81345200
A
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),
ed1e77d3
A
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]),
81345200
A
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.
ed1e77d3 655 include_for_enclosing_class = _type.enclosing_class is not None
81345200
A
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
ed1e77d3
A
770 def qualified_input_name(self, _input, forceQualified=False):
771 if forceQualified or self.target_framework != self.traits_framework:
81345200 772 return "%s::%s" % (self.target_framework.setting('namespace'), _input.name)
ed1e77d3
A
773 else:
774 return _input.name
81345200
A
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
ed1e77d3
A
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
81345200 796 def generate_enum_trait_declaration(self, _type):
ed1e77d3
A
797 decl_type = ['struct']
798 if len(self.setting('exportMacro')) > 0:
799 decl_type.append(self.setting('exportMacro'))
800
81345200
A
801 should_qualify_type = _type.framework != self.traits_framework
802 template = Templates.EnumTraitDeclaration if _type.is_enum() else Templates.EnumClassTraitDeclaration
803 template_arguments = {
ed1e77d3
A
804 'encodingTypeArgument': _type.encoding_type_argument(qualified=should_qualify_type),
805 'enumType': _type.type_name(qualified=should_qualify_type),
806 'structOrClass': " ".join(decl_type)
81345200
A
807 }
808 return Template(template).substitute(template_arguments)
809
810 def generate_for_each_macro(self):
ed1e77d3
A
811 inputs_to_generate = filter(self.should_generate_item, self._model.inputs)
812
81345200
A
813 macro_name = "%s_REPLAY_INPUT_NAMES_FOR_EACH" % self.setting('prefix').upper()
814 lines = []
815 lines.append("#define %s(macro) \\" % macro_name)
ed1e77d3 816 lines.extend([" macro(%s) \\" % _input.name for _input in inputs_to_generate])
81345200
A
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'))
ed1e77d3 836 if _type.is_enum() and _type.enclosing_class is not None:
81345200 837 prefix_components.append(_type.enclosing_class)
ed1e77d3
A
838 elif _type.is_enum_class():
839 prefix_components.append(_type.type_name(qualified=False))
81345200
A
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 = []
ed1e77d3
A
873 for i, _value in enumerate(_type.values):
874
81345200 875 template_arguments = {
ed1e77d3 876 'branchKeyword': "else if" if i > 0 else "if",
81345200
A
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 = []
ed1e77d3 885 for i, guard_value in enumerate(guard_values):
81345200 886 template_arguments = {
ed1e77d3 887 'branchKeyword': "else if" if i > 0 else "if",
81345200
A
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 = {
ed1e77d3
A
896 'encodingTypeArgument': _type.encoding_type_argument(qualified=should_qualify_type),
897 'enumType': _type.type_name(qualified=should_qualify_type),
81345200
A
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'),
ed1e77d3 907 'inputNameStringLiteral': '"%s"' % _input.name,
81345200
A
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
ed1e77d3 920 put_method = "put<%s>" % _type.encoding_type_argument(qualified=should_qualify_type)
81345200
A
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
ed1e77d3 938 get_method = "get<%s>" % _type.encoding_type_argument(qualified=should_qualify_type)
81345200
A
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)
ed1e77d3 978 if _type.mode in [TypeModes.OWNED, TypeModes.SHARED]:
81345200
A
979 return "WTF::move(%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
ed1e77d3 987def generate_from_specifications(input_filepaths=[], output_prefix="", output_dirpath=None, framework_name=None, force_output=False):
81345200
A
988
989 if not framework_name in FRAMEWORK_CONFIG_MAP:
990 raise ParseException("Unknown or unsupported framework name supplied: " + framework_name)
991
ed1e77d3
A
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
81345200 1008 model.resolve_types()
ed1e77d3 1009 generator = Generator(model, framework_name, input_filepaths[0], output_prefix)
81345200
A
1010
1011 generator.write_output_files(output_dirpath, force_output)
1012
1013
1014if __name__ == '__main__':
1015 allowed_framework_names = FRAMEWORK_CONFIG_MAP.keys()
1016
ed1e77d3 1017 cli_parser = optparse.OptionParser(usage="usage: %prog [options] <Inputs.json> [, <MoreInputs.json> ]")
81345200
A
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()
81345200
A
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 = {
ed1e77d3 1035 'input_filepaths': arg_values,
81345200
A
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:
ed1e77d3 1043 generate_from_specifications(**options)
81345200
A
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.