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