1#!/usr/bin/env python
2
3#
4# Copyright 2012 the V8 project authors. All rights reserved.
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions are
7# met:
8#
9#     * Redistributions of source code must retain the above copyright
10#       notice, this list of conditions and the following disclaimer.
11#     * Redistributions in binary form must reproduce the above
12#       copyright notice, this list of conditions and the following
13#       disclaimer in the documentation and/or other materials provided
14#       with the distribution.
15#     * Neither the name of Google Inc. nor the names of its
16#       contributors may be used to endorse or promote products derived
17#       from this software without specific prior written permission.
18#
19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30#
31
32#
33# Emits a C++ file to be compiled and linked into libv8 to support postmortem
34# debugging tools.  Most importantly, this tool emits constants describing V8
35# internals:
36#
37#    v8dbg_type_CLASS__TYPE = VALUE             Describes class type values
38#    v8dbg_class_CLASS__FIELD__TYPE = OFFSET    Describes class fields
39#    v8dbg_parent_CLASS__PARENT                 Describes class hierarchy
40#    v8dbg_frametype_NAME = VALUE               Describes stack frame values
41#    v8dbg_off_fp_NAME = OFFSET                 Frame pointer offsets
42#    v8dbg_prop_NAME = OFFSET                   Object property offsets
43#    v8dbg_NAME = VALUE                         Miscellaneous values
44#
45# These constants are declared as global integers so that they'll be present in
46# the generated libv8 binary.
47#
48
49import re
50import sys
51
52#
53# Miscellaneous constants such as tags and masks used for object identification,
54# enumeration values used as indexes in internal tables, etc..
55#
56consts_misc = [
57    { 'name': 'FirstNonstringType',     'value': 'FIRST_NONSTRING_TYPE' },
58
59    { 'name': 'IsNotStringMask',        'value': 'kIsNotStringMask' },
60    { 'name': 'StringTag',              'value': 'kStringTag' },
61    { 'name': 'NotStringTag',           'value': 'kNotStringTag' },
62
63    { 'name': 'StringEncodingMask',     'value': 'kStringEncodingMask' },
64    { 'name': 'TwoByteStringTag',       'value': 'kTwoByteStringTag' },
65    { 'name': 'OneByteStringTag',       'value': 'kOneByteStringTag' },
66
67    { 'name': 'StringRepresentationMask',
68        'value': 'kStringRepresentationMask' },
69    { 'name': 'SeqStringTag',           'value': 'kSeqStringTag' },
70    { 'name': 'ConsStringTag',          'value': 'kConsStringTag' },
71    { 'name': 'ExternalStringTag',      'value': 'kExternalStringTag' },
72    { 'name': 'SlicedStringTag',        'value': 'kSlicedStringTag' },
73
74    { 'name': 'HeapObjectTag',          'value': 'kHeapObjectTag' },
75    { 'name': 'HeapObjectTagMask',      'value': 'kHeapObjectTagMask' },
76    { 'name': 'SmiTag',                 'value': 'kSmiTag' },
77    { 'name': 'SmiTagMask',             'value': 'kSmiTagMask' },
78    { 'name': 'SmiValueShift',          'value': 'kSmiTagSize' },
79    { 'name': 'SmiShiftSize',           'value': 'kSmiShiftSize' },
80    { 'name': 'PointerSizeLog2',        'value': 'kPointerSizeLog2' },
81
82    { 'name': 'OddballFalse',           'value': 'Oddball::kFalse' },
83    { 'name': 'OddballTrue',            'value': 'Oddball::kTrue' },
84    { 'name': 'OddballTheHole',         'value': 'Oddball::kTheHole' },
85    { 'name': 'OddballNull',            'value': 'Oddball::kNull' },
86    { 'name': 'OddballArgumentsMarker', 'value': 'Oddball::kArgumentsMarker' },
87    { 'name': 'OddballUndefined',       'value': 'Oddball::kUndefined' },
88    { 'name': 'OddballUninitialized',   'value': 'Oddball::kUninitialized' },
89    { 'name': 'OddballOther',           'value': 'Oddball::kOther' },
90    { 'name': 'OddballException',       'value': 'Oddball::kException' },
91
92    { 'name': 'prop_idx_first',
93        'value': 'DescriptorArray::kFirstIndex' },
94    { 'name': 'prop_type_field',
95        'value': 'DATA' },
96    { 'name': 'prop_type_const_field',
97        'value': 'DATA_CONSTANT' },
98    { 'name': 'prop_type_mask',
99        'value': 'PropertyDetails::TypeField::kMask' },
100    { 'name': 'prop_index_mask',
101        'value': 'PropertyDetails::FieldIndexField::kMask' },
102    { 'name': 'prop_index_shift',
103        'value': 'PropertyDetails::FieldIndexField::kShift' },
104    { 'name': 'prop_representation_mask',
105        'value': 'PropertyDetails::RepresentationField::kMask' },
106    { 'name': 'prop_representation_shift',
107        'value': 'PropertyDetails::RepresentationField::kShift' },
108    { 'name': 'prop_representation_integer8',
109        'value': 'Representation::Kind::kInteger8' },
110    { 'name': 'prop_representation_uinteger8',
111        'value': 'Representation::Kind::kUInteger8' },
112    { 'name': 'prop_representation_integer16',
113        'value': 'Representation::Kind::kInteger16' },
114    { 'name': 'prop_representation_uinteger16',
115        'value': 'Representation::Kind::kUInteger16' },
116    { 'name': 'prop_representation_smi',
117        'value': 'Representation::Kind::kSmi' },
118    { 'name': 'prop_representation_integer32',
119        'value': 'Representation::Kind::kInteger32' },
120    { 'name': 'prop_representation_double',
121        'value': 'Representation::Kind::kDouble' },
122    { 'name': 'prop_representation_heapobject',
123        'value': 'Representation::Kind::kHeapObject' },
124    { 'name': 'prop_representation_tagged',
125        'value': 'Representation::Kind::kTagged' },
126    { 'name': 'prop_representation_external',
127        'value': 'Representation::Kind::kExternal' },
128
129    { 'name': 'prop_desc_key',
130        'value': 'DescriptorArray::kDescriptorKey' },
131    { 'name': 'prop_desc_details',
132        'value': 'DescriptorArray::kDescriptorDetails' },
133    { 'name': 'prop_desc_value',
134        'value': 'DescriptorArray::kDescriptorValue' },
135    { 'name': 'prop_desc_size',
136        'value': 'DescriptorArray::kDescriptorSize' },
137
138    { 'name': 'elements_fast_holey_elements',
139        'value': 'FAST_HOLEY_ELEMENTS' },
140    { 'name': 'elements_fast_elements',
141        'value': 'FAST_ELEMENTS' },
142    { 'name': 'elements_dictionary_elements',
143        'value': 'DICTIONARY_ELEMENTS' },
144
145    { 'name': 'bit_field2_elements_kind_mask',
146        'value': 'Map::ElementsKindBits::kMask' },
147    { 'name': 'bit_field2_elements_kind_shift',
148        'value': 'Map::ElementsKindBits::kShift' },
149    { 'name': 'bit_field3_dictionary_map_shift',
150        'value': 'Map::DictionaryMap::kShift' },
151    { 'name': 'bit_field3_number_of_own_descriptors_mask',
152        'value': 'Map::NumberOfOwnDescriptorsBits::kMask' },
153    { 'name': 'bit_field3_number_of_own_descriptors_shift',
154        'value': 'Map::NumberOfOwnDescriptorsBits::kShift' },
155
156    { 'name': 'off_fp_context',
157        'value': 'StandardFrameConstants::kContextOffset' },
158    { 'name': 'off_fp_constant_pool',
159        'value': 'StandardFrameConstants::kConstantPoolOffset' },
160    { 'name': 'off_fp_function',
161        'value': 'JavaScriptFrameConstants::kFunctionOffset' },
162    { 'name': 'off_fp_args',
163        'value': 'JavaScriptFrameConstants::kLastParameterOffset' },
164
165    { 'name': 'scopeinfo_idx_nparams',
166        'value': 'ScopeInfo::kParameterCount' },
167    { 'name': 'scopeinfo_idx_nstacklocals',
168        'value': 'ScopeInfo::kStackLocalCount' },
169    { 'name': 'scopeinfo_idx_ncontextlocals',
170        'value': 'ScopeInfo::kContextLocalCount' },
171    { 'name': 'scopeinfo_idx_first_vars',
172        'value': 'ScopeInfo::kVariablePartIndex' },
173
174    { 'name': 'sharedfunctioninfo_start_position_mask',
175        'value': 'SharedFunctionInfo::kStartPositionMask' },
176    { 'name': 'sharedfunctioninfo_start_position_shift',
177        'value': 'SharedFunctionInfo::kStartPositionShift' },
178
179    { 'name': 'jsarray_buffer_was_neutered_mask',
180        'value': 'JSArrayBuffer::WasNeutered::kMask' },
181    { 'name': 'jsarray_buffer_was_neutered_shift',
182        'value': 'JSArrayBuffer::WasNeutered::kShift' },
183
184    { 'name': 'context_idx_closure',
185        'value': 'Context::CLOSURE_INDEX' },
186    { 'name': 'context_idx_native',
187        'value': 'Context::NATIVE_CONTEXT_INDEX' },
188    { 'name': 'context_idx_prev',
189        'value': 'Context::PREVIOUS_INDEX' },
190    { 'name': 'context_idx_ext',
191        'value': 'Context::EXTENSION_INDEX' },
192    { 'name': 'context_min_slots',
193        'value': 'Context::MIN_CONTEXT_SLOTS' },
194
195    { 'name': 'namedictionaryshape_prefix_size',
196        'value': 'NameDictionaryShape::kPrefixSize' },
197    { 'name': 'namedictionaryshape_entry_size',
198        'value': 'NameDictionaryShape::kEntrySize' },
199    { 'name': 'globaldictionaryshape_entry_size',
200        'value': 'GlobalDictionaryShape::kEntrySize' },
201
202    { 'name': 'namedictionary_prefix_start_index',
203        'value': 'NameDictionary::kPrefixStartIndex' },
204
205    { 'name': 'seedednumberdictionaryshape_prefix_size',
206        'value': 'SeededNumberDictionaryShape::kPrefixSize' },
207    { 'name': 'seedednumberdictionaryshape_entry_size',
208        'value': 'SeededNumberDictionaryShape::kEntrySize' },
209
210    { 'name': 'unseedednumberdictionaryshape_prefix_size',
211        'value': 'UnseededNumberDictionaryShape::kPrefixSize' },
212    { 'name': 'unseedednumberdictionaryshape_entry_size',
213        'value': 'UnseededNumberDictionaryShape::kEntrySize' }
214];
215
216#
217# The following useful fields are missing accessors, so we define fake ones.
218# Please note that extra accessors should _only_ be added to expose offsets that
219# can be used to access actual V8 objects' properties. They should not be added
220# for exposing other values. For instance, enumeration values or class'
221# constants should be exposed by adding an entry in the "consts_misc" table, not
222# in this "extras_accessors" table.
223#
224extras_accessors = [
225    'JSFunction, context, Context, kContextOffset',
226    'HeapObject, map, Map, kMapOffset',
227    'JSObject, elements, Object, kElementsOffset',
228    'FixedArray, data, uintptr_t, kHeaderSize',
229    'JSArrayBuffer, backing_store, Object, kBackingStoreOffset',
230    'JSArrayBufferView, byte_offset, Object, kByteOffsetOffset',
231    'JSTypedArray, length, Object, kLengthOffset',
232    'Map, instance_attributes, int, kInstanceAttributesOffset',
233    'Map, inobject_properties_or_constructor_function_index, int, kInObjectPropertiesOrConstructorFunctionIndexOffset',
234    'Map, instance_size, int, kInstanceSizeOffset',
235    'Map, bit_field, char, kBitFieldOffset',
236    'Map, bit_field2, char, kBitField2Offset',
237    'Map, bit_field3, int, kBitField3Offset',
238    'Map, prototype, Object, kPrototypeOffset',
239    'Oddball, kind_offset, int, kKindOffset',
240    'HeapNumber, value, double, kValueOffset',
241    'ConsString, first, String, kFirstOffset',
242    'ConsString, second, String, kSecondOffset',
243    'ExternalString, resource, Object, kResourceOffset',
244    'SeqOneByteString, chars, char, kHeaderSize',
245    'SeqTwoByteString, chars, char, kHeaderSize',
246    'SharedFunctionInfo, code, Code, kCodeOffset',
247    'SharedFunctionInfo, scope_info, ScopeInfo, kScopeInfoOffset',
248    'SlicedString, parent, String, kParentOffset',
249    'Code, instruction_start, uintptr_t, kHeaderSize',
250    'Code, instruction_size, int, kInstructionSizeOffset',
251];
252
253#
254# The following is a whitelist of classes we expect to find when scanning the
255# source code. This list is not exhaustive, but it's still useful to identify
256# when this script gets out of sync with the source. See load_objects().
257#
258expected_classes = [
259    'ConsString', 'FixedArray', 'HeapNumber', 'JSArray', 'JSFunction',
260    'JSObject', 'JSRegExp', 'JSValue', 'Map', 'Oddball', 'Script',
261    'SeqOneByteString', 'SharedFunctionInfo'
262];
263
264
265#
266# The following structures store high-level representations of the structures
267# for which we're going to emit descriptive constants.
268#
269types = {};             # set of all type names
270typeclasses = {};       # maps type names to corresponding class names
271klasses = {};           # known classes, including parents
272fields = [];            # field declarations
273
274header = '''
275/*
276 * This file is generated by %s.  Do not edit directly.
277 */
278
279#include "src/v8.h"
280#include "src/frames.h"
281#include "src/frames-inl.h" /* for architecture-specific frame constants */
282#include "src/contexts.h"
283
284using namespace v8::internal;
285
286extern "C" {
287
288/* stack frame constants */
289#define FRAME_CONST(value, klass)       \
290    int v8dbg_frametype_##klass = StackFrame::value;
291
292STACK_FRAME_TYPE_LIST(FRAME_CONST)
293
294#undef FRAME_CONST
295
296''' % sys.argv[0];
297
298footer = '''
299}
300'''
301
302#
303# Get the base class
304#
305def get_base_class(klass):
306        if (klass == 'Object'):
307                return klass;
308
309        if (not (klass in klasses)):
310                return None;
311
312        k = klasses[klass];
313
314        return get_base_class(k['parent']);
315
316#
317# Loads class hierarchy and type information from "objects.h".
318#
319def load_objects():
320        objfilename = sys.argv[2];
321        objfile = open(objfilename, 'r');
322        in_insttype = False;
323
324        typestr = '';
325
326        #
327        # Construct a dictionary for the classes we're sure should be present.
328        #
329        checktypes = {};
330        for klass in expected_classes:
331                checktypes[klass] = True;
332
333        #
334        # Iterate objects.h line-by-line to collect type and class information.
335        # For types, we accumulate a string representing the entire InstanceType
336        # enum definition and parse it later because it's easier to do so
337        # without the embedded newlines.
338        #
339        for line in objfile:
340                if (line.startswith('enum InstanceType {')):
341                        in_insttype = True;
342                        continue;
343
344                if (in_insttype and line.startswith('};')):
345                        in_insttype = False;
346                        continue;
347
348                line = re.sub('//.*', '', line.strip());
349
350                if (in_insttype):
351                        typestr += line;
352                        continue;
353
354                match = re.match('class (\w[^:]*)(: public (\w[^{]*))?\s*{\s*',
355                    line);
356
357                if (match):
358                        klass = match.group(1).strip();
359                        pklass = match.group(3);
360                        if (pklass):
361                                pklass = pklass.strip();
362                        klasses[klass] = { 'parent': pklass };
363
364        #
365        # Process the instance type declaration.
366        #
367        entries = typestr.split(',');
368        for entry in entries:
369                types[re.sub('\s*=.*', '', entry).lstrip()] = True;
370
371        #
372        # Infer class names for each type based on a systematic transformation.
373        # For example, "JS_FUNCTION_TYPE" becomes "JSFunction".  We find the
374        # class for each type rather than the other way around because there are
375        # fewer cases where one type maps to more than one class than the other
376        # way around.
377        #
378        for type in types:
379                #
380                # Symbols and Strings are implemented using the same classes.
381                #
382                usetype = re.sub('SYMBOL_', 'STRING_', type);
383
384                #
385                # REGEXP behaves like REG_EXP, as in JS_REGEXP_TYPE => JSRegExp.
386                #
387                usetype = re.sub('_REGEXP_', '_REG_EXP_', usetype);
388
389                #
390                # Remove the "_TYPE" suffix and then convert to camel case,
391                # except that a "JS" prefix remains uppercase (as in
392                # "JS_FUNCTION_TYPE" => "JSFunction").
393                #
394                if (not usetype.endswith('_TYPE')):
395                        continue;
396
397                usetype = usetype[0:len(usetype) - len('_TYPE')];
398                parts = usetype.split('_');
399                cctype = '';
400
401                if (parts[0] == 'JS'):
402                        cctype = 'JS';
403                        start = 1;
404                else:
405                        cctype = '';
406                        start = 0;
407
408                for ii in range(start, len(parts)):
409                        part = parts[ii];
410                        cctype += part[0].upper() + part[1:].lower();
411
412                #
413                # Mapping string types is more complicated.  Both types and
414                # class names for Strings specify a representation (e.g., Seq,
415                # Cons, External, or Sliced) and an encoding (TwoByte/OneByte),
416                # In the simplest case, both of these are explicit in both
417                # names, as in:
418                #
419                #       EXTERNAL_ONE_BYTE_STRING_TYPE => ExternalOneByteString
420                #
421                # However, either the representation or encoding can be omitted
422                # from the type name, in which case "Seq" and "TwoByte" are
423                # assumed, as in:
424                #
425                #       STRING_TYPE => SeqTwoByteString
426                #
427                # Additionally, sometimes the type name has more information
428                # than the class, as in:
429                #
430                #       CONS_ONE_BYTE_STRING_TYPE => ConsString
431                #
432                # To figure this out dynamically, we first check for a
433                # representation and encoding and add them if they're not
434                # present.  If that doesn't yield a valid class name, then we
435                # strip out the representation.
436                #
437                if (cctype.endswith('String')):
438                        if (cctype.find('Cons') == -1 and
439                            cctype.find('External') == -1 and
440                            cctype.find('Sliced') == -1):
441                                if (cctype.find('OneByte') != -1):
442                                        cctype = re.sub('OneByteString$',
443                                            'SeqOneByteString', cctype);
444                                else:
445                                        cctype = re.sub('String$',
446                                            'SeqString', cctype);
447
448                        if (cctype.find('OneByte') == -1):
449                                cctype = re.sub('String$', 'TwoByteString',
450                                    cctype);
451
452                        if (not (cctype in klasses)):
453                                cctype = re.sub('OneByte', '', cctype);
454                                cctype = re.sub('TwoByte', '', cctype);
455
456                #
457                # Despite all that, some types have no corresponding class.
458                #
459                if (cctype in klasses):
460                        typeclasses[type] = cctype;
461                        if (cctype in checktypes):
462                                del checktypes[cctype];
463
464        if (len(checktypes) > 0):
465                for klass in checktypes:
466                        print('error: expected class \"%s\" not found' % klass);
467
468                sys.exit(1);
469
470
471#
472# For a given macro call, pick apart the arguments and return an object
473# describing the corresponding output constant.  See load_fields().
474#
475def parse_field(call):
476        # Replace newlines with spaces.
477        for ii in range(0, len(call)):
478                if (call[ii] == '\n'):
479                        call[ii] == ' ';
480
481        idx = call.find('(');
482        kind = call[0:idx];
483        rest = call[idx + 1: len(call) - 1];
484        args = re.split('\s*,\s*', rest);
485
486        consts = [];
487
488        if (kind == 'ACCESSORS' or kind == 'ACCESSORS_GCSAFE'):
489                klass = args[0];
490                field = args[1];
491                dtype = args[2];
492                offset = args[3];
493
494                return ({
495                    'name': 'class_%s__%s__%s' % (klass, field, dtype),
496                    'value': '%s::%s' % (klass, offset)
497                });
498
499        assert(kind == 'SMI_ACCESSORS' or kind == 'ACCESSORS_TO_SMI');
500        klass = args[0];
501        field = args[1];
502        offset = args[2];
503
504        return ({
505            'name': 'class_%s__%s__%s' % (klass, field, 'SMI'),
506            'value': '%s::%s' % (klass, offset)
507        });
508
509#
510# Load field offset information from objects-inl.h.
511#
512def load_fields():
513        inlfilename = sys.argv[3];
514        inlfile = open(inlfilename, 'r');
515
516        #
517        # Each class's fields and the corresponding offsets are described in the
518        # source by calls to macros like "ACCESSORS" (and friends).  All we do
519        # here is extract these macro invocations, taking into account that they
520        # may span multiple lines and may contain nested parentheses.  We also
521        # call parse_field() to pick apart the invocation.
522        #
523        prefixes = [ 'ACCESSORS', 'ACCESSORS_GCSAFE',
524                     'SMI_ACCESSORS', 'ACCESSORS_TO_SMI' ];
525        current = '';
526        opens = 0;
527
528        for line in inlfile:
529                if (opens > 0):
530                        # Continuation line
531                        for ii in range(0, len(line)):
532                                if (line[ii] == '('):
533                                        opens += 1;
534                                elif (line[ii] == ')'):
535                                        opens -= 1;
536
537                                if (opens == 0):
538                                        break;
539
540                        current += line[0:ii + 1];
541                        continue;
542
543                for prefix in prefixes:
544                        if (not line.startswith(prefix + '(')):
545                                continue;
546
547                        if (len(current) > 0):
548                                fields.append(parse_field(current));
549                                current = '';
550
551                        for ii in range(len(prefix), len(line)):
552                                if (line[ii] == '('):
553                                        opens += 1;
554                                elif (line[ii] == ')'):
555                                        opens -= 1;
556
557                                if (opens == 0):
558                                        break;
559
560                        current += line[0:ii + 1];
561
562        if (len(current) > 0):
563                fields.append(parse_field(current));
564                current = '';
565
566        for body in extras_accessors:
567                fields.append(parse_field('ACCESSORS(%s)' % body));
568
569#
570# Emit a block of constants.
571#
572def emit_set(out, consts):
573        # Fix up overzealous parses.  This could be done inside the
574        # parsers but as there are several, it's easiest to do it here.
575        ws = re.compile('\s+')
576        for const in consts:
577                name = ws.sub('', const['name'])
578                value = ws.sub('', str(const['value']))  # Can be a number.
579                out.write('int v8dbg_%s = %s;\n' % (name, value))
580        out.write('\n');
581
582#
583# Emit the whole output file.
584#
585def emit_config():
586        out = file(sys.argv[1], 'w');
587
588        out.write(header);
589
590        out.write('/* miscellaneous constants */\n');
591        emit_set(out, consts_misc);
592
593        out.write('/* class type information */\n');
594        consts = [];
595        keys = typeclasses.keys();
596        keys.sort();
597        for typename in keys:
598                klass = typeclasses[typename];
599                consts.append({
600                    'name': 'type_%s__%s' % (klass, typename),
601                    'value': typename
602                });
603
604        emit_set(out, consts);
605
606        out.write('/* class hierarchy information */\n');
607        consts = [];
608        keys = klasses.keys();
609        keys.sort();
610        for klassname in keys:
611                pklass = klasses[klassname]['parent'];
612                bklass = get_base_class(klassname);
613                if (bklass != 'Object'):
614                        continue;
615                if (pklass == None):
616                        continue;
617
618                consts.append({
619                    'name': 'parent_%s__%s' % (klassname, pklass),
620                    'value': 0
621                });
622
623        emit_set(out, consts);
624
625        out.write('/* field information */\n');
626        emit_set(out, fields);
627
628        out.write(footer);
629
630if (len(sys.argv) < 4):
631        print('usage: %s output.cc objects.h objects-inl.h' % sys.argv[0]);
632        sys.exit(2);
633
634load_objects();
635load_fields();
636emit_config();
637