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