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