Home | History | Annotate | Download | only in tools
      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 
     49 import re
     50 import 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 #
     56 consts_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 #
    228 extras_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 #
    263 expected_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 #
    274 types = {};             # set of all type names
    275 typeclasses = {};       # maps type names to corresponding class names
    276 klasses = {};           # known classes, including parents
    277 fields = [];            # field declarations
    278 
    279 header = '''
    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 
    289 using namespace v8::internal;
    290 
    291 extern "C" {
    292 
    293 /* stack frame constants */
    294 #define FRAME_CONST(value, klass)       \
    295     int v8dbg_frametype_##klass = StackFrame::value;
    296 
    297 STACK_FRAME_TYPE_LIST(FRAME_CONST)
    298 
    299 #undef FRAME_CONST
    300 
    301 ''' % sys.argv[0];
    302 
    303 footer = '''
    304 }
    305 '''
    306 
    307 #
    308 # Get the base class
    309 #
    310 def 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 #
    324 def 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 #
    480 def 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 #
    517 def 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 #
    577 def 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 #
    590 def 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 
    635 if (len(sys.argv) < 4):
    636         print('usage: %s output.cc objects.h objects-inl.h' % sys.argv[0]);
    637         sys.exit(2);
    638 
    639 load_objects();
    640 load_fields();
    641 emit_config();
    642